3 ### begin from awkenough (https://github.com/dubiousjim/awkenough) ####
4 function assert(test, msg) {
5 if (!test) die(msg ? msg : "assertion failed")
8 # if you call die, assert, or check*: start END blocks with
9 # { if (EXITCODE) exit EXITCODE; ... }
12 printf("%s: %s\n", ARGV[0], msg) > "/dev/stderr"
16 function parse_json(str, T, V, slack, c,s,n,a,A,b,B,C,U,W,i,j,k,u,v,w,root) {
17 # use strings, numbers, booleans as separators
18 # c = "[^\"\\\\[:cntrl:]]|\\\\[\"\\\\/bfnrt]|\\u[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]"
19 c = "[^\"\\\\\001-\037]|\\\\[\"\\\\/bfnrt]|\\\\u[[:xdigit:]A-F][[:xdigit:]A-F][[:xdigit:]A-F][[:xdigit:]A-F]"
21 n = "-?(0|[1-9][[:digit:]]*)([.][[:digit:]]+)?([eE][+-]?[[:digit:]]+)?"
23 root = gsplit(str, A, s "|" n "|true|false|null", T)
24 assert(root > 0, "unexpected")
26 # rejoin string using value indices
28 for (i=1; i<root; i++)
32 # cleanup types and values
33 for (i=1; i<root; i++) {
35 b = split(substr(T[i], 2, length(T[i])-2), B, /\\/)
40 for (j=2; j <= b; j++) {
43 if (++k % 2 == 1) v = v "\\"
46 if (w == "b") v = v "\b" substr(u, 2)
47 else if (w == "f") v = v "\f" substr(u, 2)
48 else if (w == "n") v = v "\n" substr(u, 2)
49 else if (w == "r") v = v "\r" substr(u, 2)
50 else if (w == "t") v = v "\t" substr(u, 2)
57 } else if (T[i] !~ /true|false|null/) {
66 gsub(/[[:space:]]+/, "", str)
67 if (str !~ /^[][{}[:digit:],:]+$/) {
68 if (slack !~ /:/) return -1
69 # handle ...unquoted:...
70 a = gsplit(str, A, "[[:alpha:]_][[:alnum:]_]*:", B)
72 for (i=1; i < a; i++) {
74 V[root] = substr(B[i], 1, length(B[i])-1)
75 str = str A[i] root ":"
79 if (str !~ /^[][{}[:digit:],:]+$/) return -10
83 a = gsplit(str, A, "[[{]", B)
86 else if (A[1] !~ /^[[:digit:]]+$/) return -3
90 # parse objects and arrays
93 for (i=2; i<=a; i++) {
94 T[k] = (B[i-1] ~ /\{/) ? "object" : "array"
97 u = gsplit(A[i], U, "[]}]", W)
98 assert(u > 0, "unexpected")
100 if (i < a && A[i] != "" && U[u] !~ /[,:]$/)
102 for (j=1; j<u; j++) {
103 if (C[0] == 0 || T[C[0]] != ((W[j] == "}") ? "object" : "array")) return -5
108 if (w) V[w] = V[w] v U[j+1]
111 if (C[0] != 0) return -6
114 for (i=root; i<k; i++) {
115 if (T[i] == "object") {
116 # check object contents
117 b = split(V[i], B, /,/)
118 for (j=1; j <= b; j++) {
119 if (B[j] !~ /^[[:digit:]]+:[[:digit:]]+$/)
121 if (T[substr(B[j], 1, index(B[j],":")-1)] != "string")
124 } else if (V[i] != "") {
125 # check array contents
126 if (slack ~ /,/ && V[i] ~ /,$/)
127 V[i] = substr(V[i], 1, length(V[i] -1))
128 if (V[i] !~ /^[[:digit:]]+(,[[:digit:]]+)*$/)
135 # populate array from str="key key=value key=value"
136 # can optionally supply "re" for equals, space; if they're the same or equals is "", array will be setlike
137 function asplit(str, array, equals, space, aux, i, n) {
138 n = split(str, aux, (space == "") ? "[ \n]+" : space)
139 if (space && equals == space)
141 else if (ismissing(equals))
143 split("", array) # delete array
144 for (i=1; i<=n; i++) {
145 if (equals && match(aux[i], equals))
146 array[substr(aux[i], 1, RSTART-1)] = substr(aux[i], RSTART+RLENGTH)
150 split("", aux) # does it help to delete the aux array?
154 function query_json(str, X, root, slack, T, V, A, B, C, i, j, k) {
155 k = parse_json(str, T, V, slack)
164 asplit(V[k], B, ":", ",")
171 } else return -11 # can't find requested root
184 j = split(V[k], A, ",")
185 k = B[k] ? B[k] SUBSEP : ""
187 for (i=1; i<=j; i++) {
188 # push A[i] to C, (B[k],i) to B
193 } else if (j == "object") {
194 asplit(V[k], A, ":", ",")
195 k = B[k] ? B[k] SUBSEP : ""
197 # push A[i] to C, (B[k],V[i]) to B
202 } else if (j == "number") {
204 } else if (j == "true") {
206 } else if (j == "false") {
208 } else if (j == "string") {
211 # null will satisfy ismissing()
220 function ismissing(u) {
221 return u == 0 && u == ""
224 # behaves like gawk's split; special cases re == "" and " "
225 # unlike split, will honor 0-length matches
226 function gsplit(str, items, re, seps, n, i, start, stop, sep1, sep2, sepn) {
228 # find separators that don't occur in str
231 sep1 = sprintf("%c", i++)
232 while (index(str, sep1))
234 sep2 = sprintf("%c", i++)
235 while (index(str, sep2))
237 split("", seps) # delete array
241 split(str, items, "")
247 split("", items) # delete array
250 if (match(str, /^[ \t\n]+/)) {
251 seps[0] = substr(str, 1, RLENGTH)
252 str = substr(str, RLENGTH+1)
254 if (match(str, /[ \t\n]+$/)) {
255 sepn = substr(str, RSTART, RLENGTH)
256 str = substr(str, 1, RSTART-1)
259 i = gsub(re, sep1 "&" sep2, str)
261 start = index(str, sep1)
262 stop = index(str, sep2) - 1
263 seps[++n] = substr(str, start + 1, stop - start)
264 items[n] = substr(str, 1, start - 1)
265 str = substr(str, stop + 2)
268 if (sepn != 1) seps[n] = sepn
272 function basename(path, suffix) {
276 sub(/^.*\//, "", path)
277 if(suffix != "" && has_suffix(path, suffix))
278 path = substr(path, 1, length(path) - length(suffix))
282 function dirname(path) {
283 if (!sub(/\/[^\/]*\/?$/, "", path))
291 function has_suffix(str, suf, len1, len2) {
294 return len2 <= len1 && substr(str, len1 - len2 + 1) == suf
296 ### end from awkenough ####
300 USAGE = "json2fstree: basedir [file]\n\
301 read json data in file and create directory tree under basedir\n"
303 if (ARGC != 2 && ARGC != 3) {
304 printf(USAGE) >"/dev/stderr"
308 if (ARGV[1] == "-h" || ARGV[1] == "--help") {
325 if (EXITCODE != "") {
328 ret = query_json($0, data)
329 assert(ret == 0, "failed to parse json\n")
334 gsub(SUBSEP, "/", path)
335 fpath = outd "/" path
336 ret = system("mkdir -p " dirname(fpath))
337 assert(ret == 0, "failed to mkdir " fpath)
338 printf("%s", data[key]) > fpath
342 # vi: ts=4 noexpandtab