Last active
August 29, 2015 14:12
-
-
Save Drugoy/7905a4c8fc4167098bf2 to your computer and use it in GitHub Desktop.
A small script to save describe networking stands
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases. | |
; #Warn ; Enable warnings to assist with detecting common errors. | |
SetWorkingDir, %A_ScriptDir% ; Ensures a consistent starting directory. | |
#SingleInstance, Force | |
If %0% ; The script was called with at least 1 argument, expectedly the user drag'n'dropped something onto the script. | |
Loop, %0% | |
input := %A_Index% | |
If FileExist(input) ; Not sure if this needed, since user can't drag'n'drop an unexisting file. | |
FileRead, input, %input% | |
; Loop, %inputFileName% | |
; inputFileName := A_LoopFileName | |
; input = [{"defroute":"123.123.123.123","ifaces":[{"addrs":[{"IP":"1.0.0.2","mask":"24"},{"IP":"321.123.123.123","mask":"23"}],"name":"em0","descr":"external","Net":"vnet2"},{"addrs":[{"IP":"2.0.0.1","mask":"24"},{"IP":"2.0.0.3","mask":"23"}],"name":"em1","descr":"internal","Net":"vnet3"}],"name":"NCC"},{"defroute":"2.0.0.1","ifaces":[{"addrs":[{"IP":"2.0.0.2","mask":"24"}],"name":"em0","descr":"external","Net":"vnet4"},{"addrs":[{"IP":"172.17.6.6","mask":"22"},{"IP":"10.0.0.3","mask":"24"}],"name":"em1","descr":"external","Net":"vnet5"}],"name":"ПУ ЦУС"}] | |
If input | |
{ | |
Try | |
machines := JSON.parse(input) | |
Catch E | |
MsgBox, % e | |
} | |
Else | |
machines := [{"Name": "", "DefRoute": "", "ifaces":[ {"Name": "em0", "Descr": "external", "Net": "vnet1", "Addrs":[ {"IP": "0.0.0.0", "Mask": "24"} ] }] }] | |
DrawGUI: | |
; Menu, menuToolbar, Add, Add node, AddNode | |
Menu, menuToolbar, Add, Open, Open | |
Menu, menuToolbar, Add, Save, Save | |
Menu, menuToolbar, Add, Parse JSON, ParseTextJSON | |
Gui, Menu, menuToolbar | |
GoSub, ParseJSON | |
Gui, Show | |
Return | |
ParseJSON: | |
For node In machines | |
{ | |
margin := 10 + (node-1) * 190 | |
Gui, Add, Text, x%margin% y0, Name: | |
Gui, Add, Edit, x+0 w120 h16 vn%node%Name, % machines[node].Name | |
If !(node = 1 && machines.MaxIndex() = 1) ; Forbid to remove the last existing node. | |
Gui, Add, Button, x+0 w16 h16 vdelN%node% gDeleteNode, - | |
Gui, Add, Button, x+0 w16 h16 vaddN%node% gAddNode, + | |
Gui, Add, Text, x%margin% y+0, Default route: | |
Gui, Add, Edit, x+0 w87 h16 vn%node%DefRoute, % machines[node].defRoute | |
For iface In machines[node].ifaces | |
{ | |
Gui, Add, Text, x%margin% y+0, If %iface%: | |
Gui, Add, Edit, x+0 w29 h16 vn%node%If%iface%Name, % machines[node].ifaces[iface].Name | |
Gui, Add, Text, x+0, Type: | |
Gui, Add, Edit, x+0 w77 h16 vn%node%If%iface%Descr, % machines[node].ifaces[iface].Descr | |
If !(iface = 1 && machines[node].ifaces.MaxIndex() = 1) ; Forbid to remove the last existing iface. | |
Gui, Add, Button, x+0 w16 h16 vn%node%DelIf%iface% gDeleteIf, - | |
Gui, Add, Button, x+0 w16 h16 vn%node%AddIf%iface% gAddIf, + | |
Gui, Add, Text, x%margin% y+0, Net: | |
Gui, Add, Edit, x+0 w131 h16 vn%node%If%iface%Net, % machines[node].ifaces[iface].Net | |
For addr In machines[node].ifaces[iface].addrs | |
{ | |
Gui, Add, Text, x%margin% y+0, Addr%addr%: | |
Gui, Add, Edit, x+5 w89 h16 vn%node%If%iface%a%addr%addr, % machines[node].ifaces[iface].addrs[addr].IP | |
Gui, Add, Text, x+0, / | |
Gui, Add, Edit, x+0 w21 h16 vn%node%if%iface%a%addr%mask, % machines[node].ifaces[iface].addrs[addr].mask | |
If !(addr = 1 && machines[node].ifaces[iface].addrs.MaxIndex() = 1) ; Forbid to remove the last existing addr. | |
Gui, Add, Button, x+0 w16 h16 vn%node%If%iface%DelA%addr% gDeleteAddr, - | |
Gui, Add, Button, x+0 w16 h16 vn%node%If%iface%AddA%addr% gAddAddr, + | |
} | |
} | |
} | |
Return | |
Open: | |
FileSelectFile, input,,, Select a file with your saved stand, Stand (*.json) | |
If (!ErrorLevel && input) | |
{ | |
FileRead, input, %input% | |
If input | |
{ | |
Try | |
machines := JSON.parse(input) | |
Catch E | |
MsgBox, % e | |
} | |
GoSub, Repaint | |
} | |
Return | |
Save: | |
FileSelectFile, output, S, % (output ? output : A_WorkingDir) "\" A_Now ".json", Where to save this stand to? | |
If (!ErrorLevel && output) | |
{ | |
jsoned := JSON.stringify(machines) | |
If FileExist(output) | |
FileDelete, %output% | |
FileAppend, %jsoned%, %output%, UTF-8 | |
; GoSub, Repaint | |
} | |
Return | |
ParseTextJSON: | |
Gui, InputJSON: New | |
Gui, Add, Text,, Paste your JSON'ified stand: | |
Gui, Add, Edit, w300 h250 vinputJSON | |
Gui, Add, Button, x250, Parse | |
Gui, InputJSON: Show | |
Gui, 1: Default | |
Return | |
Repaint: | |
Gui, Destroy | |
GoSub, DrawGUI | |
Return | |
DeleteNode: | |
machines.Remove(SubStr(A_GuiControl, 5)) | |
GoSub, Repaint | |
Return | |
AddNode: | |
RegExMatch(A_GuiControl, "Si)^addN(\d+)$", this) | |
; machines.Insert(this1+1, machines[machines.MaxIndex()]) ; Duplicate | |
machines.Insert(this1+1, {"name": "", "defroute": "", "ifaces":[ {"name":"","type":"","addrs":[ {"IP": "", "mask": ""} ] } ] }) | |
GoSub, Repaint | |
Return | |
DeleteIf: | |
RegExMatch(A_GuiControl, "Si)^n(\d+)DelIf(\d+)$", this) | |
machines[this1].ifaces.Remove(this2) | |
GoSub, Repaint | |
Return | |
AddIf: | |
RegExMatch(A_GuiControl, "Si)^n(\d+)AddIf(\d+)$", this) | |
; machines[this1].ifaces.Insert(this2+1, machines[this1].ifaces[machines[this1].ifaces.MaxIndex()]) ; Duplicate | |
machines[this1].ifaces.Insert(this2+1, {"name":"","type":"","addrs":[ {"IP": "", "mask": ""} ] }) | |
GoSub, Repaint | |
Return | |
DeleteAddr: | |
RegExMatch(A_GuiControl, "Si)^n(\d+)If(\d+)DelA(\d+)$", this) | |
machines[this1].ifaces[this2].addrs.Remove(this3) | |
GoSub, Repaint | |
Return | |
AddAddr: | |
RegExMatch(A_GuiControl, "Si)^n(\d+)If(\d+)AddA(\d+)$", this) | |
; machines[this1].ifaces[this2].addrs.Insert(this3+1, machines[this1].ifaces[this2].addrs[machines[this1].ifaces[this2].addrs.MaxIndex()]) ; Duplicate | |
machines[this1].ifaces[this2].addrs.Insert(this3+1, {"IP": "", "mask": ""}) | |
GoSub, Repaint | |
Return | |
;{ JSON lib | |
class JSON | |
{ | |
/* Function: parse | |
* Deserialize a string containing a JSON document to an AHK object. | |
* Syntax: | |
* json_obj := JSON.parse( src [, jsonize:=false ] ) | |
* Parameter(s): | |
* src [in] - String containing a JSON document | |
* jsonize [in] - If true, objects {} and arrays [] are wrapped as | |
* JSON.object and JSON.array instances respectively. | |
*/ | |
parse(src, jsonize:=false) { | |
;// Pre-validate JSON source before parsing | |
if ((src := Trim(src, " `t`n`r")) == "") ;// trim whitespace(s) | |
throw "Empty JSON source" | |
first := SubStr(src, 1, 1), last := SubStr(src, 0) | |
if !InStr("{[""tfn0123456789-", first) ;// valid beginning chars | |
|| !InStr("}]""el0123456789", last) ;// valid ending chars | |
|| (first == "{" && last != "}") ;// if starts w/ '{' must end w/ '}' | |
|| (first == "[" && last != "]") ;// if starts w/ '[' must end w/ ']' | |
|| (first == """" && last != """") ;// if starts w/ '"' must end w/ '"' | |
|| (first == "n" && last != "l") ;// assume 'null' | |
|| (InStr("tf", first) && last != "e") ;// assume 'true' OR 'false' | |
|| (InStr("-0123456789", first) && !InStr("0123456789", last)) ;// number | |
throw "Invalid JSON format" | |
esc_seq := { | |
(Join | |
"""": """", | |
"/": "/", | |
"b": "`b", | |
"f": "`f", | |
"n": "`n", | |
"r": "`r", | |
"t": "`t" | |
)} | |
i := 0, strings := [] | |
while (i := InStr(src, """",, i+1)) { | |
j := i | |
while (j := InStr(src, """",, j+1)) { | |
str := SubStr(src, i+1, j-i-1) | |
StringReplace, str, str, \\, \u005C, A | |
if (SubStr(str, 0) != "\") | |
break | |
} | |
if !j | |
throw "Missing close quote(s)" | |
src := SubStr(src, 1, i) . SubStr(src, j+1) | |
k := 0 | |
while (k := InStr(str, "\",, k+1)) { | |
ch := SubStr(str, k+1, 1) | |
if InStr("""btnfr/", ch, 1) | |
str := SubStr(str, 1, k-1) . esc_seq[ch] . SubStr(str, k+2) | |
else if (ch == "u") { | |
hex := "0x" . SubStr(str, k+2, 4) | |
if !(A_IsUnicode || (Abs(hex) < 0x100)) | |
continue ;// throw Exception() ??? | |
str := SubStr(str, 1, k-1) . Chr(hex) . SubStr(str, k+6) | |
} else throw "Invalid escape sequence: '\" ch "'" | |
} | |
strings.Insert(str) | |
} | |
;// Check for missing opening/closing brace(s) | |
if InStr(src, "{") || InStr(src, "}") { | |
StringReplace, dummy, src, {, {, UseErrorLevel | |
c1 := ErrorLevel | |
StringReplace, dummy, src, }, }, UseErrorLevel | |
c2 := ErrorLevel | |
if (c1 != c2) | |
throw "Missing " . Abs(c1-c2) . (c1 > c2 ? "clos" : "open") . "ing brace(s)" | |
} | |
;// Check for missing opening/closing bracket(s) | |
if InStr(src, "[") || InStr(src, "]") { | |
StringReplace, dummy, src, [, [, UseErrorLevel | |
c1 := ErrorLevel | |
StringReplace, dummy, src, ], ], UseErrorLevel | |
c2 := ErrorLevel | |
if (c1 != c2) | |
throw "Missing " . Abs(c1-c2) . (c1 > c2 ? "clos" : "open") . "ing bracket(s)" | |
} | |
t := "true", f := "false", n := "null", null := "" | |
jbase := jsonize ? {"{":JSON.object, "[":JSON.array} : {"{":0, "[":0} | |
, pos := 0 | |
, key := "", is_key := false | |
, stack := [tree := []] | |
, is_arr := Object(tree, 1) | |
, next := first ;// """{[01234567890-tfn" | |
while ((ch := SubStr(src, ++pos, 1)) != "") { | |
if InStr(" `t`n`r", ch) | |
continue | |
if !InStr(next, ch) | |
throw "Unexpected char: '" ch "'" | |
is_array := is_arr[obj := stack[1]] | |
if InStr("{[", ch) { | |
val := (proto := jbase[ch]) ? new proto : {} | |
, obj[is_array? NumGet(&obj+4*A_PtrSize)+1 : key] := val | |
, ObjInsert(stack, 1, val) | |
, is_arr[val] := !(is_key := ch == "{") | |
, next := is_key ? """}" : """{[]0123456789-tfn" | |
} | |
else if InStr("}]", ch) { | |
ObjRemove(stack, 1) | |
, next := is_arr[stack[1]] ? "]," : "}," | |
} | |
else if InStr(",:", ch) { | |
if (obj == tree) | |
throw "Unexpected char: '" ch "' -> there is no container object." | |
next := """{[0123456789-tfn", is_key := (!is_array && ch == ",") | |
} | |
else { | |
if (ch == """") { | |
val := ObjRemove(strings, 1) | |
if is_key { | |
key := val, next := ":" | |
continue | |
} | |
} else { | |
val := SubStr(src, pos, (SubStr(src, pos) ~= "[\]\},\s]|$")-1) | |
, pos += StrLen(val)-1 | |
if InStr("tfn", ch, 1) { | |
if !(val == %ch%) | |
throw "Expected '" %ch% "' instead of '" val "'" | |
val := %val% | |
} else if (Abs(val) == "") { | |
throw "Invalid number: " val | |
} | |
val += 0 | |
} | |
obj[is_array? NumGet(&obj+4*A_PtrSize)+1 : key] := val | |
, next := is_array ? "]," : "}," | |
} | |
} | |
return tree[1] | |
} | |
/* Function: stringify | |
* Serialize an object to a JSON formatted string. | |
* Syntax: | |
* json_str := JSON.stringify( obj [, indent:="" ] ) | |
* Parameter(s): | |
* obj [in] - The object to stringify. | |
* indent [in] - Specify string(s) to use as indentation per level. | |
*/ | |
stringify(obj:="", indent:="", lvl:=1) { | |
if IsObject(obj) { | |
if (ObjGetCapacity(obj) == "") ;// COM,Func,RegExMatch,File object | |
throw "Unsupported object type" | |
is_array := 0 | |
for k in obj | |
is_array := (k == A_Index) | |
until !is_array | |
if (Abs(indent) != "") { | |
if (indent < 0) | |
throw "Indent parameter must be a postive integer" | |
spaces := indent, indent := "" | |
Loop % spaces | |
indent .= " " | |
} | |
indt := "" | |
Loop, % indent ? lvl : 0 | |
indt .= indent | |
lvl += 1, out := "" ;// make #Warn happy | |
for k, v in obj { | |
if IsObject(k) || (k == "") | |
throw "Invalid JSON key" | |
if !is_array | |
out .= ( ObjGetCapacity([k], 1) ? JSON.stringify(k) : q . k . q ) ;// key | |
. ( indent ? ": " : ":" ) ;// token + padding | |
out .= JSON.stringify(v, indent, lvl) ;// value | |
. ( indent ? ",`n" . indt : "," ) ;// token + indent | |
} | |
if (out != "") { | |
out := Trim(out, ",`n" indent) | |
if (indent != "") | |
out := "`n" . indt . out . "`n" . SubStr(indt, StrLen(indent)+1) | |
} | |
return is_array ? "[" out "]" : "{" out "}" | |
} | |
;// Not a string - assume number -> integer or float | |
if (ObjGetCapacity([obj], 1) == "") ;// returns an integer if 'obj' is string | |
return InStr("01", obj) ? (obj ? "true" : "false") : obj | |
;// null | |
else if (obj == "") | |
return "null" | |
;// String | |
; if obj is float | |
; return obj | |
esc_seq := { | |
(Join | |
"""": "\""", | |
"/": "\/", | |
"`b": "\b", | |
"`f": "\f", | |
"`n": "\n", | |
"`r": "\r", | |
"`t": "\t" | |
)} | |
StringReplace, obj, obj, \, \\, A | |
for k, v in esc_seq | |
StringReplace, obj, obj, %k%, %v%, A | |
while RegExMatch(obj, "[^\x20-\x7e]", wstr) { | |
ucp := Asc(wstr), hex := "\u", n := 16 | |
while ((n-=4) >= 0) | |
hex .= Chr( (x := (ucp >> n) & 15) + (x < 10 ? 48 : 55) ) | |
StringReplace, obj, obj, %wstr%, %hex%, A | |
} | |
return """" . obj . """" | |
} | |
class object | |
{ | |
__New(args*) { | |
ObjInsert(this, "_", []) | |
if ((count := NumGet(&args+4*A_PtrSize)) & 1) | |
throw "Invalid number of parameters" | |
Loop, % count//2 | |
this[args[A_Index*2-1]] := args[A_Index*2] | |
} | |
__Set(key, val, args*) { | |
ObjInsert(this._, key) | |
} | |
Insert(key, val) { | |
return this[key] := val | |
} | |
/* Buggy - remaining integer keys are not adjusted | |
Remove(args*) { | |
ret := ObjRemove(this, args*), i := -1 | |
for index, key in ObjClone(this._) { | |
if ObjHasKey(this, key) | |
continue | |
ObjRemove(this._, index-(i+=1)) | |
} | |
return ret | |
} | |
*/ | |
Count() { | |
return NumGet(&(this._)+4*A_PtrSize) ;// Round(this._.MaxIndex()) | |
} | |
stringify(indent:="") { | |
return JSON.stringify(this, indent) | |
} | |
_NewEnum() { | |
static proto := {"Next":JSON.object.Next} | |
return { | |
(LTrim Join | |
"base": proto, | |
"enum": this._._NewEnum(), | |
"obj": this | |
)} | |
} | |
Next(ByRef key, ByRef val:="") { | |
if (ret := this.enum.Next(i, key)) | |
val := this.obj[key] | |
return ret | |
} | |
} | |
class array | |
{ | |
__New(args*) { | |
args.base := this.base | |
return args | |
} | |
stringify(indent:="") { | |
return JSON.stringify(this, indent) | |
} | |
} | |
} | |
;} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment