#!/bin/sh PROGNAME="$0" usage() { cat < /dev/null geo-html2gpx *.html > found.gpx EOF exit 1 } # # Report an error and exit # error() { echo "`basename $PROGNAME`: $1" >&2 exit 1 } debug() { if [ $DEBUG -ge $1 ]; then echo "`basename $PROGNAME`: $2" >&2 fi } if [ `uname` = 'Darwin' ]; then awk=gawk date=gdate else awk=awk date=date fi # # Read RC file, if there is one # USERNAME= if [ -f $HOME/.georc ]; then . $HOME/.georc # N.B. must switch to read_rc_file if LAT/LON is ever needed here fi # # Process the options # POSTPROC="cat" DEBUG=0 INCR=0 NOWPTS=0 NOZERO=0 NOHTML=0 DECODE=1 NUMLOGS=1000000 while getopts "beE:iwzl:no:u:D:h?" opt do case $opt in b) POSTPROC="gpsbabel -igpx -f- -ogpx -F-";; e) DECODE=0;; E) eval "$OPTARG";; i) INCR=1;; l) NUMLOGS="$OPTARG";; o) POSTPROC="gpsbabel -igpx -f- -o$OPTARG -F-";; n) NOHTML=1;; u) USERNAME="$OPTARG";; w) NOWPTS=1;; z) NOZERO=1;; D) DEBUG="$OPTARG";; h|\?) usage;; esac done shift `expr $OPTIND - 1` # # Main Program # YR=`date +"%Y"` cat "$@" | tr -d '\001\002\003\004\005\006\007\015\022\026\030' \ | sed 's/ 0) rv = rv * 16 + (k-1) else break; } return rv } function prsJSON_EncodeAsUTF8( v, s, p1, p2, p3, p4, cs ) { cs = "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377" if ( v < 128 ) s = sprintf("%c", v ) else if ( v < 2048 ) # 110xxxxx 10xxxxxx { p1 = int(v/64) % 32 p2 = v % 64 s = substr(cs, 65+p1, 1) substr(cs, p2+1, 1) } else if ( v < 65536 ) # 1110xxxx 10xxxxxx 10xxxxxx { p1 = int(v/4096) % 16 p2 = int(v/64) % 64 p3 = v % 64 s = substr(cs, 97+p1, 1) substr(cs, p2+1, 1) substr(cs, p3+1, 1) } else if ( v < 1114112 ) # 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx { p1 = int(v/262144) % 8 p2 = int(v/4096) % 64 p3 = int(v/64) % 64 p4 = v % 64 s = substr(cs, 113+p1, 1) substr(cs, p2+1, 1) substr(cs, p3+1, 1) substr(cs, p4+1, 1) } else s = "" return s; } function prsJSON_UnescapeString(jsonString, matchedString, matchedValue) { if (jsonString == "\"\"") return "" if (jsonString ~ /^".+"$/) jsonString = substr(jsonString,2,length(jsonString)-2) gsub(/\\\\/, "\\u005C", jsonString) gsub(/\\"/, "\"", jsonString) gsub(/\\\//, "/", jsonString) gsub(/\\b/, "\b", jsonString) gsub(/\\f/, "\f", jsonString) gsub(/\\n/, "\n", jsonString) gsub(/\\r/, "\r", jsonString) gsub(/\\t/, "\t", jsonString) if (match(jsonString, /\\[^u]/)) return "ParseJSON Error: Invalid String at " jsonString # handle encoded UTF-16 surrogates while (match(jsonString, /\\uD[89AaBb][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]\\uD[CcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]/)) { matchedValue = (prsJSON_hex2num(substr(jsonString, RSTART+2, 4)) % 1024) * 1024 + prsJSON_hex2num(substr(jsonString, RSTART+8, 4)) % 1024 + 65536 #print matchedValue, substr(jsonString, RSTART+2, 4), substr(jsonString, RSTART+8, 4) matchedString = prsJSON_EncodeAsUTF8( matchedValue ) sub(/\\uD[89AaBb][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]\\uD[CcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]/, matchedString, jsonString) } while (match(jsonString, /\\u[0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]/)) { matchedValue = prsJSON_hex2num(substr(jsonString, RSTART+2, 4)) matchedString = prsJSON_EncodeAsUTF8( matchedValue ) sub(/\\u[0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]/, matchedString, jsonString) } return jsonString; } function prsJSON_ValidString(jsonString) { return jsonString !~ /^ParseJSON Error: Invalid String at / } function prsJSON_SetDataValue(jsonData, prefix, value) { jsonData[prefix] = value } function prsJSON_Error(jsonStringArr, cnt, idx, jsonData, message) { split("", jsonData) prsJSON_SetDataValue(jsonData, "1", sprintf("ParseJSON Error: %s at ", message) (idx <= cnt ? jsonStringArr[idx] : "")) split("", jsonStringArr) return cnt + 1 } function prsJSON_CopyError(jsonData, tv) { split("", jsonData) prsJSON_SetDataValue(jsonData, "1", tv[1]) } function prsJSON_ParseNumber(jsonStringArr, cnt, idx, jsonData, prefix) { if (idx <= cnt) { if (match(jsonStringArr[idx], /^(\-?)(0|[123456789][0123456789]*)(\.[0123456789]+)?([eE][+-]?[0123456789]+)?/)) { prsJSON_SetDataValue(jsonData, prefix, substr(jsonStringArr[idx], 1, RLENGTH)) jsonStringArr[idx] = length(jsonStringArr[idx]) >= RLENGTH+1 ? substr(jsonStringArr[idx], RLENGTH+1) : "" } else idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Number not found") # starts like a number, but doesnt match the REGEX } return idx } function prsJSON_ParseString(jsonStringArr, cnt, idx, jsonData, prefix, jsonString, idxn, idxs, idxq, t) { if (idx <= cnt && length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) == "\"") { idxn = 2 jsonString = jsonStringArr[idx] do { t = length(jsonString) >= idxn ? substr(jsonString, idxn) : "" idxs = index(t, "\\") idxq = index(t, "\"") # no valid close quote found if (idxq == 0) { if (idx == cnt) break; idx++ jsonString = jsonString "," jsonStringArr[idx] } # a valid close quote was found - not before a slash if (idxq != 0 && (idxs == 0 || (idxs != 0 && idxq < idxs))) break; if (idxs != 0 && idxq == idxs + 1) # slash quote idxn = idxn + idxq else idxn = idxn + idxs + 1 } while (1) if (idxq > 0) { t = substr(jsonString, 1, idxn+idxq-1) if (match(t, /[\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037]/) == 0) { t = prsJSON_UnescapeString(t) if ( prsJSON_ValidString(t) ) { prsJSON_SetDataValue(jsonData, prefix, t) jsonStringArr[idx] = length(jsonString) >= idxn+idxq ? substr(jsonString,idxn+idxq) : "" } else idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Invalid string") } else idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Invalid character in string") } else idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Unterminated string") } else idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "String expected") return idx } function prsJSON_ParseObject(jsonStringArr, cnt, idx, jsonData, prefix, tv ) { if (idx <= cnt) { sub(/^\{[ \t\r\n\f]*/, "", jsonStringArr[idx]) #skip open { and skipwhite while (idx <= cnt && length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) != "}") { idx = prsJSON_ParseString(jsonStringArr, cnt, idx, tv, "1") if (idx <= cnt && length(tv[1]) == 0) idx = prsJSON_Error(jsonStringArr, cnt, idx, tv, "Empty string used for property name") if (idx <= cnt) { sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite if ( length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) == ":" ) { sub(/^:[ \t\r\n\f]*/, "", jsonStringArr[idx]) #skip colon and skipwhite if ( length(jsonStringArr[idx]) > 0 ) { idx = prsJSON_ParseJSONInt(jsonStringArr, cnt, idx, jsonData, prefix != "" ? prefix SUBSEP tv[1] : tv[1]) if (idx <= cnt) { sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite if (length(jsonStringArr[idx]) == 0 && idx < cnt) { idx++ sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite if (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) == "}") idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected object property") } else if (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) != "}") idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected object property or closing brace") } } else idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected JSON value (1)") } else idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected colon") } else prsJSON_CopyError(jsonData, tv) } if (idx <= cnt && (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) != "}")) idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected closing brace") if (idx <= cnt && length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) == "}") sub(/^\}[ \t\r\n\f]*/, "", jsonStringArr[idx]) #skip close } and skipwhite } return idx } function prsJSON_ParseArray(jsonStringArr, cnt, idx, jsonData, prefix, ii) { if (idx <= cnt) { sub(/^\[[ \t\r\n\f]*/, "", jsonStringArr[idx]) #skip open bracket and skipwhite ii = 1 while (idx <= cnt && length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) != "]") { idx = prsJSON_ParseJSONInt(jsonStringArr, cnt, idx, jsonData, prefix != "" ? prefix SUBSEP ii : ii ) ii++ if (idx <= cnt) { sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite if (length(jsonStringArr[idx]) == 0 && idx < cnt) { idx++; sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite if (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) == "]") idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected array value") } else if (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) != "]") idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected array value or closing bracket") } } if (idx <= cnt && (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) != "]")) idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected closing bracket") if (idx <= cnt && length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) == "]") sub(/^\][ \t\r\n\f]*/, "", jsonStringArr[idx]) #skip close bracket and skipwhite } return idx } function prsJSON_ParseJSONInt(jsonStringArr, cnt, idx, jsonData, prefix, tk) { if (idx <= cnt) { sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite if (length(jsonStringArr[idx]) > 0) { tk = substr(jsonStringArr[idx], 1, 1) if (tk == "\"" && prefix != "") idx = prsJSON_ParseString(jsonStringArr, cnt, idx, jsonData, prefix) else if (tk ~ /^[0123456789-]/ && prefix != "") idx = prsJSON_ParseNumber(jsonStringArr, cnt, idx, jsonData, prefix) else if (jsonStringArr[idx] ~ /^true/ && prefix != "") { prsJSON_SetDataValue(jsonData, prefix, "<>") jsonStringArr[idx] = length(jsonStringArr[idx]) <= 4 ? "" : substr(jsonStringArr[idx],5) } else if (jsonStringArr[idx] ~ /^false/ && prefix != "") { prsJSON_SetDataValue(jsonData, prefix, "<>") jsonStringArr[idx] = length(jsonStringArr[idx]) <= 5 ? "" : substr(jsonStringArr[idx],6) } else if (jsonStringArr[idx] ~ /^null/ && prefix != "") { prsJSON_SetDataValue(jsonData, prefix, "<>") jsonStringArr[idx] = length(jsonStringArr[idx]) <= 4 ? "" : substr(jsonStringArr[idx],5) } else if (tk == "{") idx = prsJSON_ParseObject(jsonStringArr, cnt, idx, jsonData, prefix) else if (tk == "[") idx = prsJSON_ParseArray(jsonStringArr, cnt, idx, jsonData, prefix) else idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected JSON value (2)") if (idx <= cnt) sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite } if (prefix == "" && idx <= cnt && length(jsonStringArr[idx]) != 0) idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected end of JSON text") else if (prefix == "" && idx+1 <= cnt) { idx++ idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected end of JSON text (2)") } } return idx } # # JSON Formatting Routines # function useJSON_ArrayCount( possibleArray, a, min, max, cnt, rv) { cnt = 0 for ( a in possibleArray ) { if (possibleArray[a] "" !~ /^[0123456789][0123456789]*$/) return -1 if ( cnt == 0 ) { min = possibleArray[a] max = possibleArray[a] } else { if (min == possibleArray[a] || max == possibleArray[a]) return -1 if (possibleArray[a] < min) min = possibleArray[a] if (max < possibleArray[a]) max = possibleArray[a] } cnt++ } if (min == 1 && max == cnt) return cnt return -1 } function useJSON_GetObjectMembers(jsonSchema, prefix) { if (prefix == "") prefix = "<>" return prefix in jsonSchema ? jsonSchema[prefix] : "" } # quick sort array arr function utlJSON_qsortArray(arr, left, right, i, last, t) { if (left >= right) # do nothing if array has less than 2 elements return i = left + int((right-left+1)*rand()) t = arr[left]; arr[left] = arr[i]; arr[i] = t last = left # arr[left] is now partition element for (i = left+1; i <= right; i++) { if (arr[i] < arr[left]) { last++ t = arr[last]; arr[last] = arr[i]; arr[i] = t } } t = arr[left]; arr[left] = arr[last]; arr[last] = t utlJSON_qsortArray(arr, left, last-1) utlJSON_qsortArray(arr, last+1, right) } function useJSON_GetSchema(jsonData, jsonSchema, a, tidx, tv, sv, idx) { split("", jsonSchema) for (a in jsonData) { while (match(a, SUBSEP "[^" SUBSEP "]+$")) { tidx = substr(a,1,RSTART-1) tv = substr(a,RSTART+1) sv = (tidx in jsonSchema) ? jsonSchema[tidx] : "" # if ( sv != tv && sv !~ "^" tv SUBSEP && sv !~ SUBSEP tv "$" && sv !~ SUBSEP tv SUBSEP ) # Rephrase this using index so object member names with regex characters work if ( sv != tv && index(sv, tv SUBSEP) != 1 && (length(sv) <= length(tv)+1 || substr(sv, length(sv)-length(tv)) != SUBSEP tv) && index(sv, SUBSEP tv SUBSEP) == 0 ) jsonSchema[tidx] = sv (sv == "" ? "" : SUBSEP) tv a = tidx } tidx = "<>" tv = a sv = (tidx in jsonSchema) ? jsonSchema[tidx] : "" if ( sv != tv && sv !~ "^" tv SUBSEP && sv !~ SUBSEP tv "$" && sv !~ SUBSEP tv SUBSEP ) jsonSchema[tidx] = sv (sv == "" ? "" : SUBSEP) tv } } function useJSON_EscapeString(s, ii, c, t, t2, t3, t4, cs) { cs = "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377" gsub(/\\/, "\\u005C", s) gsub(/"/, "\\\"", s) #gsub(/\//, "\\/", s) # required to decode, but not to encode gsub(/\b/, "\\b", s) gsub(/\f/, "\\f", s) gsub(/\n/, "\\n", s) gsub(/\r/, "\\r", s) gsub(/\t/, "\\t", s) for ( ii = 1 ; ii <= length(s) ; ii++ ) { t = substr(s,ii,1) if (t == "\000") # having \000 in list below doesnt work in all awks { c = 0 s = (ii > 1 ? substr(s, 1, ii-1) : "") sprintf("\\u%04X", c) (ii==length(s) ? "" : substr(s, ii+1)) ii += 5 } else { c = index("\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037", t) c = c == 0 ? -1 : c if ( c >= 0 ) { s = (ii > 1 ? substr(s, 1, ii-1) : "") sprintf("\\u%04X", c) (ii==length(s) ? "" : substr(s, ii+1)) ii += 5 } } t = index(cs, t) t2 = ii+1 <= length(s) ? index(cs, substr(s,ii+1,1)) : 0 t3 = ii+2 <= length(s) ? index(cs, substr(s,ii+2,1)) : 0 t4 = ii+3 <= length(s) ? index(cs, substr(s,ii+3,1)) : 0 if ( c < 0 && t > 64 && t <= 96 && ii+1 <= length(s) && t2 > 0 && t2 <= 64) # two character UTF-8 sequence { c = (t - 65)*64 + (t2-1) s = (ii > 1 ? substr(s, 1, ii-1) : "") sprintf("\\u%04X", c) (ii+1==length(s) ? "" : substr(s, ii+2)) ii += 5 } else if ( c < 0 && t > 96 && t <= 112 && ii+2 <= length(s) && t2 > 0 && t2 <= 64 && t3 > 0 && t3 <= 64) # three character UTF-8 sequence { c = (t - 97)*4096 + (t2-1)*64 + (t3-1) if ( c < 65536 ) { s = (ii > 1 ? substr(s, 1, ii-1) : "") sprintf("\\u%04X", c) (ii+2==length(s) ? "" : substr(s, ii+3)) ii += 5 } else { # encode in JSON-style with two \u#### UTF-16 surrogates # printf("1: %08X\n", c) s = (ii > 1 ? substr(s, 1, ii-1) : "") sprintf("\\u%04X\\u%04X", (c/1024)%1024 + 55296, c%1024 + 56320) (ii+3==length(s) ? "" : substr(s, ii+4)) ii += 11 } } # four character UTF-8 sequence, encode in JSON-style with two \u#### UTF-16 surrogates else if ( c < 0 && t > 112 && t <= 120 && ii+3 <= length(s) && t2 > 0 && t2 <= 64 && t3 > 0 && t3 <= 64 && t4 > 0 && t4 <= 64) { c = (t - 113)*262144 + (t2-1)*4096 + (t3-1)*64 + (t4-1) # printf("2: %08X, %d, %d, %d, %d\n", c, t, t2, t3, t4) # printf("\\u%04X\\u%04X\n", (c/1024)%1024 + 55296, c%1024 + 56320) c -= 65536 s = (ii > 1 ? substr(s, 1, ii-1) : "") sprintf("\\u%04X\\u%04X", (c/1024)%1024 + 55296, c%1024 + 56320) (ii+3==length(s) ? "" : substr(s, ii+4)) ii += 11 } } return "\"" s "\"" } function useJSON_GetDataValue(jsonData, prefix) { return prefix in jsonData ? jsonData[prefix] : "<>" } function useJSON_PrettyFormat(s, pretty) { if (s == "" || pretty <= 0) return s # dont sprintf the whole thing, some awks have short buffers for sprintf return sprintf("%*.*s", (pretty-1)*3, (pretty-1)*3, "") s (s == "}" || s == "]" ? "" : "\n") } function useJSON_FormatInt(jsonData, jsonSchema, prefix, pretty, allLines, member, memberArr, memberList, arrCount, a, ii) { memberList = useJSON_GetObjectMembers(jsonSchema, prefix) if ( memberList == "" ) { a = useJSON_GetDataValue(jsonData, prefix) if ( a == "<>" ) return "true" if ( a == "<>" ) return "false" if ( a == "<>" ) return "null" if ( a == "<>" ) return "" # <> is a help for dealing with empty arrays and objects # if it looks like a number, encode it as such. Cant tell a string from a number. if (a "" ~ /^(\-?)(0|[123456789][0123456789]*)(\.[0123456789]+)?([eE][+-]?[0123456789]+)?$/) return a return useJSON_EscapeString(a) } split(memberList, memberArr, SUBSEP) arrCount = useJSON_ArrayCount( memberArr ) if ( arrCount >= 0 ) { allLines = "[" (pretty == 0 ? "" : "\n") for ( ii = 1 ; ii <= arrCount ; ii++ ) allLines = allLines useJSON_PrettyFormat(useJSON_FormatInt( jsonData, jsonSchema, prefix (prefix == "" ? "" : SUBSEP) ii, (pretty != 0 ? pretty+1 : 0)) (ii < arrCount ? "," : ""), pretty != 0 ? pretty+1 : 0) allLines = allLines useJSON_PrettyFormat("]", pretty) return allLines } allLines = "{" (pretty == 0 ? "" : "\n") ii = 0 arrCount = 0 for (a in memberArr) arrCount++ utlJSON_qsortArray(memberArr, 1, arrCount) for ( ii = 1 ; ii <= arrCount ; ii++ ) allLines = allLines useJSON_PrettyFormat(useJSON_EscapeString(memberArr[ii]) (pretty == 0 ? ":" : " : ") useJSON_FormatInt(jsonData, jsonSchema, prefix (prefix == "" ? "" : SUBSEP) memberArr[ii], (pretty != 0 ? pretty+1 : 0)) (ii < arrCount ? "," : ""), pretty != 0 ? pretty+1 : 0) allLines = allLines useJSON_PrettyFormat("}", pretty) return allLines } # # Entry Points # # # ParseJSON : Parse JSON text into an awk array # # jsonString : JSON text # jsonData : array of parsed JSON data # # returns : N/A # function ParseJSON(jsonString, jsonData, jsonStringArr, cnt) { # newlines split differently in some awks, replace them with formfeeds (also white space) # if (split("1\n2\n3", jsonData, ",") == 3) # is this an awk that splits newlines differently? gsub(/\n/, "\f", jsonString) # always replace literal newlines - allows compatibility when testing split("", jsonData) # clear the array jsonData cnt = split(jsonString, jsonStringArr, ",") prsJSON_ParseJSONInt(jsonStringArr, cnt, 1, jsonData, "") } # # FormatJSON : Format parsed JSON data back into JSON text # # jsonData : array of parsed JSON data # pretty : 0 = compact format, non-zero = pretty format # # returns : string with JSON text # function FormatJSON(jsonData, pretty, jsonSchema) { useJSON_GetSchema(jsonData, jsonSchema) return useJSON_FormatInt(jsonData, jsonSchema, "", pretty ? 1 : 0) } # # JSONArrayLength : Find number of members in a JSON array # # jsonData : array of parsed JSON data # prefix : array name # # returns : number of entries in the array # function JSONArrayLength(jsonData, prefix, a, cnt, tv) { cnt = -1 for (a in jsonData) { if (prefix == "" || index(a, prefix) == 1) { tv = substr(a, prefix == "" ? 1 : (1+length(prefix)+1)) if ( index(tv, SUBSEP) ) tv = substr(tv, 1, index(tv, SUBSEP)-1) tv = tv + 0 if ( tv > cnt ) cnt = tv } } return cnt } # # JSONUnescapeString : turn a JSON-escaped string into UTF-8 # # jsonString : the escaped JSON string to convert # # returns : the string in UTF-8 # function JSONUnescapeString(jsonString) { return prsJSON_UnescapeString(jsonString) } # # JSONIsTrue : return non-zero if the value is the true value # # jsonValue : the value to test # # returns : true or false # function JSONIsTrue(jsonValue) { return jsonValue == "<>"; } # # JSONIsFalse : return non-zero if the value is the false value # # jsonValue : the value to test # # returns : true or false # function JSONIsFalse(jsonValue) { return jsonValue == "<>"; } # # JSONIsNull : return non-zero if the value is the null value # # jsonValue : the value to test # # returns : true or false # function JSONIsNull(jsonValue) { return jsonValue == "<>"; } # # JSONObjectMembers : get the set of members of an object # # jsonData : array of parsed JSON data # prefix : object name # memberArr : [out] an array of the names of the object members, if the target was an object or an array # # returns : If the target was actually an array rather than an object, the number of elements in the array # Else, zero if the target was an object or a value # function JSONObjectMembers(jsonData, prefix, memberArr, jsonSchema, memberList, rv, a) { useJSON_GetSchema(jsonData, jsonSchema) memberList = useJSON_GetObjectMembers(jsonSchema, prefix) if ( memberList == "" ) { split("", memberArr) return 0 } split(memberList, memberArr, SUBSEP) rv = useJSON_ArrayCount( memberArr ) if ( rv == -1 ) # not an array, sort the object member names { rv = 0 for (a in memberArr) rv++ utlJSON_qsortArray(memberArr, 1, rv) rv = 0 } return rv } # End of Copyright (c) 2010 Dan Saar function debug(lvl, text) { if (lvl <= DEBUG) print text > "/dev/stderr" } function wpt_init() { available = "True" archived = "False" sym = "Geocache" json_log_bool = 0 logs = "" logs_section = 0 hints = "" lat = "" yy = 0 wplist = "" nattr_yes = 0 nattr_no = 0 gs_type = "" } function umlauts(text) { # Somewhat minimal translation of HTML entities in titles gsub("ä", "\xc3\xa4", text) gsub("ö", "\xc3\xb6", text) gsub("ü", "\xc3\xbc", text) gsub("Ä", "\xc3\x84", text) gsub("Ö", "\xc3\x96", text) gsub("Ü", "\xc3\x9c", text) gsub("ß", "\xc3\x9f", text) gsub("°", "\xc2\xb0", text) gsub("&", "\\&", text) return text } function htmlclean(text) { gsub(" ", " ", text) gsub("]*>", "\n", text) gsub("<[bB][rR][^>]*>", "\n", text) gsub("<[^>]*>", "", text) # compress whitespace gsub("\n\n\n*", "\n\n", text) gsub("[ \t][ \t]*", " ", text) return text } function tableclean(text) { gsub("\n", "", text) gsub(" ", " ", text) # translate/remove HTML tags gsub("]*>", "\n", text) gsub("]*>", "", text) gsub("]*>", "", text) gsub("]*>", "", text) gsub("", "", text) gsub("", "\n", text) gsub("]*>", " | ", text) gsub("<[^>]*>", "", text) # compress whitespace gsub("[ \t][ \t]*", " ", text) return text } function remdiv(text, tag) { if (tag != "") pat = ".*
]*>[ \t\n]*" else pat = ".*]*>[ \t\n]*" sub(pat, "", text) while (text !~ "/?div") { if (getline more <= 0) break text = text "\n" more } sub("[ \t\n]*
.*", "", text) debug(3, "Div:\n" text) return text } function remspan(text, tag) { if (tag != "") pat = ".*]*>[ \t\n]*" else pat = ".*]*>[ \t\n]*" sub(pat, "", text) while (text !~ "/?span") { if (getline more <= 0) break text = text "\n" more } sub("[ \t\n]*.*", "", text) debug(3, "Span:\n" text) return text } function remspanlong(text, tag) { if (tag != "") pat = ".*]*>[ \t\n]*" else pat = ".*]*>[ \t\n]*" sub(pat, "", text) # i = "span level" i = 1; j = 0 debug(2, length(text) "\t" i " " j++ " " text) # input is in text while (i != 0) { # emergency exit if (length(text) > 500000) { debug(0, "Warning: logs exceeded 500,000 bytes!") break } # cleanup: remove , adjust "span level" while (text ~ "") { if (text ~ "") { --i; sub("", "", text) } if (text ~ "") { ++i; sub("]*>", "", text) } } debug(2, "=" length(text) "\t" i " " j++ " " text) # if "span level" down to zero, closing tag reached if (i == 0) break # get more input if (getline more <= 0) break text = text "\n" more debug(2, "+" length(more) "\t" i " " j++ " " more) } debug(1, length(text) "\t" i " " j++) sub("[ \t\n]*.*", "", text) gsub(" ", " ", text) if (tag == "CacheLogs") gsub("]*>", "", text) debug(3, "SpanLong:\n" text) return text } function remwaypoints() { text = "" while (text !~ "" && text !~ "No additional waypoints to display") { if (getline more <= 0) break text = text " " more } gsub(" ", " ", text) gsub("\n[ \t]*", "", text) debug(3, "Waypoints:\n" text "\nEnd Waypoints") return text # will return complete table contents! split by instead of # } function splitwaypoints(waypoints, line, fld, prefix, lookup, wpname, x, y, lat, lon) { text="" # separate lines split(waypoints, wps, "") i = 0 for (wp in wps) ++i wp = 1 # skip header line while (wp < i) { ++wp # get URL from full table line url = wps[wp] gsub(".*href=.", "", url) gsub("\".*", "", url) if (url !~ "^http:") { url = "" } else { debug(1, "url: " url) } # individual fields without leading/trailing blanks, remove HTML tags split(wps[wp], line, "") j = 0 for (fld in line) { ++j debug(2, "Before Line[" fld "]: " line[fld]) gsub("[ \t]*<[^>]*>", "", line[fld]) gsub("^[ \t]*", "", line[fld]) gsub("[ \t]*$", "", line[fld]) debug(2, "after Line[" fld "]: " line[fld]) } # 8 fields: 1st line old style # 9 fields: 1st line new style # 4 fields, [1]~"Note:": 2nd line old style # 4 fields, [2]~"Note:": 2nd line new style # else: drop if (j == 8) { # main information line, old style (pre-2010/07) if (!line[3]) continue prefix = substr(line[3] "00", 1, 2) lookup = line[4] wpname = line[5] lat = toupper(line[6]) gsub(" *[EW].*", "", lat) split(lat, y) lat = y[2] + y[3]/60.0 if (y[1] == "S") lat = -lat lon = toupper(line[6]) gsub("[NS] *[0-9]*.. *[0-9.]* ", "", lon) gsub("[^ 0-9.NESW-]", "", lon) split(lon, x) lon = x[2] + x[3]/60.0 if (x[1] == "W") lon = -lon text = text sprintf("\nlat=\"%.6f\" lon=\"%.6f\"|%s|%s|%s|%s", lat, lon, prefix, lookup, wpname, url) } else if (j == 9) { # main information line, new style (2010/07) if (!line[4]) continue prefix = substr(line[4] "00", 1, 2) lookup = line[5] wpname = line[6] lat = toupper(line[7]) gsub(" *[EW].*", "", lat) split(lat, y) lat = y[2] + y[3]/60.0 if (y[1] == "S") lat = -lat lon = toupper(line[7]) gsub("[NS] *[0-9]*.. *[0-9.]* ", "", lon) gsub("[^ 0-9.NESW-]", "", lon) split(lon, x) lon = x[2] + x[3]/60.0 if (x[1] == "W") lon = -lon text = text sprintf("\nlat=\"%.6f\" lon=\"%.6f\"|%s|%s|%s|%s", lat, lon, prefix, lookup, wpname, url) } else if (j == 4) { if (line[1] ~ "Note:") { # continuation line, old style text = text sprintf("|%s", line[2]) } else if (line[2] ~ "Note:") { # continuation line, new style text = text sprintf("|%s", line[3]) } } } debug(3, "Split WPs\n" text) return text } function wpclean(waypoints, line, fld, prefix, lookup, wpname, coords) { # simplify Additional Waypoints table: # prefixedname - name
coordfield
note text = "" split(waypoints, wps, "") i = 0 for (wp in wps) ++i wp = 1 while (wp < i) { ++wp split(wps[wp], line, "") j = 0 for (fld in line) { ++j gsub("[ \t]*<[^>]*>", "", line[fld]) gsub("^[ \t]*", "", line[fld]) gsub("[ \t]*$", "", line[fld]) } # 8 fields: 1st line old style # 9 fields: 1st line new style # 4 fields, [1]~"Note:": 2nd line old style # 4 fields, [2]~"Note:": 2nd line new style # else: drop if (j == 8) { # main information line, old style (pre-2010/07) if (!line[3]) continue prefix = substr(line[3] "00", 1, 2) substr(gcid, 3) lookup = line[4] wpname = line[5] gsub(" \\(.*\\).*", "", wpname) coords = toupper(line[6]) text = text sprintf("%s - %s
%s
", prefix, wpname, coords) } else if (j == 9) { # main information line, new style (2010/07) if (!line[4]) continue prefix = substr(line[4] "00", 1, 2) substr(gcid, 3) lookup = line[5] wpname = line[6] gsub(" \\(.*\\).*", "", wpname) coords = toupper(line[7]) text = text sprintf("%s - %s
%s
", prefix, wpname, coords) } else if(j == 4) { if (line[1] ~ "Note:") { # continuation line, old style text = text sprintf("%s
", line[2]) } else if (line[2] ~ "Note:") { # continuation line, new style text = text sprintf("%s
", line[3]) } } } debug(3, "Clean WPs\n" text) return text } function hex2dec(x, val) { for (val = 0; length(x); x = substr(x, 2)) val = 16*val + index("0123456789ABCDEF", substr(x, 1, 1)) - 1 return val } # Convert GC0000 to 58913 function wp2id(wp, val) { sub("^GC", "", wp) debug(5, "wp2id: " wp " ...") if ((length(wp) <= 4) && (wp < "G000")) { # old hex style val = hex2dec(wp) debug(5, "wp2id hex: " val " ...") return val } # new style, base-31, can have 4 or more places! set = "0123456789ABCDEFGHJKMNPQRTVWXYZ" val = 0 for (pos = 1; pos <= length(wp); ++pos) { val *= 31 val += index(set, substr(wp, pos, 1)) - 1 } val = val - 411120 debug(5, "wp2id id: " val " ...") return val } # to decode hints: rot13 http://lorance.freeshell.org/rot13/ function rot13 (string) { ROTFROM = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" ROTTO = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" retstr = "" for (pos = 0; pos < length(string); pos++) { char = substr(string,pos + 1,1) rotpos = index(ROTFROM,char) if (rotpos > 0) char = substr(ROTTO,rotpos,1) retstr = retstr char } return retstr } function tagstart(lvl, tag, parms) { printf "%*s", lvl*2, "" if (parms == "") printf "<%s>\n", tag else printf "<%s %s>\n", tag, parms } function tagend(lvl, tag) { printf "%*s", lvl*2, "" printf "\n", tag } function ee(text) { gsub(/&/, "\\&", text) gsub(//, "\\>", text) return text } function tagtext(lvl, tag, text) { text = ee(text) printf "%*s", lvl*2, "" printf "<%s>%s\n", tag, text, tag } function tagptext(lvl, tag, parms, text) { text = ee(text) printf "%*s", lvl*2, "" printf "<%s %s>%s\n", tag, parms, text, tag } function attr_begin1(gif, id, text) { debug(1, "attr_begin1: " gif " " id " \"" text "\"") attr_id[gif] = id; attr_text[gif] = text debug(1, "attr_id: " attr_id["slealth"]) debug(1, "attr_id: " attr_id[gif]) } function attr_begin() { # attr_begin1("slealth", 40, "Stealth required") Dont work!!! attr_id["dog"] = 1; attr_text["dog"] = "Dogs" attr_id["dogs"] = 1; attr_text["dogs"] = "Dogs allowed" attr_id["fee"] = 2; attr_text["fee"] = "Access or parking fee" attr_id["rappelling"] = 3; attr_text["rappelling"] = "Climbing gear" attr_id["boat"] = 4; attr_text["boat"] = "Boat" attr_id["scuba"] = 5; attr_text["scuba"] = "Scuba gear" attr_id["kids"] = 6; attr_text["kids"] = "Recommended for kids" attr_id["onehour"] = 7; attr_text["onehour"] = "Takes less than an hour" attr_id["scenic"] = 8; attr_text["scenic"] = "Scenic view" attr_id["hiking"] = 9; attr_text["hiking"] = "Significant hike" attr_id["climbing"] = 10; attr_text["climbing"] = "Difficult climbing" attr_id["wading"] = 11; attr_text["wading"] = "May require wading" attr_id["swimming"] = 12; attr_text["swimming"] = "May require swimming" attr_id["available"] = 13; attr_text["available"] = "Available at all times" attr_id["night"] = 14; attr_text["night"] = "Recommended at night" attr_id["winter"] = 15; attr_text["winter"] = "Available during winter" # 16 attr_id["poisonoak"] = 17; attr_text["poisonoak"] = "Poison plants" attr_id["dangerousanimals"] = 18; attr_text["dangerousanimals"] = "Dangerous Animals" attr_id["ticks"] = 19; attr_text["ticks"] = "Ticks" attr_id["mines"] = 20; attr_text["mines"] = "Abandoned mines" attr_id["cliff"] = 21; attr_text["cliff"] = "Cliff / falling rocks" attr_id["hunting"] = 22; attr_text["hunting"] = "Hunting" attr_id["danger"] = 23; attr_text["danger"] = "Dangerous area" attr_id["wheelchair"] = 24; attr_text["wheelchair"] ="Wheelchair accessible" attr_id["parking"] = 25; attr_text["parking"] = "Parking available" attr_id["public"] = 26; attr_text["public"] = "Public transportation" attr_id["water"] = 27; attr_text["water"] = "Drinking water nearby" attr_id["restrooms"] = 28; attr_text["restrooms"] ="Public restrooms nearby" attr_id["phone"] = 29; attr_text["phone"] = "Telephone nearby" attr_id["picnic"] = 30; attr_text["picnic"] = "Picnic tables nearby" attr_id["camping"] = 31; attr_text["camping"] = "Camping available" attr_id["bicycles"] = 32; attr_text["bicycles"] = "Bicycles" attr_id["motorcycles"] = 33; attr_text["motorcycles"] = "Motorcycles" attr_id["quads"] = 34; attr_text["quads"] = "Quads" attr_id["jeeps"] = 35; attr_text["jeeps"] = "Off-road vehicles" attr_id["snowmobiles"] = 36; attr_text["snowmobiles"] = "Snowmobiles" attr_id["horses"] = 37; attr_text["horses"] = "Horses" attr_id["campfires"] = 38; attr_text["campfires"] = "Campfires" attr_id["thorns"] = 39; attr_text["thorns"] = "Thorns" attr_id["stealth"] = 40; attr_text["stealth"] = "Stealth required" attr_id["stroller"] = 41; attr_text["stroller"] = "Stroller accessible" attr_id["firstaid"] = 42; attr_text["firstaid"] = "Needs maintenance" attr_id["cow"] = 43; attr_text["cow"] = "Watch for livestock" attr_id["flashlight"] = 44; attr_text["flashlight"] = "Flashlight required" attr_id["landf"] = 44; attr_text["landf"] = "Lost And Found Tour" attr_id["rv"] = 46; attr_text["rv"] = "Recreational Vehicle" attr_id["field"] = 47; attr_text["field"] = "Field Puzzle" attr_id["UV"] = 48; attr_text["UV"] = "UV Light Required" attr_id["snowshoes"] = 49; attr_text["snowshoes"] = "Snowshoes" attr_id["skiis"] = 50; attr_text["skiis"] = "Cross Country Skis" attr_id["s-tool"] = 51; attr_text["s-tool"] = "Special Tool Required" attr_id["nightcache"] = 52; attr_text["nightcache"] = "Night Cache" attr_id["parkngrab"] = 53; attr_text["parkngrab"] = "Park and Grab" attr_id["AbandonedBuilding"] = 54; attr_text["AbandonedBuilding"] = "Abandoned Structure" attr_id["hike_short"] = 55; attr_text["hike_short"] = "Short hike (less than 1km)" attr_id["hike_med"] = 56; attr_text["hike_med"] = "Medium hike (1km-10km)" attr_id["hike_long"] = 57; attr_text["hike_long"] = "Long hike (+10km)" attr_id["fuel"] = 58; attr_text["fuel"] = "Fuel Nearby" attr_id["food"] = 59; attr_text["food"] = "Food Nearby" attr_id["wirelessbeacon"] = 60; attr_text["wirelessbeacon"] = "Wireless Beacon" attr_id["partnership"] = 61; attr_text["partnership"] = "Partnership" attr_id["seasonal"] = 62; attr_text["seasonal"] = "Seasonal Access" attr_id["tourist"] = 63; attr_text["tourist"] = "Tourist Friendly" attr_id["treeclimbing"] = 64; attr_text["treeclimbing"] = "Tree Climbing" attr_id["frontyard"] = 65; attr_text["frontyard"] = "Front Yard (Private Residence)" attr_id["teamwork"] = 66; attr_text["teamwork"] = "Teamwork Required" } function tagattr(lvl, kind, yesno) { kind = kind "" #debug(1, "kind: \"" kind "\"") if (attr_id[kind] == 0) return printf "%*s", lvl*2, "" printf "", attr_id[kind], yesno printf "%s", attr_text[kind] printf "\n" } /cache_types.aspx/ { # gc 02/01/11 gs_type = $0 sub(/.* alt=./, "", gs_type) sub(/. width=.*/, "", gs_type) debug(1, "type: " gs_type) } // { if (gs_type) { gs_name = remspan($0, "ctl00_ContentBody_CacheName") next } gs_type = $0 sub(/.* alt=./, "", gs_type) sub(/. width=.*/, "", gs_type) debug(1, "type: " gs_type) } // { gs_name = remspan($0, "CacheName") } // { gs_name = remspan($0, "ctl00_ContentBody_CacheName") } // { gcid = remspan($0) } /;wp=GC.*" / { # new way, yech! gcid = $0; sub(/.*wp=/, "", gcid); sub(/".*/, "", gcid) } // { gs_short_description = remspan($0) } // { gs_long_description = remspanlong($0, "LongDescription") waypoints = "" } // { gs_long_description = remspanlong($0, "ctl00_ContentBody_LongDescription") waypoints = "" } /
", "\n", hints) if (DECODE) hints=rot13(hints) } /", "", hints) sub(".*", "", hints) gsub("
", "\n", hints) # debug(1, "Hints: " hints) if (DECODE) hints=rot13(hints) } /Additional Waypoints/ { waypoints = remwaypoints() wplist = splitwaypoints(waypoints) } /Additional Waypoints/ { waypoints = remwaypoints() wplist = splitwaypoints(waypoints) } /class="LogsTable Table"/ { # old logs_section = 1 } /class="LogsTable"/ { # new 06/28/11 logs_section = 1 } (logs_section > 0) { logs = logs $0 } (logs_section > 0) && / 0) && /<\/table>/ { logs_section -= 1 } // { logs = remspanlong($0, "CacheLogs") # remove header which does not exist >2010-01-12 sub(".*td class=.containerHeader.>Cache Logs", "", logs) } // { logs = remspanlong($0, "ctl00_ContentBody_CacheLogs") } // { stats = remspan($0) } // { numvisits = remspan($0) debug(1, numvisits) } /lnkPrintFriendly/ { gid = $0 if (gid ~ /ID=/) { # Printable page has ID number sub(/^.*ID=/, "", gid) sub(/&.*/, "", gid) } else { # Non-printable page has guid number sub(/^.*guid=/, "", gid) sub(/&.*/, "", gid) } } /^ *by /, "", gs_owner) sub(/<.*/, "", gs_owner) debug(1, "owner: " gs_owner) gs_guid = $0 sub(/.*guid=/, "", gs_guid) sub(/&.*/, "", gs_guid) } /.* alt=.Size/ { gs_size = $0 sub(/.*Size: /, "", gs_size); sub(". />.*", "", gs_size) } /by /, "", gs_owner); sub(/ [[].*/, "", gs_owner) debug(1, gs_owner) sub(/]*>/, "", gs_owner) sub(/<.a[^>]*>/, "", gs_owner) sub(/.*
/, "", gs_owner) sub(/^by /, "", gs_owner) debug(1, "owner " gs_owner) gs_size = text; sub(/.*Size: /, "", gs_size); sub(/<.*/, "", gs_size) gs_guid = text; sub(/.*guid=/, "", gs_guid) sub(/&.*/, "", gs_guid) debug(1, "guid " gs_guid) } //, "", gs_type) debug(1, "gs_type: " gs_type) gs_owner = text sub(/.*ds=2.>/, "", gs_owner); sub(/<.*/, "", gs_owner) debug(1, "gs_owner: " gs_owner) gs_size = text; sub(/.*Size: /, "", gs_size); sub(/<.*/, "", gs_size) gs_guid = text; sub(/.*guid=/, "", gs_guid) sub(/&.*/, "", gs_guid) sub(/. title=.*/, "", gs_guid) debug(1, "guid: " gs_guid) } /Hidden/ { # gc 2/1/11 getline time getline time sub(/^ */, "", time) sub(/<.*/, "", time) split(time, fld, "/") time = sprintf("%d-%02d-%02d", fld[3], fld[1], fld[2]) debug(1, "time: " time) } /> / { # gc 6/28/11 getline time getline time getline time sub(/^ */, "", time) sub(/<.*/, "", time) gsub(/-/, "/", time) rc = split(time, fld, "/") if (rc == 1) rc = split(time, fld, "-") debug(1, "timerc: " rc) if (DATEFMT == 1) time = sprintf("%d-%02d-%02d", fld[3], fld[2], fld[1]) else if (fld[1] >= 1000) time = sprintf("%d-%02d-%02d", fld[1], fld[2], fld[3]) else time = sprintf("%d-%02d-%02d", fld[3], fld[1], fld[2]) debug(1, "time: " time) } // { getline text time = remspan($0) split(time, fld, "/") time = sprintf("%d-%02d-%02d", fld[3], fld[1], fld[2]) } // { time = remspan($0, "ctl00_ContentBody_DateHidden") rc = split(time, fld, "/") if (rc == 3) { time = sprintf("%d-%02d-%02d", fld[3], fld[1], fld[2]) debug(1, "time: " time) next } rc = split(time, fld, ",") if (rc == 3) { yyyy = fld[3]; split(fld[2], fld, " ") mm = Month[ fld[1] ] dd = fld[2] time = sprintf("%d-%02d-%02d", yyyy, mm, dd) debug(1, "time: " time) next } time = "" } /ctl00_ContentBody_uxLegendScale/ { text = $0 sub(/.*alt=./, "", text); sub(/ .*/, "", text) gs_diff = text debug(1 , "gs_diff: " gs_diff) } /ctl00_ContentBody_Localize6/ { text = $0 sub(/.*alt=./, "", text); sub(/ .*/, "", text) gs_terr = text debug(1 , "gs_terr: " gs_terr) } /^ *Difficulty:<.strong>/ { getline text sub(/.*alt=./, "", text); sub(/ .*/, "", text) gs_diff = text debug(1 , "gs_diff: " gs_diff) } /^ *Difficulty:/ { # gc 2/1/11 getline text getline text getline text sub(/.*alt=./, "", text); sub(/ .*/, "", text) gs_diff = text debug(1 , "gs_diff: " gs_diff) } // { text = remspan($0) sub(/.*alt=./, "", text); sub(/ .*/, "", text) gs_diff = text } // { text = remspan($0, "ctl00_ContentBody_Difficulty") sub(/.*alt=./, "", text); sub(/ .*/, "", text) debug(1, "difficulty " text) gs_diff = text } /^ *Terrain:<.strong>/ { getline text sub(/.*alt=./, "", text); sub(/ .*/, "", text) gs_terr = text debug(1 , "gs_terr: " gs_terr) } /^ *Terrain:/ { # gc 2/1/11 getline text getline text getline text sub(/.*alt=./, "", text); sub(/ .*/, "", text) gs_terr = text debug(1 , "gs_terr: " gs_terr) } // { text = remspan($0) sub(/.*alt=./, "", text); sub(/ .*/, "", text) gs_terr = text } // { text = remspan($0, "ctl00_ContentBody_Terrain") sub(/.*alt=./, "", text); sub(/ .*/, "", text) debug(1, "terrain " text) gs_terr = text } /title=.What are Attributes?/ { text = $0 debug(5, "Attr " text) gsub("[^/, "", text) # after 06/03/10 gsub(/alt="[^"]*" title="[^"]*" width="30" height="30" .>/, "", text) gsub("

/ { if ((lat == "") || (lon == "")) { debug(0, "Waypoint coordinates not found for " gcid ", no output!") #next } # too long a block to be indented if (!INCR && first) { print "" tagstart(0, "gpx") tagtext(1, "desc", "Geocache file generated by geo-html2gpx") tagtext(1, "author", "geo-html2gpx") "date +%Y-%m-%dT%H:%M:%S" | getline date tagtext(1, "time", date) first = 0 } gs_name = umlauts(gs_name) gs_owner = umlauts(gs_owner) tagstart(1, "wpt", "lat=\"" lat "\" lon=\"" lon "\"") if (time != "") tagtext(2, "time", time "T00:00:00.0000000-07:00") tagtext(2, "name", gcid) tagtext(2, "desc", gs_name " by " gs_owner ", " \ gs_type " (" gs_diff "/" gs_terr ")") # alternate URL... tagtext(2, "url", BaseURL "?wp=" gcid) # alternate URL... tagtext(2, "url", BaseURL "?id=" gid) tagtext(2, "url", BaseURL "?wp=" gcid) tagtext(2, "urlname", gs_name) # we do this last... tagtext(2, "sym", sym) tagtext(2, "type", "Geocache|" gs_type) # FIXME? GC-written GPX files contain numeric, non-UUID, # cache/owner/finder ids # Oregon needs numeric cache id, or behaves erratically! gid = wp2id(gcid) tagstart(2, "groundspeak:cache", "id=\"" gid "\" available=\"" available \ "\" archived=\"" archived "\"" \ " xmlns:groundspeak=\"http://www.groundspeak.com/cache/1/0/1\"") tagtext(3, "groundspeak:name", gs_name) tagtext(3, "groundspeak:placed_by", gs_owner) tagptext(3,"groundspeak:owner", "id=\"" gs_guid "\"", gs_owner) tagtext(3, "groundspeak:type", gs_type) if (nattr_yes != 0 || nattr_no != 0) { tagstart(3, "groundspeak:attributes") for (i = 1; i <= nattr_yes; ++i) tagattr(4, attr_yes[i], 1) for (i = 1; i <= nattr_no; ++i) tagattr(4, attr_no[i], 0) tagend(3, "groundspeak:attributes") } tagtext(3, "groundspeak:container", gs_size) tagtext(3, "groundspeak:difficulty", gs_diff) tagtext(3, "groundspeak:terrain", gs_terr) tagtext(3, "groundspeak:country", gs_country) tagtext(3, "groundspeak:state", gs_state) if (!NOHTML) { tagptext(3, "groundspeak:short_description", "html=\"True\"", gs_short_description) if (!NOWPTS && waypoints) { # reproduce "simplified table" by GC PQ # prefixed_gcid - wpname
original_style_coord
note
waypoints = wpclean(waypoints) # include "zero" waypoints here! gs_long_description = gs_long_description \ "

Additional Waypoints

" waypoints } tagptext(3, "groundspeak:long_description", "html=\"True\"", gs_long_description) } else { gs_short_description = htmlclean(gs_short_description) tagptext(3, "groundspeak:short_description", "html=\"False\"", gs_short_description) gs_long_description = htmlclean(gs_long_description) if (waypoints) gs_long_description = gs_long_description \ "\n\nAdditional Waypoints\n" tableclean(waypoints) tagptext(3, "groundspeak:long_description", "html=\"False\"", gs_long_description) } tagtext(3, "groundspeak:encoded_hints", hints) if (json_log_bool) { nlogs = JSONArrayLength(json_logs, "data") debug(1, "New Logs: " nlogs) if (nlogs > 1) tagstart(3, "groundspeak:logs") else tagstart(3, "groundspeak:logs", "/") for (i = 1; i < nlogs; ++i) { ltype = json_logs["data" SUBSEP i SUBSEP "LogTypeImage"] if (ltype ~ /smile/) ltype = "Found it" else if (ltype ~ /happy/) ltype = "Found it" else if (ltype ~ /note/) ltype = "Write note" else if (ltype ~ /sad/) ltype = "Didn'"'"'t Find it" else if (ltype ~ /attended/) ltype = "Attended" else if (ltype ~ /rsvp/) ltype = "Will Attend" else if (ltype ~ /greenlight/) ltype = "Green" else if (ltype ~ /traffic_cone/) ltype = "Archive" else if (ltype ~ /disabled/) ltype = "Temporarily Disable Listing" else if (ltype ~ /coord_update/) ltype = "Update Coordinates" else ltype = "Unknown" ldate = json_logs["data" SUBSEP i SUBSEP "Visited"] lfinder = json_logs["data" SUBSEP i SUBSEP "UserName"] logid = json_logs["data" SUBSEP i SUBSEP "LogID"] guid = json_logs["data" SUBSEP i SUBSEP "LogGuid"] ltext = json_logs["data" SUBSEP i SUBSEP "LogText"] ltext = htmlclean(ltext) ltext = umlauts(ltext) if (lfinder == USERNAME && ltype == "Found it") sym = "Geocache Found" if (lfinder == USERNAME && ltype == "Attended") sym = "Geocache Found" tagstart(4, "groundspeak:log", "id=\"" logid "\"") tagtext(5, "groundspeak:date", ldate) tagtext(5, "groundspeak:type", ltype) tagptext(5, "groundspeak:finder", "id=\"" guid "\"", lfinder) tagptext(5, "groundspeak:text", "encoded=\"" "False" "\"", ltext) tagend(4, "groundspeak:log") } if (nlogs > 1) tagend(3, "groundspeak:logs") } else { # nlogs = split(logs, entry, "") nlogs = split(logs, entry, "
") if (nlogs > NUMLOGS+1) nlogs = NUMLOGS+1 if (nlogs > 1) tagstart(3, "groundspeak:logs") else tagstart(3, "groundspeak:logs", "/") for (i = 1; i < nlogs; ++i) { sub("]*>", "", entry[i]) sub("", "", entry[i]) if (!entry[i]) continue # old split location sub(/.*<[Ss][Tt][Rr][Oo][Nn][Gg]>.*/, "", ltype) # leaves the URL of the smiley if (ltype ~ /smile/) ltype = "Found it" else if (ltype ~ /happy/) ltype = "Found it" else if (ltype ~ /note/) ltype = "Write note" else if (ltype ~ /sad/) ltype = "Didn'"'"'t Find it" else if (ltype ~ /attended/) ltype = "Attended" else if (ltype ~ /rsvp/) ltype = "Will Attend" else if (ltype ~ /greenlight/) ltype = "Green" else if (ltype ~ /traffic_cone/) ltype = "Archive" else if (ltype ~ /disabled/) ltype = "Temporarily Disable Listing" else if (ltype ~ /coord_update/) ltype = "Update Coordinates" else ltype = "Unknown" ldate = entry[i] # split off  /blank sub(/^[^>]*>[^ ;]*[ ;]/, "", ldate) sub(/ by <.*/, "", ldate) sub(/ by /, "", ldate) sub(/.*LogDate.>about /, "", ldate) sub(/.*LogDate.>/, "", ldate) sub(/<.*/, "", ldate) gsub(/-/, "/", ldate) debug(1, "logdate: " ldate) if (ldate ~ /ago/) { cmd = sprintf("%s -d \"12am %s\" +%%Y-%%m-%%dT07:00:00Z", DATE, ldate) cmd | getline ldate; close(cmd) } else { n = split(ldate, fld, " ") if (n >= 2) { #old format: August 18 mm = Month[fld[1]] dd = fld[2] + 0 if (n >= 3) yy = fld[3] if (yy+0 == 0) yy = YR ldate = sprintf("%d-%02d-%02dT07:00:00", yy, mm, dd) } n = split(ldate, fld, "/") if (n == 3) { #new format: 08/18/2011 if (DATEFMT == 1) ldate = sprintf("%d-%02d-%02dT07:00:00", fld[3], fld[2], fld[1]) else ldate = sprintf("%d-%02d-%02dT07:00:00", fld[3], fld[1], fld[2]) debug(1, "logdate: " ldate) } } lfinder = entry[i] sub(/[^<]*]*>/, "", guid) # Delete all before .*/, "", guid) # Delete all after ]*>/, "", lfinder) # Delete all before ]*>/, "", lfinder) # Delete all before name sub(/<.*/, "", lfinder) # Delete all after name lfinder = umlauts(lfinder) debug(1, "lfinder: " lfinder) ltext = entry[i] sub(/.*found\)
/, "", ltext) sub(".*", "", ltext) sub("
]*>[^<]*", "", ltext) sub("]*>[^<]*", "", ltext) # remove remaining HTML tags from log text. Seems to be a good # idea in any case, independent of NOHTML setting! ltext = htmlclean(ltext) ltext = umlauts(ltext) if (lfinder == USERNAME && ltype == "Found it") sym = "Geocache Found" if (lfinder == USERNAME && ltype == "Attended") sym = "Geocache Found" tagstart(4, "groundspeak:log", "id=\"" logid "\"") tagtext(5, "groundspeak:date", ldate) tagtext(5, "groundspeak:type", ltype) tagptext(5, "groundspeak:finder", "id=\"" guid "\"", lfinder) tagptext(5, "groundspeak:text", "encoded=\"" "False" "\"", ltext) tagend(4, "groundspeak:log") } if (nlogs > 1) tagend(3, "groundspeak:logs") } tagstart(3, "groundspeak:travelbugs", "/") tagend(2, "groundspeak:cache") tagtext(2, "sym", sym) tagend(1, "wpt") # add Additional Waypoints in wpt form if (!NOWPTS && wplist) { split(wplist, wps, "\n") i = 0 for (wp in wps) ++i wp = 0 while (wp < i) { ++wp # lat lon|prefix|lookup|wpname|url|note # i.e.: lat="44.888267" lon="-93.159233"|PC|PARK|http://... # |GCPMG6-Parking (Parking Area)|.31 miles from cache. debug(1, "wps: " wps[wp]) split(wps[wp], line, "|") if (line[1] && (!NOZERO || (line[1] !~ "lat=\"0.000000\" lon=\"0.000000\"") ) ) { # line format: coords|prefix|lookup|wpname|note tagstart(1, "wpt", line[1]) #tagtext(2, "time", "...") tagtext(2, "name", line[2] substr(gcid,3)) tagtext(2, "cmt", line[6] ? line[6] : "") statname = line[4] gsub(" \\(.*\\).*", "", statname) desc = line[4] sub(" \\(.*", "", desc) tagtext(2, "desc", desc) tagtext(2, "url", line[5]) urlname = desc tagtext(2, "urlname", urlname) stattype = line[4] gsub(".*\\(", "", stattype) gsub("\\).*", "", stattype) tagtext(2, "sym", stattype) tagtext(2, "type", "Waypoint|" stattype) tagend(1, "wpt") } } } wpt_init() } END { if (!INCR && !first) tagend(0, "gpx") } ' | $POSTPROC