#!/bin/dash

# obb2bsv - Openbox bindings to backquote-separated values
# Copyright (C) step, 2020
# License: GNU GPL Version 2

# STDIN  FORMAT: see http://openbox.org/wiki/Help:Bindings for input format.
# STDOUT FORMAT: 4-field \b-separated lines: device, context, event, description.

# Usage: [ENVVAR=value] obb2bsv.sh [<] "$HOME/.config/openbox/rc.xml"
# Environment Variables:
# OBB_OFS - separate output fields with this string (default \b)
# OBB_CTX_ANY - output this string for the empty/any context (default "")
# OBB_SQUEEZE_WS - set non-empty to squeeze white space characters

####################
# Preprocess input #
####################

awk '#{{{awk
# Delete multiline XML comments because xml2 is buggy in this regard
/^[[:space:]]*<!--/,/-->/ { next }

# Insert special tags `print_keybind` and `print_mousebind` to trigger our
# future output actions. This is done because xml2 replaces a run of closing
# tags with the last tag only:
# `<B><A k="1"></A><A k="2"></A>...`    =>    `B/A/@k=1\nB/A\nB/A/@k=2\n...`
# but
# `...<A k="3"></A></B>`                =>    `B/A/@k=3\nB\n`
# that is, since the `B/A\n` was omitted we can`t rely on it to mark our output
# actions. Therefore, we add our own triggers, which xml2 won`t leave out.
{ gsub("</keybind>", "<print_keybind/>&") }
{ gsub("</mousebind>", "<print_mousebind/>&") }

# Convert to line format.
{ print | "xml2" }

#awk}}}' "${1:--}" |

	################################
	# Parse xml2 and format output #
	################################

	awk -v OFS="${OBB_OFS:-\010}" \
		-v CTX_ANY="$OBB_CTX_ANY" \
		-v SQUEEZE_WS="$OBB_SQUEEZE_WS" \
		'#{{{awk
# discard residual XML comments
/\/!=/ { next }

{ s0 = $0 }

# Squeeze white space
SQUEEZE_WS { gsub(/[[:space:]]/, " ", s0); gsub(/ +/, " ", s0) }

################
#  KEYBOARD 0  #
################

/\/keyboard\/keybind\/@key=/,/\/print_keybind$/ {
	if(p = index(s0, "/@key=")) {
		key = substr(s0, p +6)
	}
	else if(p = index(s0, "/@name=")) {
		name = name sep substr(s0, p +7)
		sep = "; "
	}
	else if(match(s0, /(command|desktop|menu|to)=/)){
		s = substr(s0, RSTART)
		s = substr(s, index(s, "=") +1)
		name = name " " s
	}
	else if(s0 ~ /\/print_keybind$/) {
		#print "key("key") name("name")"
		print 0, CTX_ANY, key, name
		key = name = sep = ""
	}
}

#############
#  MOUSE 1  #
#############

# Enter mouse context
/\/mouse\/context\/@name=/ {
	context = substr(s0, index(s0, "=") +1)
	if("" == context)
		context = CTX_ANY
	next
}

# In a mouse context
/\/mouse\/context\/mousebind\/@button=/,/\/print_mousebind$/ {
	if(p = index(s0, "/@button=")) {
		button = substr(s0, p +9)
	}
	else if(p = index(s0, "/@action=")) {
		action = substr(s0, p +9)
	}
	else if(p = index(s0, "/@name=")) {
		name = name sep substr(s0, p +7)
		sep = "; "
	}
	else if(match(s0, /(command|desktop|menu|to)=/)){
		s = substr(s0, RSTART)
		s = substr(s, index(s, "=") +1)
		name = name " " s
	}
	else if(s0 ~ /\/print_mousebind$/) {
		#print "context("context") action("action") button("button") name("name")"
		print 1, context, button" "action, name
		button = action = name = sep = ""
	}
}

/\/context$/ {
	context = ""
	next
}

#awk}}}'

