rwdfoldeditor 0.01
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme.txt +462 -0
- data/bin/rwdfoldeditor +19 -0
- data/code/01rwdcore/01rwdcore.rb +29 -0
- data/code/01rwdcore/02helptexthashbegin.rb +4 -0
- data/code/01rwdcore/03helptexthash.rb +23 -0
- data/code/01rwdcore/04helptextend.rb +6 -0
- data/code/01rwdcore/jumplinkcommand.rb +26 -0
- data/code/01rwdcore/openhelpwindow.rb +31 -0
- data/code/01rwdcore/returntomain.rb +10 -0
- data/code/01rwdcore/rundocuments.rb +10 -0
- data/code/01rwdcore/runeditconfiguration.rb +10 -0
- data/code/01rwdcore/runhelpabout.rb +10 -0
- data/code/01rwdcore/runopentinkerdocument.rb +7 -0
- data/code/01rwdcore/rwdtinkerversion.rb +22 -0
- data/code/01rwdcore/rwdwindowreturn.rb +9 -0
- data/code/01rwdcore/selectiontab.rb +9 -0
- data/code/01rwdcore/setuphelpaboutoptions.rb +13 -0
- data/code/01rwdcore/setuptinkerdocuments.rb +6 -0
- data/code/01rwdcore/test_cases.rb +109 -0
- data/code/01rwdcore/test_harness.rb +13 -0
- data/code/01rwdcore/uploadreturns.rb +62 -0
- data/code/dd0viewphoto/dd0viewphoto.rb +3 -0
- data/code/superant.com.foldeditor/0uninstallapplet.rb +10 -0
- data/code/superant.com.foldeditor/changehypernotename.rb +21 -0
- data/code/superant.com.foldeditor/chooselinkfile.rb +6 -0
- data/code/superant.com.foldeditor/choosenotefile.rb +6 -0
- data/code/superant.com.foldeditor/createnewnotehtml.rb +31 -0
- data/code/superant.com.foldeditor/helptexthashrwdhypernote.rb +38 -0
- data/code/superant.com.foldeditor/hyperlinkcreatelinkfile.rb +19 -0
- data/code/superant.com.foldeditor/listfoldeditorfiles.rb +7 -0
- data/code/superant.com.foldeditor/loadfolddocument.rb +21 -0
- data/code/superant.com.foldeditor/loadrwdfoldeditlinkfile.rb +19 -0
- data/code/superant.com.foldeditor/openhelpwindowrwdhyernote.rb +30 -0
- data/code/superant.com.foldeditor/runrwdapplet.rb +10 -0
- data/code/superant.com.foldeditor/rwddisplayfoldlinks.rb +33 -0
- data/code/superant.com.foldeditor/rwdtinkerversion.rb +10 -0
- data/code/superant.com.foldeditor/savefoldlinkfile.rb +20 -0
- data/code/superant.com.foldeditor/savehtmlhypernoterecord.rb +20 -0
- data/code/superant.com.rwdtinkerbackwindow/diagnostictab.rb +19 -0
- data/code/superant.com.rwdtinkerbackwindow/helptexthashtinkerwin2.rb +61 -0
- data/code/superant.com.rwdtinkerbackwindow/initiateapplets.rb +240 -0
- data/code/superant.com.rwdtinkerbackwindow/installgemapplet.rb +34 -0
- data/code/superant.com.rwdtinkerbackwindow/installremotegem.rb +20 -0
- data/code/superant.com.rwdtinkerbackwindow/listgemdirs.rb +12 -0
- data/code/superant.com.rwdtinkerbackwindow/listgemzips.rb +53 -0
- data/code/superant.com.rwdtinkerbackwindow/listinstalledfiles.rb +12 -0
- data/code/superant.com.rwdtinkerbackwindow/listzips.rb +27 -0
- data/code/superant.com.rwdtinkerbackwindow/loadconfigurationrecord.rb +22 -0
- data/code/superant.com.rwdtinkerbackwindow/loadconfigurationvariables.rb +14 -0
- data/code/superant.com.rwdtinkerbackwindow/network.rb +87 -0
- data/code/superant.com.rwdtinkerbackwindow/openappletname.rb +19 -0
- data/code/superant.com.rwdtinkerbackwindow/openhelpwindowtinkerwin2.rb +38 -0
- data/code/superant.com.rwdtinkerbackwindow/remotegemlist.rb +24 -0
- data/code/superant.com.rwdtinkerbackwindow/removeapplet.rb +46 -0
- data/code/superant.com.rwdtinkerbackwindow/removeappletvariables.rb +52 -0
- data/code/superant.com.rwdtinkerbackwindow/runremoteinstall.rb +11 -0
- data/code/superant.com.rwdtinkerbackwindow/runrwdtinkerbackwindow.rb +15 -0
- data/code/superant.com.rwdtinkerbackwindow/rwdtinkerwin2version.rb +13 -0
- data/code/superant.com.rwdtinkerbackwindow/saveconfigurationrecord.rb +19 -0
- data/code/superant.com.rwdtinkerbackwindow/viewappletcontents.rb +22 -0
- data/code/superant.com.rwdtinkerbackwindow/viewgemappletcontents.rb +24 -0
- data/code/zz0applicationend/zz0end.rb +4 -0
- data/configuration/language.dist +8 -0
- data/configuration/rwdapplicationidentity.dist +3 -0
- data/configuration/rwdtinker.dist +18 -0
- data/configuration/rwdwfoldeditor-0.01.dist +12 -0
- data/configuration/tinkerwin2variables.dist +17 -0
- data/gui/00coreguibegin/applicationguitop.rwd +4 -0
- data/gui/frontwindow0/cc0openphoto.rwd +22 -0
- data/gui/frontwindowselections/00selectiontabbegin.rwd +11 -0
- data/gui/frontwindowselections/jumplinkcommands.rwd +15 -0
- data/gui/frontwindowselections/wwselectionend.rwd +3 -0
- data/gui/frontwindowtdocuments/00documentbegin.rwd +6 -0
- data/gui/frontwindowtdocuments/tinkerdocuments.rwd +14 -0
- data/gui/frontwindowtdocuments/zzdocumentend.rwd +8 -0
- data/gui/helpaboutbegin/zzzrwdlasttab.rwd +6 -0
- data/gui/helpaboutbegin/zzzzhelpscreenstart.rwd +3 -0
- data/gui/helpaboutbegin/zzzzzzhelpabouttab.rwd +15 -0
- data/gui/helpaboutzend/helpscreenend.rwd +3 -0
- data/gui/helpaboutzend/zhelpscreenstart2.rwd +3 -0
- data/gui/helpaboutzend/zzzzhelpabout2.rwd +15 -0
- data/gui/helpaboutzend/zzzzhelpscreen2end.rwd +3 -0
- data/gui/tinkerbackwindows/superant.com.foldeditor/10appletbegin.rwd +4 -0
- data/gui/tinkerbackwindows/superant.com.foldeditor/10aviewnote.rwd +42 -0
- data/gui/tinkerbackwindows/superant.com.foldeditor/15htmlview.rwd +46 -0
- data/gui/tinkerbackwindows/superant.com.foldeditor/56viewfold.rwd +43 -0
- data/gui/tinkerbackwindows/superant.com.foldeditor/81jumplinkcommands.rwd +17 -0
- data/gui/tinkerbackwindows/superant.com.foldeditor/9end.rwd +6 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/1appname.rwd +5 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/40rwdlistzips.rwd +41 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/45installremotezip.rwd +44 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/50rwdlistapplets.rwd +44 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/60editconfiguration.rwd +30 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/70rwddiagnostics.rwd +29 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/81jumplinkcommands.rwd +17 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/9backend.rwd +6 -0
- data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/1appname.rwd +31 -0
- data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/9end.rwd +4 -0
- data/gui/tinkerbackwindows/superant.com.versionwindow/1appname.rwd +19 -0
- data/gui/tinkerbackwindows/superant.com.versionwindow/helpaboutwindow.rwd +17 -0
- data/gui/zzcoreguiend/yy9rwdend.rwd +4 -0
- data/init.rb +277 -0
- data/installed/rwdwfoldeditor-0.01.inf +13 -0
- data/installed/temp.rb +1 -0
- data/lang/en/rwdcore/languagefile.rb +58 -0
- data/lang/es/rwdcore/languagefile-es.rb +62 -0
- data/lang/fr/rwdcore/languagefile.rb +64 -0
- data/lang/jp/rwdcore/languagefile.rb +72 -0
- data/lang/nl/rwdcore/languagefile.rb +75 -0
- data/lib/rconftool.rb +380 -0
- data/lib/rwd/browser.rb +123 -0
- data/lib/rwd/ftools.rb +174 -0
- data/lib/rwd/mime.rb +328 -0
- data/lib/rwd/net.rb +866 -0
- data/lib/rwd/ruby.rb +889 -0
- data/lib/rwd/rwd.rb +1942 -0
- data/lib/rwd/sgml.rb +236 -0
- data/lib/rwd/thread.rb +63 -0
- data/lib/rwd/tree.rb +371 -0
- data/lib/rwd/xml.rb +101 -0
- data/lib/zip/ioextras.rb +114 -0
- data/lib/zip/stdrubyext.rb +111 -0
- data/lib/zip/tempfile_bugfixed.rb +195 -0
- data/lib/zip/zip.rb +1378 -0
- data/lib/zip/zipfilesystem.rb +558 -0
- data/lib/zip/ziprequire.rb +61 -0
- data/rwd_files/HowTo_FoldEditor.txt +105 -0
- data/rwd_files/HowTo_Tinker.txt +471 -0
- data/rwd_files/HowTo_TinkerWin2.txt +202 -0
- data/rwd_files/Readme.txt +57 -0
- data/rwd_files/RubyWebDialogs.html +6 -0
- data/rwd_files/default.fld +9 -0
- data/rwd_files/favicon.ico +0 -0
- data/rwd_files/rdoc-style.css +175 -0
- data/rwd_files/rwdapplications.html +54 -0
- data/rwd_files/rwdhypernote-0.13.fld +202 -0
- data/rwd_files/tinker.png +0 -0
- data/rwdconfig.dist +21 -0
- data/rwdfoldeditor.rb +1 -0
- data/tests/RubyGauge.rb +179 -0
- data/tests/checkdepends.sh +4 -0
- data/tests/cleancnf.sh +6 -0
- data/tests/makedist-rwdwfoldeditor.rb +56 -0
- data/tests/makedist.rb +66 -0
- data/tests/rdep.rb +354 -0
- data/tests/totranslate.lang +93 -0
- data/zips/rwdwaddresses-1.05.zip +0 -0
- data/zips/rwdwcalc-0.61.zip +0 -0
- data/zips/rwdwfoldeditor-0.01.zip +0 -0
- data/zips/rwdwgutenberg-0.09.zip +0 -0
- data/zips/rwdwschedule-1.04.zip +0 -0
- data/zips/rwdwshell-1.04.zip +0 -0
- data/zips/temp.rb +1 -0
- data/zips/wrubyslippers-1.06.zip +0 -0
- metadata +212 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
# English Language files for RwdTinker core
|
2
|
+
|
3
|
+
:applet_installed => '"applet installed!"',
|
4
|
+
:application_version => '"Application Version"',
|
5
|
+
:clear => "Clear",
|
6
|
+
:cancel => "Cancel",
|
7
|
+
:clickfor_version => "Click for Version" ,
|
8
|
+
:clickbelowtoviewlistof_zip => '"click below to view lists of zips"',
|
9
|
+
:documents => '"Documents"',
|
10
|
+
:edit => "edit" ,
|
11
|
+
:file_name => '"File Name:"',
|
12
|
+
:fill_record => '"Fill Record"',
|
13
|
+
:gemcommands => '"Gem Commands"',
|
14
|
+
:gemfiles => '"Gem Files"' ,
|
15
|
+
:gemdocs => '"Gem Documents"' ,
|
16
|
+
:gem_name => '"Gem Name"' ,
|
17
|
+
:help => '"Help"',
|
18
|
+
:help_about => '"Help About"',
|
19
|
+
:installgem => '"Install Gem"' ,
|
20
|
+
:install_applet => '"install (rwdtinker) applet"',
|
21
|
+
:jumptoapplication_location => '"Jump to Application Location"',
|
22
|
+
:list_applets => '"List Applets"',
|
23
|
+
:listappletsinthegem_directory => '"List applets in the Gem Directory"',
|
24
|
+
:listappletsavailablefor_installation => "List applets available for installation",
|
25
|
+
:list_files => '"List Files"',
|
26
|
+
:list_installed_gems => '"List installed Gems"' ,
|
27
|
+
:list_photos => '"List Photos"',
|
28
|
+
:list_zips => '"List Zips"',
|
29
|
+
:listzipdirappletsavailable => '"List (zip directory) applets available for installation"',
|
30
|
+
:menu_panel => '"Menu Panel"',
|
31
|
+
:module_unknown => '"Unknown Module"',
|
32
|
+
:next => '"Next"',
|
33
|
+
:open => "Open",
|
34
|
+
:open_document => '"Open Document"',
|
35
|
+
:openselectedhelp_about => '"Open selected help about"',
|
36
|
+
:reload_variables => '"Reload Variables"' ,
|
37
|
+
:remove_applet => '"remove applet"',
|
38
|
+
:return => "Return",
|
39
|
+
:rwdtinker_window_2 => "RwdTinker Window 2",
|
40
|
+
:rwdtinker => "RwdTinker",
|
41
|
+
:rwdtinker_back_window => '"RwdTinker Back Window"',
|
42
|
+
:rwdtinker_help => '"RwdTinker Help"',
|
43
|
+
:rwdtinker_help_window => '"RwdTinker Help Window"',
|
44
|
+
:showjump_links => '"Show Jump Links"',
|
45
|
+
:showdocument_list => '"Show Document List"',
|
46
|
+
:showhelpabout_links => '"Show Help About Links"',
|
47
|
+
|
48
|
+
:save => "Save" ,
|
49
|
+
:save_changes => '"Save Changes"',
|
50
|
+
:selection_panel => '"Selection Panel"',
|
51
|
+
:selection_tab => '"Selection Tab"',
|
52
|
+
:tinker_logo => "Tinker Logo",
|
53
|
+
:tinkerback_window => '"Tinker Back Window"',
|
54
|
+
:viewapplet_contents => '"View Applet Contents"',
|
55
|
+
:viewalreadyinstalled_applications => '"View already installed GEM applications"',
|
56
|
+
:viewinstalled_text => '"View Install Text"',
|
57
|
+
:view_platform => '"view platform"',
|
58
|
+
:viewplatform_information => '"View Platform Information"',
|
59
|
+
:view_event => '"View event"',
|
60
|
+
:view_gem => '"View Gem"',
|
61
|
+
:viewinstall_text => '"View Install Text"' ,
|
62
|
+
:viewlistinstall_files => '"View List of Installed Files"',
|
63
|
+
:view_photo => '"View Photo"',
|
64
|
+
:window => "Window" ,
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Japanese Language files for RwdTinker core
|
2
|
+
:cancel => "とりけし",
|
3
|
+
:open => "あける",
|
4
|
+
:view_event => "けんかい ぎょうじょ",
|
5
|
+
:edit_event => "しゅうろく",
|
6
|
+
:install_gem => "インストール",
|
7
|
+
:list_installed_gems => "かきつらねる",
|
8
|
+
:view_gem => "けんかい",
|
9
|
+
:edit => "しゅうろく",
|
10
|
+
:gem_commands => "コマンド",
|
11
|
+
:gem_files => "ファイル",
|
12
|
+
:gem_docs => "しょるい",
|
13
|
+
:gem_name => "こうもくめい",
|
14
|
+
:selection_panel => "せんたく",
|
15
|
+
:documents => "オンラインマニュアル",
|
16
|
+
|
17
|
+
# English Language files for RwdTinker core
|
18
|
+
|
19
|
+
:applet_installed => '"applet installed!"',
|
20
|
+
:application_version => '"Application Version"',
|
21
|
+
:clear => "Clear",
|
22
|
+
:clickfor_version => "Click for Version" ,
|
23
|
+
:clickbelowtoviewlistof_zip => '"click below to view lists of zips"',
|
24
|
+
|
25
|
+
:file_name => '"File Name:"',
|
26
|
+
:fill_record => '"Fill Record"',
|
27
|
+
:help => '"Help"',
|
28
|
+
:help_about => '"Help About"',
|
29
|
+
|
30
|
+
:install_applet => '"install (rwdtinker) applet"',
|
31
|
+
:jumptoapplication_location => '"Jump to Application Location"',
|
32
|
+
:list_applets => '"List Applets"',
|
33
|
+
:listappletsinthegem_directory => '"List applets in the Gem Directory"',
|
34
|
+
:listappletsavailablefor_installation => "List applets available for installation",
|
35
|
+
:list_files => '"List Files"',
|
36
|
+
|
37
|
+
:list_photos => '"List Photos"',
|
38
|
+
:list_zips => '"List Zips"',
|
39
|
+
:listzipdirappletsavailable => '"List (zip directory) applets available for installation"',
|
40
|
+
:menu_panel => '"Menu Panel"',
|
41
|
+
:module_unknown => '"Unknown Module"',
|
42
|
+
:next => '"Next"',
|
43
|
+
|
44
|
+
:open_document => '"Open Document"',
|
45
|
+
:openselectedhelp_about => '"Open selected help about"',
|
46
|
+
:reload_variables => '"Reload Variables"' ,
|
47
|
+
:remove_applet => '"remove applet"',
|
48
|
+
:return => "Return",
|
49
|
+
:rwdtinker_window_2 => "RwdTinker Window 2",
|
50
|
+
:rwdtinker => "RwdTinker",
|
51
|
+
:rwdtinker_back_window => '"RwdTinker Back Window"',
|
52
|
+
:rwdtinker_help => '"RwdTinker Help"',
|
53
|
+
:rwdtinker_help_window => '"RwdTinker Help Window"',
|
54
|
+
:showjump_links => '"Show Jump Links"',
|
55
|
+
:showdocument_list => '"Show Document List"',
|
56
|
+
:showhelpabout_links => '"Show Help About Links"',
|
57
|
+
|
58
|
+
:save => "Save" ,
|
59
|
+
:save_changes => '"Save Changes"',
|
60
|
+
|
61
|
+
:selection_tab => '"Selection Tab"',
|
62
|
+
:tinker_logo => "Tinker Logo",
|
63
|
+
:tinkerback_window => '"Tinker Back Window"',
|
64
|
+
:viewapplet_contents => '"View Applet Contents"',
|
65
|
+
:viewalreadyinstalled_applications => '"View already installed GEM applications"',
|
66
|
+
:viewinstalled_text => '"View Install Text"',
|
67
|
+
:view_platform => '"view platform"',
|
68
|
+
:viewplatform_information => '"View Platform Information"',
|
69
|
+
:viewinstall_text => '"View Install Text"' ,
|
70
|
+
:viewlistinstall_files => '"View List of Installed Files"',
|
71
|
+
:view_photo => '"View Photo"',
|
72
|
+
:window => "Window" ,
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# English Language files for RwdTinker core
|
2
|
+
|
3
|
+
:cancel => '"Annuleren"',
|
4
|
+
:open => '"Openen"',
|
5
|
+
:save => '"Opslaan"',
|
6
|
+
:window => '"Venster"',
|
7
|
+
:applet_installed => '"Applet geinstalleerd"',
|
8
|
+
:clickfor_version => '"Klier hier voor de Versie"',
|
9
|
+
:documents => '"Documenten"',
|
10
|
+
:help => '"Help"',
|
11
|
+
:module_unknown => '"Module onbekend"',
|
12
|
+
:next => '"Volgende"',
|
13
|
+
:rwdtinker => '"RWDTinker"',
|
14
|
+
:rwdtinker_help_window => '"RWDTinker Help Venster"',
|
15
|
+
:rwdtinker_help => '"RWDTinker Help"',
|
16
|
+
:rwdtinker_window_2 => '"RWDTinker Venster 2"',
|
17
|
+
:rwdtinker_back_window => '"RwdTinker Back Venster"',
|
18
|
+
:tinker_logo => '"RWDTinker Logo"',
|
19
|
+
:selection_panel => '"Selectie Paneel"',
|
20
|
+
# English Language files for RwdTinker core
|
21
|
+
|
22
|
+
|
23
|
+
:applet_installed => '"applet installed!"',
|
24
|
+
:application_version => '"Application Version"',
|
25
|
+
:clear => "Clear",
|
26
|
+
:clickfor_version => "Click for Version" ,
|
27
|
+
:clickbelowtoviewlistof_zip => '"click below to view lists of zips"',
|
28
|
+
|
29
|
+
:edit => "edit" ,
|
30
|
+
:file_name => '"File Name:"',
|
31
|
+
:fill_record => '"Fill Record"',
|
32
|
+
:help => '"Help"',
|
33
|
+
:help_about => '"Help About"',
|
34
|
+
:install_applet => '"install (rwdtinker) applet"',
|
35
|
+
:jumptoapplication_location => '"Jump to Application Location"',
|
36
|
+
:list_applets => '"List Applets"',
|
37
|
+
:listappletsinthegem_directory => '"List applets in the Gem Directory"',
|
38
|
+
:listappletsavailablefor_installation => "List applets available for installation",
|
39
|
+
:list_files => '"List Files"',
|
40
|
+
:list_installed_gems => '"List installed Gems"' ,
|
41
|
+
:list_photos => '"List Photos"',
|
42
|
+
:list_zips => '"List Zips"',
|
43
|
+
:listzipdirappletsavailable => '"List (zip directory) applets available for installation"',
|
44
|
+
:menu_panel => '"Menu Panel"',
|
45
|
+
|
46
|
+
|
47
|
+
:open_document => '"Open Document"',
|
48
|
+
:openselectedhelp_about => '"Open selected help about"',
|
49
|
+
:reload_variables => '"Reload Variables"' ,
|
50
|
+
:remove_applet => '"remove applet"',
|
51
|
+
:return => "Return",
|
52
|
+
:rwdtinker_window_2 => "RwdTinker Window 2",
|
53
|
+
:rwdtinker => "RwdTinker",
|
54
|
+
:rwdtinker_back_window => '"RwdTinker Back Window"',
|
55
|
+
:rwdtinker_help => '"RwdTinker Help"',
|
56
|
+
:rwdtinker_help_window => '"RwdTinker Help Window"',
|
57
|
+
:showjump_links => '"Show Jump Links"',
|
58
|
+
:showdocument_list => '"Show Document List"',
|
59
|
+
:showhelpabout_links => '"Show Help About Links"',
|
60
|
+
|
61
|
+
|
62
|
+
:save_changes => '"Save Changes"',
|
63
|
+
:selection_tab => '"Selection Tab"',
|
64
|
+
:tinker_logo => "Tinker Logo",
|
65
|
+
:tinkerback_window => '"Tinker Back Window"',
|
66
|
+
:viewapplet_contents => '"View Applet Contents"',
|
67
|
+
:viewalreadyinstalled_applications => '"View already installed GEM applications"',
|
68
|
+
:viewinstalled_text => '"View Install Text"',
|
69
|
+
:view_platform => '"view platform"',
|
70
|
+
:viewplatform_information => '"View Platform Information"',
|
71
|
+
:view_event => '"View event"',
|
72
|
+
:viewinstall_text => '"View Install Text"' ,
|
73
|
+
:viewlistinstall_files => '"View List of Installed Files"',
|
74
|
+
:view_photo => '"View Photo"',
|
75
|
+
|
data/lib/rconftool.rb
ADDED
@@ -0,0 +1,380 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
# Copyright (c) 2005 Brian Candler
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to
|
7
|
+
# deal in the Software without restriction, including without limitation the
|
8
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
9
|
+
# sell copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
22
|
+
|
23
|
+
##########################################################################
|
24
|
+
# rconftool is a reimplementation of Sam Varshavchik's sysconftool in Ruby.
|
25
|
+
# See http://www.courier-mta.org/sysconftool/ for details of the original.
|
26
|
+
# Its purpose is to keep configuration files "fresh" when upgrading an
|
27
|
+
# application from one version to another, ensuring that all necessary
|
28
|
+
# settings are present and obsolete ones removed.
|
29
|
+
#
|
30
|
+
# rconftool can be called as a library function or from the command line. It
|
31
|
+
# can also install groups of files recursively from one directory tree into
|
32
|
+
# another.
|
33
|
+
##########################################################################
|
34
|
+
|
35
|
+
require 'fileutils'
|
36
|
+
|
37
|
+
module Rconftool
|
38
|
+
VERSION = "0.1"
|
39
|
+
class NoVersionLine < RuntimeError; end
|
40
|
+
|
41
|
+
# This module function installs a single source (.dist) file to a target
|
42
|
+
# location, having first merged in any compatible settings from the
|
43
|
+
# target file if it existed previously [if it does not exist, any settings
|
44
|
+
# from 'oldfile' are used instead]
|
45
|
+
#
|
46
|
+
# If the distfile is not in sysconftool format (i.e. doesn't have a
|
47
|
+
# ##VERSION: header within the first 20 lines), then for safety it is only
|
48
|
+
# installed if the target file does not already exist. No attempt at data
|
49
|
+
# merging is made in that case.
|
50
|
+
|
51
|
+
def self.install(distfile, targetfile=nil, oldfile=nil, opt={})
|
52
|
+
debug = opt[:debug] || $stdout
|
53
|
+
|
54
|
+
targetfile ||= distfile
|
55
|
+
if opt[:strip_regexp]
|
56
|
+
targetfile = targetfile.sub(opt[:strip_regexp], '')
|
57
|
+
oldfile = oldfile.sub(opt[:strip_regexp], '') if oldfile
|
58
|
+
end
|
59
|
+
if opt[:add_suffix]
|
60
|
+
targetfile = targetfile + opt[:add_suffix]
|
61
|
+
oldfile = oldfile + opt[:add_suffix] if oldfile
|
62
|
+
end
|
63
|
+
raise Errno::EEXIST, "#{distfile}: dist and target filenames are the same" if distfile == targetfile
|
64
|
+
|
65
|
+
# Read in the source (.dist) file
|
66
|
+
begin
|
67
|
+
src = ConfigFile.new(distfile)
|
68
|
+
rescue NoVersionLine
|
69
|
+
# Fallback behaviour when installing a file which is not in sysconftool
|
70
|
+
# format: we install the file only if it doesn't already exist
|
71
|
+
if File.exist?(targetfile)
|
72
|
+
debug << "#{targetfile}: already exists, skipping\n"
|
73
|
+
return
|
74
|
+
end
|
75
|
+
return if opt[:noclobber]
|
76
|
+
copyfrom = (oldfile and File.exist?(oldfile)) ? oldfile : distfile
|
77
|
+
if File.symlink?(copyfrom)
|
78
|
+
File.symlink(File.readlink(copyfrom), targetfile)
|
79
|
+
debug << "#{targetfile}: symlink copied from #{copyfrom}\n"
|
80
|
+
else
|
81
|
+
FileUtils.cp copyfrom, targetfile, :preserve=>true
|
82
|
+
debug << "#{targetfile}: copied from #{copyfrom}\n"
|
83
|
+
end
|
84
|
+
return
|
85
|
+
end
|
86
|
+
|
87
|
+
# OK, so we have a sysconftool file to install. Read in the existing
|
88
|
+
# target file, or if that does not exist, the oldfile
|
89
|
+
begin
|
90
|
+
old = ConfigFile.new
|
91
|
+
old.read(targetfile)
|
92
|
+
rescue NoVersionLine
|
93
|
+
# That's OK; the old target will be renamed to .bak
|
94
|
+
rescue Errno::ENOENT
|
95
|
+
begin
|
96
|
+
target_missing = true
|
97
|
+
old.read(oldfile) if oldfile
|
98
|
+
rescue Errno::ENOENT, NoVersionLine
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Same VERSION? No merge is required
|
103
|
+
if src.version == old.version and not opt[:force]
|
104
|
+
if target_missing
|
105
|
+
FileUtils.cp oldfile, targetfile, :preserve=>true
|
106
|
+
debug << "#{targetfile}: same VERSION, copied from #{oldfile}\n"
|
107
|
+
return
|
108
|
+
end
|
109
|
+
debug << "#{targetfile}: same VERSION, no change\n"
|
110
|
+
return
|
111
|
+
end
|
112
|
+
|
113
|
+
# Merge in old settings (note: any settings which are in targetfile but
|
114
|
+
# not in distfile will be silently dropped)
|
115
|
+
debug << "#{targetfile}:\n"
|
116
|
+
src.settings[1..-1].each do |src_setting|
|
117
|
+
name = src_setting.name
|
118
|
+
old_setting = old[name]
|
119
|
+
unless old_setting
|
120
|
+
debug << " #{name}: new\n"
|
121
|
+
next
|
122
|
+
end
|
123
|
+
if old_setting.version == src_setting.version
|
124
|
+
debug << " #{name}: unchanged\n"
|
125
|
+
src_setting.add_comment("\n DEFAULT SETTING from #{distfile}:\n")
|
126
|
+
src_setting.add_comment(src_setting.content)
|
127
|
+
src_setting.content = old_setting.content
|
128
|
+
next
|
129
|
+
end
|
130
|
+
# Otherwise, must install updated setting and comment out
|
131
|
+
# the current setting for reference
|
132
|
+
debug << " #{name}: UPDATED\n"
|
133
|
+
src_setting.add_comment("\n Previous setting (inserted by rconftool):\n\n")
|
134
|
+
src_setting.add_comment(old_setting.content)
|
135
|
+
end
|
136
|
+
|
137
|
+
return if opt[:noclobber]
|
138
|
+
|
139
|
+
# Write out the new file and carry forward permissions
|
140
|
+
begin
|
141
|
+
tempfile = targetfile+".new#{$$}"
|
142
|
+
src.write(tempfile)
|
143
|
+
st = File.stat(distfile)
|
144
|
+
begin
|
145
|
+
File.chown(st.uid, st.gid, tempfile)
|
146
|
+
rescue Errno::EPERM
|
147
|
+
end
|
148
|
+
File.chmod(st.mode, tempfile)
|
149
|
+
File.rename(targetfile, targetfile+".bak") unless target_missing
|
150
|
+
File.rename(tempfile, targetfile)
|
151
|
+
rescue
|
152
|
+
File.delete(tempfile) rescue nil
|
153
|
+
raise
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
HEADER_ID = '__header__'
|
158
|
+
|
159
|
+
# Object to represent a single setting
|
160
|
+
|
161
|
+
class Setting
|
162
|
+
attr_reader :name, :version
|
163
|
+
attr_accessor :content
|
164
|
+
|
165
|
+
def initialize(name, version)
|
166
|
+
@name = name.gsub(/\s+/,'')
|
167
|
+
@version = version.gsub(/s+/,'')
|
168
|
+
@comment = ""
|
169
|
+
@content = ""
|
170
|
+
@in_content = false
|
171
|
+
end
|
172
|
+
def <<(str)
|
173
|
+
@in_content = true unless /\A#/ =~ str
|
174
|
+
if @in_content
|
175
|
+
@content << str
|
176
|
+
else
|
177
|
+
@comment << str
|
178
|
+
end
|
179
|
+
end
|
180
|
+
# Add text to 'comment' portion of setting, prefixing each line with '#'
|
181
|
+
def add_comment(str)
|
182
|
+
@comment << str.gsub(/^/,'#')
|
183
|
+
end
|
184
|
+
def to_s
|
185
|
+
return "#{@comment}#{@content}" if @name == HEADER_ID
|
186
|
+
return "##NAME: #{@name}:#{@version}\n#{@comment}#{@content}"
|
187
|
+
end
|
188
|
+
end # class Setting
|
189
|
+
|
190
|
+
# Object to represent an entire configuration file. It consists of
|
191
|
+
# an array of Setting objects, with the first one having a special name
|
192
|
+
# (__header__). We also keep a hash of setting name => setting object
|
193
|
+
# to enable us to find a particular setting quickly.
|
194
|
+
|
195
|
+
class ConfigFile
|
196
|
+
attr_reader :version, :settings
|
197
|
+
|
198
|
+
def initialize(filename=nil)
|
199
|
+
read(filename) if filename
|
200
|
+
end
|
201
|
+
|
202
|
+
# fetch a setting by name
|
203
|
+
def [](item)
|
204
|
+
@settings_hash[item]
|
205
|
+
end
|
206
|
+
|
207
|
+
def read(filename)
|
208
|
+
@version = nil
|
209
|
+
curr_setting = Setting.new(HEADER_ID,'')
|
210
|
+
@settings = [curr_setting]
|
211
|
+
@settings_hash = {}
|
212
|
+
|
213
|
+
File.open(filename) do |f|
|
214
|
+
# VERSION header must occur within first 20 lines
|
215
|
+
20.times do
|
216
|
+
line = f.gets
|
217
|
+
break unless line
|
218
|
+
curr_setting << line
|
219
|
+
if line =~ /\A##VERSION:/
|
220
|
+
@version = line
|
221
|
+
break
|
222
|
+
end
|
223
|
+
end
|
224
|
+
raise NoVersionLine, "#{filename}: No VERSION line found" unless @version
|
225
|
+
|
226
|
+
while line = f.gets
|
227
|
+
unless line =~ /\A##NAME:(.*):(.*)/
|
228
|
+
curr_setting << line
|
229
|
+
next
|
230
|
+
end
|
231
|
+
curr_setting = Setting.new($1,$2)
|
232
|
+
@settings << curr_setting
|
233
|
+
@settings_hash[curr_setting.name] = curr_setting
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def write(filename)
|
239
|
+
File.open(filename,"w") do |f|
|
240
|
+
@settings.each do |s|
|
241
|
+
f << s.to_s
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end # class ConfigFile
|
246
|
+
|
247
|
+
# Yield directory contents recursively, without doing chdir(). Note
|
248
|
+
# that yielded pathnames are relative to the base directory given;
|
249
|
+
# so that, for example, you can simulate 'cp -r /foo/bar/ /baz/' by
|
250
|
+
# recurse_dir("/foo/bar") { |n| copy("/foo/bar/"+n,"/baz/"+n) unless
|
251
|
+
# File.directory?("/foo/bar/"+n) }
|
252
|
+
# Current behaviour is that if a directory is a symlink, we follow it.
|
253
|
+
# (Perhaps the block we yield should return true/false?)
|
254
|
+
|
255
|
+
def self.recurse_dir(base)
|
256
|
+
base = base+File::SEPARATOR unless base[-1,1] == File::SEPARATOR
|
257
|
+
dirs = ['']
|
258
|
+
while dir = dirs.pop
|
259
|
+
yield dir unless dir == ''
|
260
|
+
Dir.foreach(base+dir) do |n|
|
261
|
+
next if n == '.' || n == '..'
|
262
|
+
target = dir + n
|
263
|
+
if File.directory?(base+target)
|
264
|
+
dirs << target+File::SEPARATOR
|
265
|
+
next
|
266
|
+
end
|
267
|
+
yield target
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
class Processor
|
273
|
+
attr_reader :o
|
274
|
+
|
275
|
+
# Parse command-line options and set the @o options hash
|
276
|
+
|
277
|
+
def initialize(argv=nil)
|
278
|
+
require 'optparse'
|
279
|
+
|
280
|
+
@o = { :strip_regexp => /\.dist\z/ }
|
281
|
+
return unless argv
|
282
|
+
opts = OptionParser.new do |opts|
|
283
|
+
opts.banner = "rconftool version #{VERSION}"
|
284
|
+
opts.separator "Usage: #{$0} [options]"
|
285
|
+
opts.separator ""
|
286
|
+
opts.separator "Specific options:"
|
287
|
+
|
288
|
+
opts.on("-n", "--noclobber", "Dummy run") do
|
289
|
+
@o[:noclobber] = true
|
290
|
+
end
|
291
|
+
opts.on("-f", "--force", "Update files even if VERSION is same") do
|
292
|
+
@o[:force] = true
|
293
|
+
end
|
294
|
+
opts.on("-q", "--quiet", "No progress reporting") do
|
295
|
+
@o[:debug] = ""
|
296
|
+
end
|
297
|
+
opts.on("--targetdir DIR", "Where to write merged config files") do |dir|
|
298
|
+
@o[:targetdir] = dir
|
299
|
+
end
|
300
|
+
opts.on("--olddir DIR", "If file does not exist in targetdir,",
|
301
|
+
"try to merge from here") do |dir|
|
302
|
+
@o[:olddir] = dir
|
303
|
+
end
|
304
|
+
opts.on("--[no-]recursive", "Traverse directories recursively") do |v|
|
305
|
+
@o[:recursive] = v
|
306
|
+
end
|
307
|
+
opts.on("--strip-suffix FOO", "Remove suffix FOO from target filenames",
|
308
|
+
"(default .dist)") do |suffix|
|
309
|
+
@o[:strip_regexp] = /#{Regexp.escape(suffix)}\z/
|
310
|
+
end
|
311
|
+
opts.on("-a", "--add-suffix FOO", "Add suffix FOO to target filenames") do |suffix|
|
312
|
+
@o[:add_suffix] = suffix
|
313
|
+
end
|
314
|
+
|
315
|
+
opts.on_tail("-?", "--help", "Show this message") do
|
316
|
+
puts opts
|
317
|
+
exit
|
318
|
+
end
|
319
|
+
end
|
320
|
+
opts.parse!(argv)
|
321
|
+
end
|
322
|
+
|
323
|
+
# Process a list of files, [src1,src2,...]. If recursive mode has been
|
324
|
+
# enabled, then subdirectories of destdir are created as necessary
|
325
|
+
# when 'src' is a directory, and the mode/ownership of these newly
|
326
|
+
# created directories is copied from the original.
|
327
|
+
|
328
|
+
def run(files)
|
329
|
+
done_work = false
|
330
|
+
files.each do |f|
|
331
|
+
if not File.directory?(f)
|
332
|
+
dst = old = nil
|
333
|
+
dst = @o[:targetdir] + File::SEPARATOR + File.basename(f) if @o[:targetdir]
|
334
|
+
old = @o[:olddir] + File::SEPARATOR + File.basename(f) if @o[:olddir]
|
335
|
+
Rconftool::install(f, dst, old, @o)
|
336
|
+
elsif not @o[:recursive]
|
337
|
+
raise Errno::EISDIR, "#{f} (not copied). Need --recursive?"
|
338
|
+
else
|
339
|
+
Rconftool::recurse_dir(f) do |nf|
|
340
|
+
src = f + File::SEPARATOR + nf
|
341
|
+
dst = old = nil
|
342
|
+
dst = @o[:targetdir] + File::SEPARATOR + nf if @o[:targetdir]
|
343
|
+
old = @o[:olddir] + File::SEPARATOR + nf if @o[:olddir]
|
344
|
+
if File.directory?(src)
|
345
|
+
if dst and not File.directory?(dst)
|
346
|
+
orig = File.stat(src)
|
347
|
+
Dir.mkdir(dst, orig.mode)
|
348
|
+
begin
|
349
|
+
File.chown(orig.uid, orig.gid, dst)
|
350
|
+
rescue Errno::EPERM
|
351
|
+
end
|
352
|
+
end
|
353
|
+
else
|
354
|
+
Rconftool::install(src, dst, old, @o)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
done_work = true
|
359
|
+
end
|
360
|
+
unless done_work
|
361
|
+
$stderr.puts "Usage: #{$0} [options] src1 src2 ...\n"+
|
362
|
+
"Try #{$0} --help for more information\n"
|
363
|
+
exit 1
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end # class Processor
|
367
|
+
|
368
|
+
end # module Rconftool
|
369
|
+
|
370
|
+
# Run from command line?
|
371
|
+
if __FILE__ == $0
|
372
|
+
|
373
|
+
begin
|
374
|
+
s = Rconftool::Processor.new(ARGV)
|
375
|
+
s.run(ARGV)
|
376
|
+
rescue Exception => e
|
377
|
+
$stderr.puts "#{$0}: #{e}"
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|