Mercurial > cgi-bin > hgweb.cgi > Osdep
comparison src/name/blackcap/osdep/Osdep.kt @ 0:895aa9a8d628
Initial commit.
author | David Barts <n5jrn@me.com> |
---|---|
date | Sat, 08 Feb 2020 08:46:17 -0800 |
parents | |
children | 5ea54efa8e45 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:895aa9a8d628 |
---|---|
1 /* | |
2 * Recursively examines the specified directories for files that end in | |
3 * .<type>.osdep, where <type> is one of unix, mac, windows, or default. | |
4 * The file that matches the current OS type (or the type specified with | |
5 * the -t option) | |
6 */ | |
7 package name.blackcap.osdep | |
8 | |
9 import java.io.File | |
10 import java.io.FileInputStream | |
11 import java.io.FileOutputStream | |
12 import java.io.IOException | |
13 import kotlin.collections.HashMap | |
14 import kotlin.collections.HashSet | |
15 import org.apache.commons.cli.CommandLine | |
16 import org.apache.commons.cli.DefaultParser | |
17 import org.apache.commons.cli.HelpFormatter | |
18 import org.apache.commons.cli.Option | |
19 import org.apache.commons.cli.Options | |
20 import org.apache.commons.cli.ParseException | |
21 | |
22 var ostype = run { | |
23 val rawType = System.getProperty("os.name")?.toLowerCase() | |
24 if (rawType == null) { | |
25 "default" | |
26 } else if (rawType.contains("win")) { | |
27 "windows" | |
28 } else if (rawType.contains("mac")) { | |
29 "mac" | |
30 } else if (rawType.contains("nix") || rawType.contains("nux") || rawType.contains("aix") || rawType.contains("sunos")) { | |
31 "unix" | |
32 } else { | |
33 "default" | |
34 } | |
35 } | |
36 | |
37 val MYNAME = "osdep" | |
38 val MYEXT = ".osdep" | |
39 | |
40 val files = HashMap<String, HashSet<String>>() | |
41 | |
42 var errors = 0 | |
43 | |
44 fun main(args: Array<String>) { | |
45 val options = Options().apply { | |
46 addOption("?", "help", false, "Print this help message.") | |
47 addOption("c", "clean", false, "Clean instead of copying.") | |
48 addOption("t", "type", true, "Specify OS type (default ${ostype}).") | |
49 } | |
50 var cmdLine: CommandLine? = null | |
51 try { | |
52 cmdLine = DefaultParser().parse(options, args) | |
53 } catch (e: ParseException) { | |
54 System.err.format("%s: %s%n", MYNAME, e.message) | |
55 System.exit(2) | |
56 } | |
57 | |
58 if (cmdLine!!.hasOption("help")) { | |
59 val usage = MYNAME + " [--help] [--clean] [--type=type] directory [...]" | |
60 HelpFormatter().printHelp(usage, options, false); | |
61 System.exit(0); | |
62 } | |
63 | |
64 val clOstype = cmdLine!!.getOptionValue("type") | |
65 if (clOstype != null) { | |
66 ostype = clOstype | |
67 } | |
68 | |
69 for (arg in cmdLine.args) { | |
70 try { | |
71 File(arg).walk().forEach { build(it) } | |
72 } catch (e: Exception) { | |
73 handle(e, "cannot walk '${arg}'") | |
74 } | |
75 } | |
76 | |
77 val process = if (cmdLine!!.hasOption("clean")) ::clean else ::copy | |
78 files.forEach { | |
79 process(it.key, it.value) | |
80 } | |
81 System.exit(if (errors == 0) 0 else 1) | |
82 } | |
83 | |
84 fun build(file: File) { | |
85 if (!file.isFile()) { | |
86 return | |
87 } | |
88 val path = file.toString() | |
89 if (!path.endsWith(MYEXT)) { | |
90 return | |
91 } | |
92 val dot2 = path.lastIndexOf('.') | |
93 if (dot2 <= 0) { | |
94 return | |
95 } | |
96 val dot1 = path.lastIndexOf('.', dot2 - 1) | |
97 if (dot1 <= 0) { | |
98 return | |
99 } | |
100 val base = path.substring(0 .. (dot1 - 1)) | |
101 val type = path.substring((dot1 + 1) .. (dot2 - 1)) | |
102 println("base=${base}, type=${type}") /* debug */ | |
103 val fset = files[base] | |
104 if (fset != null) { | |
105 fset.add(type) | |
106 } else { | |
107 files[base] = HashSet<String>().apply { add(type) } | |
108 } | |
109 } | |
110 | |
111 fun copy(base: String, types: HashSet<String>) { | |
112 val type = if (ostype in types) ostype else "default" | |
113 if (type !in types) { | |
114 System.err.format("%s: type '%s' missing for '%s'%n", MYNAME, type, base) | |
115 errors += 1 | |
116 return | |
117 } | |
118 copf("${base}.${type}${MYEXT}", base) | |
119 } | |
120 | |
121 fun copf(source: String, target: String) { | |
122 val sf = File(source) | |
123 val tf = File(target) | |
124 try { | |
125 FileInputStream(sf).use { sfp -> | |
126 FileOutputStream(tf).use { tfp -> | |
127 sfp.copyTo(tfp) | |
128 } | |
129 } | |
130 } catch (e: Exception) { | |
131 handle(e, "cannot copy '${source}' to '${target}'") | |
132 return | |
133 } | |
134 try { | |
135 tf.setLastModified(sf.lastModified()) | |
136 } catch (e: Exception) { | |
137 handle(e, "cannot set time of '${target}'") | |
138 } | |
139 } | |
140 | |
141 fun clean(base: String, @Suppress("UNUSED_PARAMETER") ignored: HashSet<String>) { | |
142 val file = File(base) | |
143 if (file.exists()) { | |
144 try { | |
145 if (!file.delete()) { | |
146 System.err.format("%s: '%s' not deleted%n", MYNAME, base) | |
147 errors += 1 | |
148 } | |
149 } catch (e: Exception) { | |
150 handle(e, "cannot delete '${base}'") | |
151 } | |
152 } | |
153 } | |
154 | |
155 fun handle(exception: Exception, message: String) { | |
156 val fallback = when (exception) { | |
157 is SecurityException -> "security violation" | |
158 is IOException -> "I/O error" | |
159 else -> throw exception | |
160 } | |
161 System.err.format("%s: %s%n", MYNAME, exception.message ?: fallback) | |
162 System.err.format("%s: %s%n", MYNAME, message) | |
163 errors += 1 | |
164 } |