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 }