0
|
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 }
|
1
|
50 val cmdLine: CommandLine? = try {
|
|
51 DefaultParser().parse(options, args)
|
0
|
52 } catch (e: ParseException) {
|
|
53 System.err.format("%s: %s%n", MYNAME, e.message)
|
|
54 System.exit(2)
|
1
|
55 null
|
0
|
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
|
1
|
64 val clOstype = cmdLine.getOptionValue("type")
|
0
|
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
|
1
|
77 val process = if (cmdLine.hasOption("clean")) ::clean else ::copy
|
0
|
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 val fset = files[base]
|
|
103 if (fset != null) {
|
|
104 fset.add(type)
|
|
105 } else {
|
|
106 files[base] = HashSet<String>().apply { add(type) }
|
|
107 }
|
|
108 }
|
|
109
|
|
110 fun copy(base: String, types: HashSet<String>) {
|
|
111 val type = if (ostype in types) ostype else "default"
|
|
112 if (type !in types) {
|
|
113 System.err.format("%s: type '%s' missing for '%s'%n", MYNAME, type, base)
|
|
114 errors += 1
|
|
115 return
|
|
116 }
|
|
117 copf("${base}.${type}${MYEXT}", base)
|
|
118 }
|
|
119
|
|
120 fun copf(source: String, target: String) {
|
|
121 val sf = File(source)
|
|
122 val tf = File(target)
|
|
123 try {
|
|
124 FileInputStream(sf).use { sfp ->
|
|
125 FileOutputStream(tf).use { tfp ->
|
|
126 sfp.copyTo(tfp)
|
|
127 }
|
|
128 }
|
|
129 } catch (e: Exception) {
|
|
130 handle(e, "cannot copy '${source}' to '${target}'")
|
|
131 return
|
|
132 }
|
|
133 try {
|
|
134 tf.setLastModified(sf.lastModified())
|
|
135 } catch (e: Exception) {
|
|
136 handle(e, "cannot set time of '${target}'")
|
|
137 }
|
|
138 }
|
|
139
|
|
140 fun clean(base: String, @Suppress("UNUSED_PARAMETER") ignored: HashSet<String>) {
|
|
141 val file = File(base)
|
|
142 if (file.exists()) {
|
|
143 try {
|
|
144 if (!file.delete()) {
|
|
145 System.err.format("%s: '%s' not deleted%n", MYNAME, base)
|
|
146 errors += 1
|
|
147 }
|
|
148 } catch (e: Exception) {
|
|
149 handle(e, "cannot delete '${base}'")
|
|
150 }
|
|
151 }
|
|
152 }
|
|
153
|
|
154 fun handle(exception: Exception, message: String) {
|
|
155 val fallback = when (exception) {
|
|
156 is SecurityException -> "security violation"
|
|
157 is IOException -> "I/O error"
|
|
158 else -> throw exception
|
|
159 }
|
|
160 System.err.format("%s: %s%n", MYNAME, exception.message ?: fallback)
|
|
161 System.err.format("%s: %s%n", MYNAME, message)
|
|
162 errors += 1
|
|
163 }
|