8 `basename $PROGNAME`- Convert gc.com *printable* web pages into GPX
11 `basename $PROGNAME` [options] [gc-com.html]...
14 Convert gc.com *printable* web pages into GPX, including
15 cache description and all logs.
17 The *printable* web pages can be fetched using geo-nearest,
18 geo-newest, geo-placed, geo-found, or geo-gid with the -H option.
21 -b Normalize output by postprocessing with gpsbabel
22 -e Encode hints with rot13 (e.g. NORTH = ABEGU)
23 -i Incremental, no XML and GPX headers
24 -l number Maximum number of log entries to be exported [unlimited]
25 -n No HTML in descriptions (experimental)
26 -o FMT Output FMT instead of GPX by using gpsbabel
27 -u username Indicate found status for username [$USERNAME]
28 -w Do not add "Additional Waypoints" to the GPX output
29 -z Do not output waypoints with "zero" coordinates
30 -E var=val Set environment "var" to "val"
35 Defaults can also be set with variables in file \$HOME/.georc:
40 Geocaching.com date formats that are compatible:
42 GC Format Example Compatible
43 YYYY-MM-DD 2011-07-13 yes
44 YYYY/MM/DD 2011/07/13 yes
45 MM/DD/YYYY 07/13/2011 yes
46 DD/MM/YYYY 13/07/2011 yes if DATEFMT=1 in \$HOME/.georc
47 DD/Mmm/YYYY 13/Jul/2001 no
48 Mmm/DD/YYYY Jul/13/2011 no
49 DD Mmm YY 13 Jul 11 yes (english only)
54 geo-found -n9999 -H. > /dev/null
55 geo-html2gpx *.html > found.gpx
62 # Report an error and exit
65 echo "`basename $PROGNAME`: $1" >&2
70 if [ $DEBUG -ge $1 ]; then
71 echo "`basename $PROGNAME`: $2" >&2
75 if [ `uname` = 'Darwin' ]; then
84 # Read RC file, if there is one
87 if [ -f $HOME/.georc ]; then
89 # N.B. must switch to read_rc_file if LAT/LON is ever needed here
103 while getopts "beE:iwzl:no:u:D:h?" opt
106 b) POSTPROC="gpsbabel -igpx -f- -ogpx -F-";;
110 l) NUMLOGS="$OPTARG";;
111 o) POSTPROC="gpsbabel -igpx -f- -o$OPTARG -F-";;
113 u) USERNAME="$OPTARG";;
120 shift `expr $OPTIND - 1`
127 cat "$@" | tr -d '\001\002\003\004\005\006\007\015\022\026\030' \
130 $awk -vDEBUG=$DEBUG -vINCR=$INCR \
131 -vNOWPTS=$NOWPTS -vNOZERO=$NOZERO \
134 -vUSERNAME="$USERNAME" \
136 -vDATEFMT="$DATEFMT" \
137 -vYR="$YR" -vNUMLOGS=$NUMLOGS \
139 # Copyright (c) 2010 Dan Saar
141 # Permission is hereby granted, free of charge, to any person obtaining a copy
142 # of this software and associated documentation files (the "Software"), to deal
143 # in the Software without restriction, including without limitation the rights
144 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
145 # copies of the Software, and to permit persons to whom the Software is
146 # furnished to do so, subject to the following conditions:
148 # The above copyright notice and this permission notice shall be included in
149 # all copies or substantial portions of the Software.
151 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
152 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
153 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
154 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
155 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
156 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
159 function prsJSON_hex2num(s, rv, ii, len, k)
165 for (ii = 1; ii <= len; ii++)
167 k = index("0123456789abcdef", substr(s, ii, 1))
177 function prsJSON_EncodeAsUTF8( v, s, p1, p2, p3, p4, cs )
179 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"
182 s = sprintf("%c", v )
184 else if ( v < 2048 ) # 110xxxxx 10xxxxxx
188 s = substr(cs, 65+p1, 1) substr(cs, p2+1, 1)
191 else if ( v < 65536 ) # 1110xxxx 10xxxxxx 10xxxxxx
193 p1 = int(v/4096) % 16
196 s = substr(cs, 97+p1, 1) substr(cs, p2+1, 1) substr(cs, p3+1, 1)
199 else if ( v < 1114112 ) # 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
201 p1 = int(v/262144) % 8
202 p2 = int(v/4096) % 64
205 s = substr(cs, 113+p1, 1) substr(cs, p2+1, 1) substr(cs, p3+1, 1) substr(cs, p4+1, 1)
214 function prsJSON_UnescapeString(jsonString, matchedString, matchedValue)
216 if (jsonString == "\"\"")
219 if (jsonString ~ /^".+"$/)
220 jsonString = substr(jsonString,2,length(jsonString)-2)
222 gsub(/\\\\/, "\\u005C", jsonString)
223 gsub(/\\"/, "\"", jsonString)
224 gsub(/\\\//, "/", jsonString)
225 gsub(/\\b/, "\b", jsonString)
226 gsub(/\\f/, "\f", jsonString)
227 gsub(/\\n/, "\n", jsonString)
228 gsub(/\\r/, "\r", jsonString)
229 gsub(/\\t/, "\t", jsonString)
231 if (match(jsonString, /\\[^u]/))
232 return "ParseJSON Error: Invalid String at " jsonString
234 # handle encoded UTF-16 surrogates
235 while (match(jsonString, /\\uD[89AaBb][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]\\uD[CcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]/))
237 matchedValue = (prsJSON_hex2num(substr(jsonString, RSTART+2, 4)) % 1024) * 1024 + prsJSON_hex2num(substr(jsonString, RSTART+8, 4)) % 1024 + 65536
238 #print matchedValue, substr(jsonString, RSTART+2, 4), substr(jsonString, RSTART+8, 4)
239 matchedString = prsJSON_EncodeAsUTF8( matchedValue )
240 sub(/\\uD[89AaBb][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]\\uD[CcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]/, matchedString, jsonString)
243 while (match(jsonString, /\\u[0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]/))
245 matchedValue = prsJSON_hex2num(substr(jsonString, RSTART+2, 4))
246 matchedString = prsJSON_EncodeAsUTF8( matchedValue )
247 sub(/\\u[0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf][0123456789AaBbCcDdEeFf]/, matchedString, jsonString)
253 function prsJSON_ValidString(jsonString)
255 return jsonString !~ /^ParseJSON Error: Invalid String at /
258 function prsJSON_SetDataValue(jsonData, prefix, value)
260 jsonData[prefix] = value
263 function prsJSON_Error(jsonStringArr, cnt, idx, jsonData, message)
266 prsJSON_SetDataValue(jsonData, "1", sprintf("ParseJSON Error: %s at ", message) (idx <= cnt ? jsonStringArr[idx] : ""))
267 split("", jsonStringArr)
271 function prsJSON_CopyError(jsonData, tv)
274 prsJSON_SetDataValue(jsonData, "1", tv[1])
277 function prsJSON_ParseNumber(jsonStringArr, cnt, idx, jsonData, prefix)
281 if (match(jsonStringArr[idx], /^(\-?)(0|[123456789][0123456789]*)(\.[0123456789]+)?([eE][+-]?[0123456789]+)?/))
283 prsJSON_SetDataValue(jsonData, prefix, substr(jsonStringArr[idx], 1, RLENGTH))
284 jsonStringArr[idx] = length(jsonStringArr[idx]) >= RLENGTH+1 ? substr(jsonStringArr[idx], RLENGTH+1) : ""
287 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Number not found") # starts like a number, but doesnt match the REGEX
293 function prsJSON_ParseString(jsonStringArr, cnt, idx, jsonData, prefix, jsonString, idxn, idxs, idxq, t)
295 if (idx <= cnt && length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) == "\"")
298 jsonString = jsonStringArr[idx]
302 t = length(jsonString) >= idxn ? substr(jsonString, idxn) : ""
303 idxs = index(t, "\\")
304 idxq = index(t, "\"")
306 # no valid close quote found
313 jsonString = jsonString "," jsonStringArr[idx]
316 # a valid close quote was found - not before a slash
317 if (idxq != 0 && (idxs == 0 || (idxs != 0 && idxq < idxs)))
320 if (idxs != 0 && idxq == idxs + 1) # slash quote
324 idxn = idxn + idxs + 1
330 t = substr(jsonString, 1, idxn+idxq-1)
331 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)
333 t = prsJSON_UnescapeString(t)
334 if ( prsJSON_ValidString(t) )
336 prsJSON_SetDataValue(jsonData, prefix, t)
337 jsonStringArr[idx] = length(jsonString) >= idxn+idxq ? substr(jsonString,idxn+idxq) : ""
340 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Invalid string")
343 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Invalid character in string")
346 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Unterminated string")
349 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "String expected")
354 function prsJSON_ParseObject(jsonStringArr, cnt, idx, jsonData, prefix, tv )
358 sub(/^\{[ \t\r\n\f]*/, "", jsonStringArr[idx]) #skip open { and skipwhite
360 while (idx <= cnt && length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) != "}")
362 idx = prsJSON_ParseString(jsonStringArr, cnt, idx, tv, "1")
364 if (idx <= cnt && length(tv[1]) == 0)
365 idx = prsJSON_Error(jsonStringArr, cnt, idx, tv, "Empty string used for property name")
369 sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite
371 if ( length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) == ":" )
373 sub(/^:[ \t\r\n\f]*/, "", jsonStringArr[idx]) #skip colon and skipwhite
375 if ( length(jsonStringArr[idx]) > 0 )
377 idx = prsJSON_ParseJSONInt(jsonStringArr, cnt, idx, jsonData, prefix != "" ? prefix SUBSEP tv[1] : tv[1])
380 sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite
382 if (length(jsonStringArr[idx]) == 0 && idx < cnt)
385 sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite
386 if (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) == "}")
387 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected object property")
390 else if (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) != "}")
391 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected object property or closing brace")
395 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected JSON value (1)")
398 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected colon")
401 prsJSON_CopyError(jsonData, tv)
404 if (idx <= cnt && (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) != "}"))
405 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected closing brace")
407 if (idx <= cnt && length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) == "}")
408 sub(/^\}[ \t\r\n\f]*/, "", jsonStringArr[idx]) #skip close } and skipwhite
414 function prsJSON_ParseArray(jsonStringArr, cnt, idx, jsonData, prefix, ii)
418 sub(/^\[[ \t\r\n\f]*/, "", jsonStringArr[idx]) #skip open bracket and skipwhite
421 while (idx <= cnt && length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) != "]")
423 idx = prsJSON_ParseJSONInt(jsonStringArr, cnt, idx, jsonData, prefix != "" ? prefix SUBSEP ii : ii )
428 sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite
430 if (length(jsonStringArr[idx]) == 0 && idx < cnt)
433 sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite
434 if (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) == "]")
435 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected array value")
438 else if (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) != "]")
439 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected array value or closing bracket")
443 if (idx <= cnt && (length(jsonStringArr[idx]) == 0 || substr(jsonStringArr[idx], 1, 1) != "]"))
444 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected closing bracket")
446 if (idx <= cnt && length(jsonStringArr[idx]) > 0 && substr(jsonStringArr[idx], 1, 1) == "]")
447 sub(/^\][ \t\r\n\f]*/, "", jsonStringArr[idx]) #skip close bracket and skipwhite
453 function prsJSON_ParseJSONInt(jsonStringArr, cnt, idx, jsonData, prefix, tk)
457 sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite
459 if (length(jsonStringArr[idx]) > 0)
461 tk = substr(jsonStringArr[idx], 1, 1)
462 if (tk == "\"" && prefix != "")
463 idx = prsJSON_ParseString(jsonStringArr, cnt, idx, jsonData, prefix)
464 else if (tk ~ /^[0123456789-]/ && prefix != "")
465 idx = prsJSON_ParseNumber(jsonStringArr, cnt, idx, jsonData, prefix)
466 else if (jsonStringArr[idx] ~ /^true/ && prefix != "")
468 prsJSON_SetDataValue(jsonData, prefix, "<<true>>")
469 jsonStringArr[idx] = length(jsonStringArr[idx]) <= 4 ? "" : substr(jsonStringArr[idx],5)
471 else if (jsonStringArr[idx] ~ /^false/ && prefix != "")
473 prsJSON_SetDataValue(jsonData, prefix, "<<false>>")
474 jsonStringArr[idx] = length(jsonStringArr[idx]) <= 5 ? "" : substr(jsonStringArr[idx],6)
476 else if (jsonStringArr[idx] ~ /^null/ && prefix != "")
478 prsJSON_SetDataValue(jsonData, prefix, "<<null>>")
479 jsonStringArr[idx] = length(jsonStringArr[idx]) <= 4 ? "" : substr(jsonStringArr[idx],5)
482 idx = prsJSON_ParseObject(jsonStringArr, cnt, idx, jsonData, prefix)
484 idx = prsJSON_ParseArray(jsonStringArr, cnt, idx, jsonData, prefix)
486 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected JSON value (2)")
489 sub(/^[ \t\r\n\f]+/, "", jsonStringArr[idx]) #skipwhite
492 if (prefix == "" && idx <= cnt && length(jsonStringArr[idx]) != 0)
493 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected end of JSON text")
494 else if (prefix == "" && idx+1 <= cnt)
497 idx = prsJSON_Error(jsonStringArr, cnt, idx, jsonData, "Expected end of JSON text (2)")
506 # JSON Formatting Routines
509 function useJSON_ArrayCount( possibleArray, a, min, max, cnt, rv)
513 for ( a in possibleArray )
515 if (possibleArray[a] "" !~ /^[0123456789][0123456789]*$/)
520 min = possibleArray[a]
521 max = possibleArray[a]
525 if (min == possibleArray[a] || max == possibleArray[a])
528 if (possibleArray[a] < min)
529 min = possibleArray[a]
531 if (max < possibleArray[a])
532 max = possibleArray[a]
538 if (min == 1 && max == cnt)
544 function useJSON_GetObjectMembers(jsonSchema, prefix)
546 if (prefix == "") prefix = "<<novalue>>"
547 return prefix in jsonSchema ? jsonSchema[prefix] : ""
550 # quick sort array arr
551 function utlJSON_qsortArray(arr, left, right, i, last, t)
553 if (left >= right) # do nothing if array has less than 2 elements
555 i = left + int((right-left+1)*rand())
559 last = left # arr[left] is now partition element
560 for (i = left+1; i <= right; i++)
562 if (arr[i] < arr[left])
571 arr[left] = arr[last];
573 utlJSON_qsortArray(arr, left, last-1)
574 utlJSON_qsortArray(arr, last+1, right)
577 function useJSON_GetSchema(jsonData, jsonSchema, a, tidx, tv, sv, idx)
579 split("", jsonSchema)
582 while (match(a, SUBSEP "[^" SUBSEP "]+$"))
584 tidx = substr(a,1,RSTART-1)
585 tv = substr(a,RSTART+1)
586 sv = (tidx in jsonSchema) ? jsonSchema[tidx] : ""
587 # if ( sv != tv && sv !~ "^" tv SUBSEP && sv !~ SUBSEP tv "$" && sv !~ SUBSEP tv SUBSEP )
588 # Rephrase this using index so object member names with regex characters work
589 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 )
590 jsonSchema[tidx] = sv (sv == "" ? "" : SUBSEP) tv
596 sv = (tidx in jsonSchema) ? jsonSchema[tidx] : ""
597 if ( sv != tv && sv !~ "^" tv SUBSEP && sv !~ SUBSEP tv "$" && sv !~ SUBSEP tv SUBSEP )
598 jsonSchema[tidx] = sv (sv == "" ? "" : SUBSEP) tv
602 function useJSON_EscapeString(s, ii, c, t, t2, t3, t4, cs)
604 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"
605 gsub(/\\/, "\\u005C", s)
607 #gsub(/\//, "\\/", s) # required to decode, but not to encode
614 for ( ii = 1 ; ii <= length(s) ; ii++ )
618 if (t == "\000") # having \000 in list below doesnt work in all awks
621 s = (ii > 1 ? substr(s, 1, ii-1) : "") sprintf("\\u%04X", c) (ii==length(s) ? "" : substr(s, ii+1))
626 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)
631 s = (ii > 1 ? substr(s, 1, ii-1) : "") sprintf("\\u%04X", c) (ii==length(s) ? "" : substr(s, ii+1))
637 t2 = ii+1 <= length(s) ? index(cs, substr(s,ii+1,1)) : 0
638 t3 = ii+2 <= length(s) ? index(cs, substr(s,ii+2,1)) : 0
639 t4 = ii+3 <= length(s) ? index(cs, substr(s,ii+3,1)) : 0
641 if ( c < 0 && t > 64 && t <= 96 && ii+1 <= length(s) && t2 > 0 && t2 <= 64) # two character UTF-8 sequence
643 c = (t - 65)*64 + (t2-1)
644 s = (ii > 1 ? substr(s, 1, ii-1) : "") sprintf("\\u%04X", c) (ii+1==length(s) ? "" : substr(s, ii+2))
648 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
650 c = (t - 97)*4096 + (t2-1)*64 + (t3-1)
653 s = (ii > 1 ? substr(s, 1, ii-1) : "") sprintf("\\u%04X", c) (ii+2==length(s) ? "" : substr(s, ii+3))
658 # encode in JSON-style with two \u#### UTF-16 surrogates
659 # printf("1: %08X\n", c)
660 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))
665 # four character UTF-8 sequence, encode in JSON-style with two \u#### UTF-16 surrogates
666 else if ( c < 0 && t > 112 && t <= 120 && ii+3 <= length(s) && t2 > 0 && t2 <= 64 && t3 > 0 && t3 <= 64 && t4 > 0 && t4 <= 64)
668 c = (t - 113)*262144 + (t2-1)*4096 + (t3-1)*64 + (t4-1)
669 # printf("2: %08X, %d, %d, %d, %d\n", c, t, t2, t3, t4)
670 # printf("\\u%04X\\u%04X\n", (c/1024)%1024 + 55296, c%1024 + 56320)
672 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))
680 function useJSON_GetDataValue(jsonData, prefix)
682 return prefix in jsonData ? jsonData[prefix] : "<<novalue>>"
685 function useJSON_PrettyFormat(s, pretty)
687 if (s == "" || pretty <= 0) return s
689 # dont sprintf the whole thing, some awks have short buffers for sprintf
690 return sprintf("%*.*s", (pretty-1)*3, (pretty-1)*3, "") s (s == "}" || s == "]" ? "" : "\n")
693 function useJSON_FormatInt(jsonData, jsonSchema, prefix, pretty, allLines, member, memberArr, memberList, arrCount, a, ii)
695 memberList = useJSON_GetObjectMembers(jsonSchema, prefix)
697 if ( memberList == "" )
699 a = useJSON_GetDataValue(jsonData, prefix)
700 if ( a == "<<true>>" ) return "true"
701 if ( a == "<<false>>" ) return "false"
702 if ( a == "<<null>>" ) return "null"
703 if ( a == "<<novalue>>" ) return "" # <<novalue>> is a help for dealing with empty arrays and objects
705 # if it looks like a number, encode it as such. Cant tell a string from a number.
706 if (a "" ~ /^(\-?)(0|[123456789][0123456789]*)(\.[0123456789]+)?([eE][+-]?[0123456789]+)?$/)
709 return useJSON_EscapeString(a)
712 split(memberList, memberArr, SUBSEP)
713 arrCount = useJSON_ArrayCount( memberArr )
717 allLines = "[" (pretty == 0 ? "" : "\n")
719 for ( ii = 1 ; ii <= arrCount ; ii++ )
720 allLines = allLines useJSON_PrettyFormat(useJSON_FormatInt( jsonData, jsonSchema, prefix (prefix == "" ? "" : SUBSEP) ii, (pretty != 0 ? pretty+1 : 0)) (ii < arrCount ? "," : ""), pretty != 0 ? pretty+1 : 0)
721 allLines = allLines useJSON_PrettyFormat("]", pretty)
726 allLines = "{" (pretty == 0 ? "" : "\n")
734 utlJSON_qsortArray(memberArr, 1, arrCount)
736 for ( ii = 1 ; ii <= arrCount ; ii++ )
737 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)
739 allLines = allLines useJSON_PrettyFormat("}", pretty)
749 # ParseJSON : Parse JSON text into an awk array
751 # jsonString : JSON text
752 # jsonData : array of parsed JSON data
756 function ParseJSON(jsonString, jsonData, jsonStringArr, cnt)
758 # newlines split differently in some awks, replace them with formfeeds (also white space)
759 # if (split("1\n2\n3", jsonData, ",") == 3) # is this an awk that splits newlines differently?
760 gsub(/\n/, "\f", jsonString) # always replace literal newlines - allows compatibility when testing
762 split("", jsonData) # clear the array jsonData
763 cnt = split(jsonString, jsonStringArr, ",")
764 prsJSON_ParseJSONInt(jsonStringArr, cnt, 1, jsonData, "")
768 # FormatJSON : Format parsed JSON data back into JSON text
770 # jsonData : array of parsed JSON data
771 # pretty : 0 = compact format, non-zero = pretty format
773 # returns : string with JSON text
775 function FormatJSON(jsonData, pretty, jsonSchema)
777 useJSON_GetSchema(jsonData, jsonSchema)
778 return useJSON_FormatInt(jsonData, jsonSchema, "", pretty ? 1 : 0)
782 # JSONArrayLength : Find number of members in a JSON array
784 # jsonData : array of parsed JSON data
785 # prefix : array name
787 # returns : number of entries in the array
789 function JSONArrayLength(jsonData, prefix, a, cnt, tv)
795 if (prefix == "" || index(a, prefix) == 1)
797 tv = substr(a, prefix == "" ? 1 : (1+length(prefix)+1))
798 if ( index(tv, SUBSEP) )
799 tv = substr(tv, 1, index(tv, SUBSEP)-1)
810 # JSONUnescapeString : turn a JSON-escaped string into UTF-8
812 # jsonString : the escaped JSON string to convert
814 # returns : the string in UTF-8
816 function JSONUnescapeString(jsonString)
818 return prsJSON_UnescapeString(jsonString)
822 # JSONIsTrue : return non-zero if the value is the true value
824 # jsonValue : the value to test
826 # returns : true or false
828 function JSONIsTrue(jsonValue)
830 return jsonValue == "<<true>>";
834 # JSONIsFalse : return non-zero if the value is the false value
836 # jsonValue : the value to test
838 # returns : true or false
840 function JSONIsFalse(jsonValue)
842 return jsonValue == "<<false>>";
846 # JSONIsNull : return non-zero if the value is the null value
848 # jsonValue : the value to test
850 # returns : true or false
852 function JSONIsNull(jsonValue)
854 return jsonValue == "<<null>>";
858 # JSONObjectMembers : get the set of members of an object
860 # jsonData : array of parsed JSON data
861 # prefix : object name
862 # memberArr : [out] an array of the names of the object members, if the target was an object or an array
864 # returns : If the target was actually an array rather than an object, the number of elements in the array
865 # Else, zero if the target was an object or a value
867 function JSONObjectMembers(jsonData, prefix, memberArr, jsonSchema, memberList, rv, a)
869 useJSON_GetSchema(jsonData, jsonSchema)
870 memberList = useJSON_GetObjectMembers(jsonSchema, prefix)
872 if ( memberList == "" )
878 split(memberList, memberArr, SUBSEP)
879 rv = useJSON_ArrayCount( memberArr )
880 if ( rv == -1 ) # not an array, sort the object member names
886 utlJSON_qsortArray(memberArr, 1, rv)
891 # End of Copyright (c) 2010 Dan Saar
893 function debug(lvl, text) {
895 print text > "/dev/stderr"
898 function wpt_init() {
914 function umlauts(text) {
915 # Somewhat minimal translation of HTML entities in titles
916 gsub("ä", "\xc3\xa4", text)
917 gsub("ö", "\xc3\xb6", text)
918 gsub("ü", "\xc3\xbc", text)
919 gsub("Ä", "\xc3\x84", text)
920 gsub("Ö", "\xc3\x96", text)
921 gsub("Ü", "\xc3\x9c", text)
922 gsub("ß", "\xc3\x9f", text)
923 gsub("°", "\xc2\xb0", text)
924 gsub("&", "\\&", text)
928 function htmlclean(text) {
929 gsub(" ", " ", text)
930 gsub("</?[pP][^>]*>", "\n", text)
931 gsub("<[bB][rR][^>]*>", "\n", text)
932 gsub("<[^>]*>", "", text)
933 # compress whitespace
934 gsub("\n\n\n*", "\n\n", text)
935 gsub("[ \t][ \t]*", " ", text)
939 function tableclean(text) {
941 gsub(" ", " ", text)
942 # translate/remove HTML tags
943 gsub("</?[pP][^>]*>", "\n", text)
944 gsub("</[bB][rR][^>]*>", "", text)
945 gsub("</?font[^>]*>", "", text)
946 gsub("</?table[^>]*>", "", text)
947 gsub("<t[rdh]>", "", text)
948 gsub("</tr>", "\n", text)
949 gsub("</t[dh][^>]*>", " | ", text)
950 gsub("<[^>]*>", "", text)
951 # compress whitespace
952 gsub("[ \t][ \t]*", " ", text)
956 function remdiv(text, tag) {
958 pat = ".*<div id=." tag ".[^>]*>[ \t\n]*"
960 pat = ".*<div[^>]*>[ \t\n]*"
962 while (text !~ "/?div")
964 if (getline more <= 0)
966 text = text "\n" more
968 sub("[ \t\n]*</div>.*", "", text)
969 debug(3, "Div:\n" text)
973 function remspan(text, tag) {
975 pat = ".*<span id=." tag ".[^>]*>[ \t\n]*"
977 pat = ".*<span[^>]*>[ \t\n]*"
979 while (text !~ "/?span")
981 if (getline more <= 0)
983 text = text "\n" more
985 sub("[ \t\n]*</span>.*", "", text)
986 debug(3, "Span:\n" text)
990 function remspanlong(text, tag) {
992 pat = ".*<span id=." tag ".[^>]*>[ \t\n]*"
994 pat = ".*<span[^>]*>[ \t\n]*"
998 debug(2, length(text) "\t" i " " j++ " " text)
1003 if (length(text) > 500000)
1005 debug(0, "Warning: logs exceeded 500,000 bytes!")
1008 # cleanup: remove </*span...>, adjust "span level"
1009 while (text ~ "</*span.*>")
1011 if (text ~ "</span>")
1013 --i; sub("</span>", "", text)
1015 if (text ~ "<span.*>")
1017 ++i; sub("<span[^>]*>", "", text)
1020 debug(2, "=" length(text) "\t" i " " j++ " " text)
1021 # if "span level" down to zero, closing tag reached
1024 if (getline more <= 0)
1026 text = text "\n" more
1027 debug(2, "+" length(more) "\t" i " " j++ " " more)
1029 debug(1, length(text) "\t" i " " j++)
1030 sub("[ \t\n]*</span>.*", "", text)
1031 gsub(" ", " ", text)
1032 if (tag == "CacheLogs")
1033 gsub("</?table[^>]*>", "", text)
1034 debug(3, "SpanLong:\n" text)
1038 function remwaypoints() {
1040 while (text !~ "</table>" && text !~ "No additional waypoints to display")
1042 if (getline more <= 0)
1044 text = text " " more
1046 gsub(" ", " ", text)
1047 gsub("\n[ \t]*", "", text)
1048 debug(3, "Waypoints:\n" text "\nEnd Waypoints")
1050 # will return complete table contents! split by </tr> instead of
1054 function splitwaypoints(waypoints,
1055 line, fld, prefix, lookup, wpname, x, y, lat, lon) {
1058 split(waypoints, wps, "</tr>")
1062 wp = 1 # skip header line
1066 # get URL from full table line
1068 gsub(".*href=.", "", url)
1069 gsub("\".*", "", url)
1070 if (url !~ "^http:")
1076 debug(1, "url: " url)
1078 # individual fields without leading/trailing blanks, remove HTML tags
1079 split(wps[wp], line, "</td>")
1084 debug(2, "Before Line[" fld "]: " line[fld])
1085 gsub("[ \t]*<[^>]*>", "", line[fld])
1086 gsub("^[ \t]*", "", line[fld])
1087 gsub("[ \t]*$", "", line[fld])
1088 debug(2, "after Line[" fld "]: " line[fld])
1090 # 8 fields: 1st line old style
1091 # 9 fields: 1st line new style
1092 # 4 fields, [1]~"Note:": 2nd line old style
1093 # 4 fields, [2]~"Note:": 2nd line new style
1097 # main information line, old style (pre-2010/07)
1098 if (!line[3]) continue
1099 prefix = substr(line[3] "00", 1, 2)
1102 lat = toupper(line[6])
1103 gsub(" *[EW].*", "", lat)
1105 lat = y[2] + y[3]/60.0
1108 lon = toupper(line[6])
1109 gsub("[NS] *[0-9]*.. *[0-9.]* ", "", lon)
1110 gsub("[^ 0-9.NESW-]", "", lon)
1112 lon = x[2] + x[3]/60.0
1115 text = text sprintf("\nlat=\"%.6f\" lon=\"%.6f\"|%s|%s|%s|%s",
1116 lat, lon, prefix, lookup, wpname, url)
1120 # main information line, new style (2010/07)
1121 if (!line[4]) continue
1122 prefix = substr(line[4] "00", 1, 2)
1125 lat = toupper(line[7])
1126 gsub(" *[EW].*", "", lat)
1128 lat = y[2] + y[3]/60.0
1131 lon = toupper(line[7])
1132 gsub("[NS] *[0-9]*.. *[0-9.]* ", "", lon)
1133 gsub("[^ 0-9.NESW-]", "", lon)
1135 lon = x[2] + x[3]/60.0
1138 text = text sprintf("\nlat=\"%.6f\" lon=\"%.6f\"|%s|%s|%s|%s",
1139 lat, lon, prefix, lookup, wpname, url)
1143 if (line[1] ~ "Note:")
1145 # continuation line, old style
1146 text = text sprintf("|%s", line[2])
1148 else if (line[2] ~ "Note:")
1150 # continuation line, new style
1151 text = text sprintf("|%s", line[3])
1155 debug(3, "Split WPs\n" text)
1159 function wpclean(waypoints, line, fld, prefix, lookup, wpname, coords) {
1160 # simplify Additional Waypoints table:
1161 # prefixedname - name<br>coordfield<br>note
1163 split(waypoints, wps, "</tr>")
1171 split(wps[wp], line, "</td>")
1176 gsub("[ \t]*<[^>]*>", "", line[fld])
1177 gsub("^[ \t]*", "", line[fld])
1178 gsub("[ \t]*$", "", line[fld])
1180 # 8 fields: 1st line old style
1181 # 9 fields: 1st line new style
1182 # 4 fields, [1]~"Note:": 2nd line old style
1183 # 4 fields, [2]~"Note:": 2nd line new style
1187 # main information line, old style (pre-2010/07)
1188 if (!line[3]) continue
1189 prefix = substr(line[3] "00", 1, 2) substr(gcid, 3)
1192 gsub(" \\(.*\\).*", "", wpname)
1193 coords = toupper(line[6])
1194 text = text sprintf("%s - %s<br />%s<br />", prefix, wpname, coords)
1198 # main information line, new style (2010/07)
1199 if (!line[4]) continue
1200 prefix = substr(line[4] "00", 1, 2) substr(gcid, 3)
1203 gsub(" \\(.*\\).*", "", wpname)
1204 coords = toupper(line[7])
1205 text = text sprintf("%s - %s<br />%s<br />", prefix, wpname, coords)
1209 if (line[1] ~ "Note:")
1211 # continuation line, old style
1212 text = text sprintf("%s<br />", line[2])
1214 else if (line[2] ~ "Note:")
1216 # continuation line, new style
1217 text = text sprintf("%s<br />", line[3])
1221 debug(3, "Clean WPs\n" text)
1225 function hex2dec(x, val) {
1226 for (val = 0; length(x); x = substr(x, 2))
1227 val = 16*val + index("0123456789ABCDEF", substr(x, 1, 1)) - 1
1231 # Convert GC0000 to 58913
1232 function wp2id(wp, val) {
1234 debug(5, "wp2id: " wp " ...")
1235 if ((length(wp) <= 4) && (wp < "G000"))
1239 debug(5, "wp2id hex: " val " ...")
1242 # new style, base-31, can have 4 or more places!
1243 set = "0123456789ABCDEFGHJKMNPQRTVWXYZ"
1245 for (pos = 1; pos <= length(wp); ++pos)
1248 val += index(set, substr(wp, pos, 1)) - 1
1251 debug(5, "wp2id id: " val " ...")
1255 # to decode hints: rot13 http://lorance.freeshell.org/rot13/
1256 function rot13 (string) {
1257 ROTFROM = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"
1258 ROTTO = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
1260 for (pos = 0; pos < length(string); pos++)
1262 char = substr(string,pos + 1,1)
1263 rotpos = index(ROTFROM,char)
1265 char = substr(ROTTO,rotpos,1)
1266 retstr = retstr char
1271 function tagstart(lvl, tag, parms) {
1272 printf "%*s", lvl*2, ""
1274 printf "<%s>\n", tag
1276 printf "<%s %s>\n", tag, parms
1279 function tagend(lvl, tag) {
1280 printf "%*s", lvl*2, ""
1281 printf "</%s>\n", tag
1285 gsub(/&/, "\\&", text)
1286 gsub(/</, "\\<", text)
1287 gsub(/>/, "\\>", text)
1291 function tagtext(lvl, tag, text) {
1293 printf "%*s", lvl*2, ""
1294 printf "<%s>%s</%s>\n", tag, text, tag
1297 function tagptext(lvl, tag, parms, text) {
1299 printf "%*s", lvl*2, ""
1300 printf "<%s %s>%s</%s>\n", tag, parms, text, tag
1303 function attr_begin1(gif, id, text) {
1304 debug(1, "attr_begin1: " gif " " id " \"" text "\"")
1305 attr_id[gif] = id; attr_text[gif] = text
1306 debug(1, "attr_id: " attr_id["slealth"])
1307 debug(1, "attr_id: " attr_id[gif])
1309 function attr_begin() {
1310 # attr_begin1("slealth", 40, "Stealth required") Dont work!!!
1311 attr_id["dog"] = 1; attr_text["dog"] = "Dogs"
1312 attr_id["dogs"] = 1; attr_text["dogs"] = "Dogs allowed"
1313 attr_id["fee"] = 2; attr_text["fee"] = "Access or parking fee"
1314 attr_id["rappelling"] = 3; attr_text["rappelling"] = "Climbing gear"
1315 attr_id["boat"] = 4; attr_text["boat"] = "Boat"
1316 attr_id["scuba"] = 5; attr_text["scuba"] = "Scuba gear"
1317 attr_id["kids"] = 6; attr_text["kids"] = "Recommended for kids"
1318 attr_id["onehour"] = 7; attr_text["onehour"] = "Takes less than an hour"
1319 attr_id["scenic"] = 8; attr_text["scenic"] = "Scenic view"
1320 attr_id["hiking"] = 9; attr_text["hiking"] = "Significant hike"
1322 attr_id["climbing"] = 10; attr_text["climbing"] = "Difficult climbing"
1323 attr_id["wading"] = 11; attr_text["wading"] = "May require wading"
1324 attr_id["swimming"] = 12; attr_text["swimming"] = "May require swimming"
1325 attr_id["available"] = 13; attr_text["available"] = "Available at all times"
1326 attr_id["night"] = 14; attr_text["night"] = "Recommended at night"
1327 attr_id["winter"] = 15; attr_text["winter"] = "Available during winter"
1329 attr_id["poisonoak"] = 17; attr_text["poisonoak"] = "Poison plants"
1330 attr_id["dangerousanimals"] = 18; attr_text["dangerousanimals"] = "Dangerous Animals"
1331 attr_id["ticks"] = 19; attr_text["ticks"] = "Ticks"
1333 attr_id["mines"] = 20; attr_text["mines"] = "Abandoned mines"
1334 attr_id["cliff"] = 21; attr_text["cliff"] = "Cliff / falling rocks"
1335 attr_id["hunting"] = 22; attr_text["hunting"] = "Hunting"
1336 attr_id["danger"] = 23; attr_text["danger"] = "Dangerous area"
1337 attr_id["wheelchair"] = 24; attr_text["wheelchair"] ="Wheelchair accessible"
1338 attr_id["parking"] = 25; attr_text["parking"] = "Parking available"
1339 attr_id["public"] = 26; attr_text["public"] = "Public transportation"
1340 attr_id["water"] = 27; attr_text["water"] = "Drinking water nearby"
1341 attr_id["restrooms"] = 28; attr_text["restrooms"] ="Public restrooms nearby"
1342 attr_id["phone"] = 29; attr_text["phone"] = "Telephone nearby"
1344 attr_id["picnic"] = 30; attr_text["picnic"] = "Picnic tables nearby"
1345 attr_id["camping"] = 31; attr_text["camping"] = "Camping available"
1346 attr_id["bicycles"] = 32; attr_text["bicycles"] = "Bicycles"
1347 attr_id["motorcycles"] = 33; attr_text["motorcycles"] = "Motorcycles"
1348 attr_id["quads"] = 34; attr_text["quads"] = "Quads"
1349 attr_id["jeeps"] = 35; attr_text["jeeps"] = "Off-road vehicles"
1350 attr_id["snowmobiles"] = 36; attr_text["snowmobiles"] = "Snowmobiles"
1351 attr_id["horses"] = 37; attr_text["horses"] = "Horses"
1352 attr_id["campfires"] = 38; attr_text["campfires"] = "Campfires"
1353 attr_id["thorns"] = 39; attr_text["thorns"] = "Thorns"
1355 attr_id["stealth"] = 40; attr_text["stealth"] = "Stealth required"
1356 attr_id["stroller"] = 41; attr_text["stroller"] = "Stroller accessible"
1357 attr_id["firstaid"] = 42; attr_text["firstaid"] = "Needs maintenance"
1358 attr_id["cow"] = 43; attr_text["cow"] = "Watch for livestock"
1359 attr_id["flashlight"] = 44; attr_text["flashlight"] = "Flashlight required"
1360 attr_id["landf"] = 44; attr_text["landf"] = "Lost And Found Tour"
1361 attr_id["rv"] = 46; attr_text["rv"] = "Recreational Vehicle"
1362 attr_id["field"] = 47; attr_text["field"] = "Field Puzzle"
1363 attr_id["UV"] = 48; attr_text["UV"] = "UV Light Required"
1364 attr_id["snowshoes"] = 49; attr_text["snowshoes"] = "Snowshoes"
1366 attr_id["skiis"] = 50; attr_text["skiis"] = "Cross Country Skis"
1367 attr_id["s-tool"] = 51; attr_text["s-tool"] = "Special Tool Required"
1368 attr_id["nightcache"] = 52; attr_text["nightcache"] = "Night Cache"
1369 attr_id["parkngrab"] = 53; attr_text["parkngrab"] = "Park and Grab"
1370 attr_id["AbandonedBuilding"] = 54; attr_text["AbandonedBuilding"] = "Abandoned Structure"
1371 attr_id["hike_short"] = 55; attr_text["hike_short"] = "Short hike (less than 1km)"
1372 attr_id["hike_med"] = 56; attr_text["hike_med"] = "Medium hike (1km-10km)"
1373 attr_id["hike_long"] = 57; attr_text["hike_long"] = "Long hike (+10km)"
1374 attr_id["fuel"] = 58; attr_text["fuel"] = "Fuel Nearby"
1375 attr_id["food"] = 59; attr_text["food"] = "Food Nearby"
1377 attr_id["wirelessbeacon"] = 60; attr_text["wirelessbeacon"] = "Wireless Beacon"
1378 attr_id["partnership"] = 61; attr_text["partnership"] = "Partnership"
1379 attr_id["seasonal"] = 62; attr_text["seasonal"] = "Seasonal Access"
1380 attr_id["tourist"] = 63; attr_text["tourist"] = "Tourist Friendly"
1381 attr_id["treeclimbing"] = 64; attr_text["treeclimbing"] = "Tree Climbing"
1382 attr_id["frontyard"] = 65; attr_text["frontyard"] = "Front Yard (Private Residence)"
1383 attr_id["teamwork"] = 66; attr_text["teamwork"] = "Teamwork Required"
1386 function tagattr(lvl, kind, yesno) {
1388 #debug(1, "kind: \"" kind "\"")
1389 if (attr_id[kind] == 0)
1391 printf "%*s", lvl*2, ""
1392 printf "<groundspeak:attribute id=\"%d\" inc=\"%d\">", attr_id[kind], yesno
1393 printf "%s", attr_text[kind]
1394 printf "</groundspeak:attribute>\n"
1397 /cache_types.aspx/ { # gc 02/01/11
1399 sub(/.* alt=./, "", gs_type)
1400 sub(/. width=.*/, "", gs_type)
1401 debug(1, "type: " gs_type)
1403 /<span id="ctl00_ContentBody_CacheName">/ {
1406 gs_name = remspan($0, "ctl00_ContentBody_CacheName")
1410 sub(/.* alt=./, "", gs_type)
1411 sub(/. width=.*/, "", gs_type)
1412 debug(1, "type: " gs_type)
1414 /<span id="CacheName">/ { gs_name = remspan($0, "CacheName") }
1415 /<span id="ctl00_ContentBody_CacheName">/ {
1416 gs_name = remspan($0, "ctl00_ContentBody_CacheName")
1418 /<span id=".*WaypointName".*>/ { gcid = remspan($0) }
1421 gcid = $0; sub(/.*wp=/, "", gcid); sub(/".*/, "", gcid)
1423 /<span id=".*ShortDescription">/ {
1424 gs_short_description = remspan($0)
1426 /<span id="LongDescription">/ {
1427 gs_long_description = remspanlong($0, "LongDescription")
1430 /<span id="ctl00_ContentBody_LongDescription">/ {
1431 gs_long_description = remspanlong($0, "ctl00_ContentBody_LongDescription")
1434 /<div id="div_hint"/ {
1436 gsub("\n", " ", hints)
1437 gsub("^ *", "", hints)
1438 gsub("<br>", "\n", hints)
1442 /<span id="Hints"/ {
1444 hints = htmlclean(hints)
1447 gsub("\n", " ", hints)
1449 /<span id="ctl00_ContentBody_Hints"/ {
1451 sub(".*displayMe.>", "", hints)
1452 sub("</span>.*", "", hints)
1453 gsub("<br>", "\n", hints)
1454 # debug(1, "Hints: " hints)
1458 /<b>Additional Waypoints/ {
1459 waypoints = remwaypoints()
1460 wplist = splitwaypoints(waypoints)
1462 /<strong>Additional Waypoints/ {
1463 waypoints = remwaypoints()
1464 wplist = splitwaypoints(waypoints)
1466 /class="LogsTable Table"/ { # old
1469 /class="LogsTable"/ { # new 06/28/11
1472 (logs_section > 0) {
1475 (logs_section > 0) && /<table/ {
1478 (logs_section > 0) && /<\/table>/ {
1482 /<span id="CacheLogs">/ {
1483 logs = remspanlong($0, "CacheLogs")
1484 # remove header which does not exist >2010-01-12
1485 sub(".*td class=.containerHeader.>Cache Logs</td></tr>", "", logs)
1487 /<span id="ctl00_ContentBody_CacheLogs">/ {
1488 logs = remspanlong($0, "ctl00_ContentBody_CacheLogs")
1490 /<span id=".*CacheStats">/ { stats = remspan($0) }
1491 /<span id=".*NumVisits">/ {
1492 numvisits = remspan($0)
1496 /lnkPrintFriendly/ {
1500 # Printable page has ID number
1501 sub(/^.*ID=/, "", gid)
1506 # Non-printable page has guid number
1507 sub(/^.*guid=/, "", gid)
1513 sub(/.*ds=2.>/, "", gs_owner)
1514 sub(/<.*/, "", gs_owner)
1515 debug(1, "owner: " gs_owner)
1517 sub(/.*guid=/, "", gs_guid)
1518 sub(/&.*/, "", gs_guid)
1522 sub(/.*Size: /, "", gs_size); sub(". />.*", "", gs_size)
1524 /<span id="CacheOwner"/ {
1526 debug(1, "Owner text " text)
1527 gs_type = text; sub(/<.*/, "", gs_type)
1530 sub(/.*<br>by /, "", gs_owner); sub(/ [[].*/, "", gs_owner)
1532 sub(/<a[^>]*>/, "", gs_owner)
1533 sub(/<.a[^>]*>/, "", gs_owner)
1534 sub(/.*<br .>/, "", gs_owner)
1535 sub(/^by /, "", gs_owner)
1536 debug(1, "owner " gs_owner)
1537 gs_size = text; sub(/.*Size: /, "", gs_size); sub(/<.*/, "", gs_size)
1538 gs_guid = text; sub(/.*guid=/, "", gs_guid)
1539 sub(/&.*/, "", gs_guid)
1540 debug(1, "guid " gs_guid)
1542 /<span id="ctl00_ContentBody_CacheOwner"/ {
1544 debug(2, "Owner text: " text)
1546 sub(/<br .*/, "", gs_type)
1547 sub(/.*>/, "", gs_type)
1548 debug(1, "gs_type: " gs_type)
1551 sub(/.*ds=2.>/, "", gs_owner); sub(/<.*/, "", gs_owner)
1552 debug(1, "gs_owner: " gs_owner)
1554 gs_size = text; sub(/.*Size: /, "", gs_size); sub(/<.*/, "", gs_size)
1555 gs_guid = text; sub(/.*guid=/, "", gs_guid)
1556 sub(/&.*/, "", gs_guid)
1557 sub(/. title=.*/, "", gs_guid)
1558 debug(1, "guid: " gs_guid)
1560 /<span id="ErrorText"/ {
1561 if ($0 ~ "unavailable")
1563 if ($0 ~ "been archived")
1566 /<span id="ctl00_ContentBody_ErrorText"/ {
1567 errortext = remspan($0, "ctl00_ContentBody_ErrorText")
1568 if (errortext ~ "unavailable")
1570 if (errortext ~ "been archived")
1572 debug(1, "available: " available "; archived: " archived)
1574 /<span id="LargeMapPrint"/ {
1576 lat = text; sub(/.*latitude=/, "", lat); sub(/&.*/, "", lat)
1577 lon = text; sub(/.*longitude=/, "", lon); sub(/\".*/, "", lon)
1583 lat = $0; sub(/.*lat=/, "", lat); sub(/;.*/, "", lat)
1584 lon = $0; sub(/.*lng=/, "", lon); sub(/;.*/, "", lon)
1587 /<span id=".*Location"/ {
1590 sub(/In */, "", gs_state)
1591 sub(/,.*/, "", gs_state)
1594 sub(/.*, /, "", gs_country)
1595 sub(/ <.*/, "", gs_country)
1596 sub(/^In /, "", gs_country)
1598 /lat=.*; lng=.*; guid=/ {
1601 lat = $0; sub(/.*lat=/, "", lat); sub(/;.*/, "", lat)
1602 lon = $0; sub(/.*lng=/, "", lon); sub(/;.*/, "", lon)
1605 /<span class="minorCacheDetails">Hidden/ { # gc 2/1/11
1608 sub(/^ */, "", time)
1609 sub(/<.*/, "", time)
1610 split(time, fld, "/")
1611 time = sprintf("%d-%02d-%02d", fld[3], fld[1], fld[2])
1612 debug(1, "time: " time)
1614 /> <span class="minorCacheDetails">/ { # gc 6/28/11
1618 sub(/^ */, "", time)
1619 sub(/<.*/, "", time)
1620 gsub(/-/, "/", time)
1621 rc = split(time, fld, "/")
1623 rc = split(time, fld, "-")
1624 debug(1, "timerc: " rc)
1626 time = sprintf("%d-%02d-%02d", fld[3], fld[2], fld[1])
1627 else if (fld[1] >= 1000)
1628 time = sprintf("%d-%02d-%02d", fld[1], fld[2], fld[3])
1630 time = sprintf("%d-%02d-%02d", fld[3], fld[1], fld[2])
1631 debug(1, "time: " time)
1633 /<span id="DateHidden">/ {
1636 split(time, fld, "/")
1637 time = sprintf("%d-%02d-%02d", fld[3], fld[1], fld[2])
1639 /<span id="ctl00_ContentBody_DateHidden">/ {
1640 time = remspan($0, "ctl00_ContentBody_DateHidden")
1641 rc = split(time, fld, "/")
1644 time = sprintf("%d-%02d-%02d", fld[3], fld[1], fld[2])
1645 debug(1, "time: " time)
1648 rc = split(time, fld, ",")
1652 split(fld[2], fld, " ")
1653 mm = Month[ fld[1] ]
1655 time = sprintf("%d-%02d-%02d", yyyy, mm, dd)
1656 debug(1, "time: " time)
1661 /ctl00_ContentBody_uxLegendScale/ {
1663 sub(/.*alt=./, "", text); sub(/ .*/, "", text)
1665 debug(1 , "gs_diff: " gs_diff)
1667 /ctl00_ContentBody_Localize6/ {
1669 sub(/.*alt=./, "", text); sub(/ .*/, "", text)
1671 debug(1 , "gs_terr: " gs_terr)
1673 /^ *Difficulty:<.strong>/ {
1675 sub(/.*alt=./, "", text); sub(/ .*/, "", text)
1677 debug(1 , "gs_diff: " gs_diff)
1679 /^ *Difficulty:/ { # gc 2/1/11
1683 sub(/.*alt=./, "", text); sub(/ .*/, "", text)
1685 debug(1 , "gs_diff: " gs_diff)
1687 /<span id="Difficulty">/ {
1689 sub(/.*alt=./, "", text); sub(/ .*/, "", text)
1692 /<span id="ctl00_ContentBody_Difficulty">/ {
1693 text = remspan($0, "ctl00_ContentBody_Difficulty")
1694 sub(/.*alt=./, "", text); sub(/ .*/, "", text)
1695 debug(1, "difficulty " text)
1698 /^ *Terrain:<.strong>/ {
1700 sub(/.*alt=./, "", text); sub(/ .*/, "", text)
1702 debug(1 , "gs_terr: " gs_terr)
1704 /^ *Terrain:/ { # gc 2/1/11
1708 sub(/.*alt=./, "", text); sub(/ .*/, "", text)
1710 debug(1 , "gs_terr: " gs_terr)
1712 /<span id="Terrain">/ {
1714 sub(/.*alt=./, "", text); sub(/ .*/, "", text)
1717 /<span id="ctl00_ContentBody_Terrain">/ {
1718 text = remspan($0, "ctl00_ContentBody_Terrain")
1719 sub(/.*alt=./, "", text); sub(/ .*/, "", text)
1720 debug(1, "terrain " text)
1723 /title=.What are Attributes?/ {
1725 debug(5, "Attr " text)
1726 gsub("<img src=./images/attributes/", "", text)
1728 gsub(/alt="[^"]*" width="30" height="30" .>/, "", text)
1730 gsub(/alt="[^"]*" title="[^"]*" width="30" height="30" .>/, "", text)
1731 gsub("<p class=.NoSpacing.*", "", text)
1732 gsub(/^ */, "", text)
1733 gsub(/\.gif../, "", text)
1734 gsub(/attribute-blank/, "", text)
1737 gsub(/[a-z0-9A-Z]*-no/, "", attrs_yes)
1738 gsub(/-yes/, "", attrs_yes)
1741 gsub(/[a-z0-9A-Z]*-yes/, "", attrs_no)
1742 gsub(/-no/, "", attrs_no)
1744 debug(1, "attrs_yes: " attrs_yes)
1745 debug(1, "attrs_no: " attrs_no)
1746 nattr_yes = split(attrs_yes, attr_yes, " ")
1747 nattr_no = split(attrs_no, attr_no, " ")
1748 debug(1, "nattr_yes: " nattr_yes)
1749 debug(1, "nattr_no: " nattr_no)
1751 /^{.status.:.success/ {
1752 ParseJSON($0, json_logs)
1757 Month["January"] = 1
1758 Month["February"] = 2
1765 Month["September"] = 9
1766 Month["October"] = 10
1767 Month["November"] = 11
1768 Month["December"] = 12
1769 BaseURL = "http://www.geocaching.com/seek/cache_details.aspx"
1777 if ((lat == "") || (lon == ""))
1779 debug(0, "Waypoint coordinates not found for " gcid ", no output!")
1783 # too long a block to be indented
1786 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
1788 tagtext(1, "desc", "Geocache file generated by geo-html2gpx")
1789 tagtext(1, "author", "geo-html2gpx")
1790 "date +%Y-%m-%dT%H:%M:%S" | getline date
1791 tagtext(1, "time", date)
1795 gs_name = umlauts(gs_name)
1796 gs_owner = umlauts(gs_owner)
1798 tagstart(1, "wpt", "lat=\"" lat "\" lon=\"" lon "\"")
1800 tagtext(2, "time", time "T00:00:00.0000000-07:00")
1801 tagtext(2, "name", gcid)
1802 tagtext(2, "desc", gs_name " by " gs_owner ", " \
1803 gs_type " (" gs_diff "/" gs_terr ")")
1805 # alternate URL... tagtext(2, "url", BaseURL "?wp=" gcid)
1806 # alternate URL... tagtext(2, "url", BaseURL "?id=" gid)
1807 tagtext(2, "url", BaseURL "?wp=" gcid)
1808 tagtext(2, "urlname", gs_name)
1810 # we do this last... tagtext(2, "sym", sym)
1812 tagtext(2, "type", "Geocache|" gs_type)
1814 # FIXME? GC-written GPX files contain numeric, non-UUID,
1815 # cache/owner/finder ids
1816 # Oregon needs numeric cache id, or behaves erratically!
1818 tagstart(2, "groundspeak:cache",
1819 "id=\"" gid "\" available=\"" available \
1820 "\" archived=\"" archived "\"" \
1821 " xmlns:groundspeak=\"http://www.groundspeak.com/cache/1/0/1\"")
1822 tagtext(3, "groundspeak:name", gs_name)
1823 tagtext(3, "groundspeak:placed_by", gs_owner)
1824 tagptext(3,"groundspeak:owner", "id=\"" gs_guid "\"", gs_owner)
1825 tagtext(3, "groundspeak:type", gs_type)
1827 if (nattr_yes != 0 || nattr_no != 0)
1829 tagstart(3, "groundspeak:attributes")
1830 for (i = 1; i <= nattr_yes; ++i)
1831 tagattr(4, attr_yes[i], 1)
1832 for (i = 1; i <= nattr_no; ++i)
1833 tagattr(4, attr_no[i], 0)
1834 tagend(3, "groundspeak:attributes")
1837 tagtext(3, "groundspeak:container", gs_size)
1838 tagtext(3, "groundspeak:difficulty", gs_diff)
1839 tagtext(3, "groundspeak:terrain", gs_terr)
1840 tagtext(3, "groundspeak:country", gs_country)
1841 tagtext(3, "groundspeak:state", gs_state)
1844 tagptext(3, "groundspeak:short_description", "html=\"True\"",
1845 gs_short_description)
1846 if (!NOWPTS && waypoints)
1848 # reproduce "simplified table" by GC PQ
1849 # prefixed_gcid - wpname<br />original_style_coord<br />note<br />
1850 waypoints = wpclean(waypoints)
1851 # include "zero" waypoints here!
1852 gs_long_description = gs_long_description \
1853 "<p>Additional Waypoints</p>" waypoints
1855 tagptext(3, "groundspeak:long_description", "html=\"True\"",
1856 gs_long_description)
1860 gs_short_description = htmlclean(gs_short_description)
1861 tagptext(3, "groundspeak:short_description", "html=\"False\"",
1862 gs_short_description)
1863 gs_long_description = htmlclean(gs_long_description)
1865 gs_long_description = gs_long_description \
1866 "\n\nAdditional Waypoints\n" tableclean(waypoints)
1867 tagptext(3, "groundspeak:long_description", "html=\"False\"",
1868 gs_long_description)
1870 tagtext(3, "groundspeak:encoded_hints", hints)
1874 nlogs = JSONArrayLength(json_logs, "data")
1875 debug(1, "New Logs: " nlogs)
1877 tagstart(3, "groundspeak:logs")
1879 tagstart(3, "groundspeak:logs", "/")
1881 for (i = 1; i < nlogs; ++i)
1883 ltype = json_logs["data" SUBSEP i SUBSEP "LogTypeImage"]
1884 if (ltype ~ /smile/) ltype = "Found it"
1885 else if (ltype ~ /happy/) ltype = "Found it"
1886 else if (ltype ~ /note/) ltype = "Write note"
1887 else if (ltype ~ /sad/) ltype = "Didn'"'"'t Find it"
1888 else if (ltype ~ /attended/) ltype = "Attended"
1889 else if (ltype ~ /rsvp/) ltype = "Will Attend"
1890 else if (ltype ~ /greenlight/) ltype = "Green"
1891 else if (ltype ~ /traffic_cone/) ltype = "Archive"
1892 else if (ltype ~ /disabled/) ltype = "Temporarily Disable Listing"
1893 else if (ltype ~ /coord_update/) ltype = "Update Coordinates"
1894 else ltype = "Unknown"
1896 ldate = json_logs["data" SUBSEP i SUBSEP "Visited"]
1897 lfinder = json_logs["data" SUBSEP i SUBSEP "UserName"]
1898 logid = json_logs["data" SUBSEP i SUBSEP "LogID"]
1899 guid = json_logs["data" SUBSEP i SUBSEP "LogGuid"]
1900 ltext = json_logs["data" SUBSEP i SUBSEP "LogText"]
1901 ltext = htmlclean(ltext)
1902 ltext = umlauts(ltext)
1904 if (lfinder == USERNAME && ltype == "Found it")
1905 sym = "Geocache Found"
1906 if (lfinder == USERNAME && ltype == "Attended")
1907 sym = "Geocache Found"
1908 tagstart(4, "groundspeak:log", "id=\"" logid "\"")
1909 tagtext(5, "groundspeak:date", ldate)
1910 tagtext(5, "groundspeak:type", ltype)
1911 tagptext(5, "groundspeak:finder", "id=\"" guid "\"", lfinder)
1912 tagptext(5, "groundspeak:text", "encoded=\"" "False" "\"", ltext)
1913 tagend(4, "groundspeak:log")
1917 tagend(3, "groundspeak:logs")
1921 # nlogs = split(logs, entry, "</tr>")
1922 nlogs = split(logs, entry, "</tr><tr>")
1923 if (nlogs > NUMLOGS+1)
1927 tagstart(3, "groundspeak:logs")
1929 tagstart(3, "groundspeak:logs", "/")
1931 for (i = 1; i < nlogs; ++i)
1933 sub("<tr><td[^>]*>", "", entry[i])
1934 sub("</td>", "", entry[i])
1935 if (!entry[i]) continue
1936 # old split location
1937 sub(/.*<[Ss][Tt][Rr][Oo][Nn][Gg]><img src=./, "", entry[i])
1940 #debug(1, "log: " ltype)
1941 sub(/>.*/, "", ltype) # leaves the URL of the smiley
1942 if (ltype ~ /smile/) ltype = "Found it"
1943 else if (ltype ~ /happy/) ltype = "Found it"
1944 else if (ltype ~ /note/) ltype = "Write note"
1945 else if (ltype ~ /sad/) ltype = "Didn'"'"'t Find it"
1946 else if (ltype ~ /attended/) ltype = "Attended"
1947 else if (ltype ~ /rsvp/) ltype = "Will Attend"
1948 else if (ltype ~ /greenlight/) ltype = "Green"
1949 else if (ltype ~ /traffic_cone/) ltype = "Archive"
1950 else if (ltype ~ /disabled/) ltype = "Temporarily Disable Listing"
1951 else if (ltype ~ /coord_update/) ltype = "Update Coordinates"
1952 else ltype = "Unknown"
1955 # split off /blank
1956 sub(/^[^>]*>[^ ;]*[ ;]/, "", ldate)
1957 sub(/ by <.*/, "", ldate)
1958 sub(/ by /, "", ldate)
1959 sub(/.*LogDate.>about /, "", ldate)
1960 sub(/.*LogDate.>/, "", ldate)
1961 sub(/<.*/, "", ldate)
1962 gsub(/-/, "/", ldate)
1963 debug(1, "logdate: " ldate)
1966 cmd = sprintf("%s -d \"12am %s\" +%%Y-%%m-%%dT07:00:00Z",
1968 cmd | getline ldate; close(cmd)
1972 n = split(ldate, fld, " ")
1975 #old format: August 18
1982 ldate = sprintf("%d-%02d-%02dT07:00:00", yy, mm, dd)
1984 n = split(ldate, fld, "/")
1987 #new format: 08/18/2011
1989 ldate = sprintf("%d-%02d-%02dT07:00:00",
1990 fld[3], fld[2], fld[1])
1992 ldate = sprintf("%d-%02d-%02dT07:00:00",
1993 fld[3], fld[1], fld[2])
1994 debug(1, "logdate: " ldate)
1999 sub(/[^<]*</, "", lfinder) # Delete all before <A NAME...
2002 sub(/[^"]*"/, "", logid)
2003 sub(/.* id="/, "", logid)
2004 sub(/.*LUID=/, "", logid)
2005 sub(/\".*/, "", logid)
2006 debug(1, "logid: " logid)
2009 debug(1, "guid: " guid)
2010 #sub(/[^>]*>/, "", guid) # Delete all before <A HREF...
2011 #sub(/>.*/, "", guid) # Delete all after <A HREF...
2012 sub(/.*guid=/, "", guid)
2013 sub(/\".*/, "", guid)
2014 sub(/\&.*/, "", guid)
2015 sub(/. id=.*/, "", guid)
2016 debug(1, "guid: " guid)
2018 #debug(1, "lfinder: " lfinder)
2019 sub(/[^>]*>/, "", lfinder) # Delete all before <A HREF...
2020 #debug(1, "lfinder: " lfinder)
2021 #sub(/[^>]*>/, "", lfinder) # Delete all before name
2022 sub(/<.*/, "", lfinder) # Delete all after name
2023 lfinder = umlauts(lfinder)
2024 debug(1, "lfinder: " lfinder)
2027 sub(/.*found\)<br .>/, "", ltext)
2028 sub("</font>.*", "", ltext)
2029 sub("<a href=.log.aspx[^>]*>[^<]*</a>", "", ltext)
2030 sub("<a href=.upload.aspx[^>]*>[^<]*</a>", "", ltext)
2031 # remove remaining HTML tags from log text. Seems to be a good
2032 # idea in any case, independent of NOHTML setting!
2033 ltext = htmlclean(ltext)
2034 ltext = umlauts(ltext)
2036 if (lfinder == USERNAME && ltype == "Found it")
2037 sym = "Geocache Found"
2038 if (lfinder == USERNAME && ltype == "Attended")
2039 sym = "Geocache Found"
2040 tagstart(4, "groundspeak:log", "id=\"" logid "\"")
2041 tagtext(5, "groundspeak:date", ldate)
2042 tagtext(5, "groundspeak:type", ltype)
2043 tagptext(5, "groundspeak:finder", "id=\"" guid "\"", lfinder)
2044 tagptext(5, "groundspeak:text", "encoded=\"" "False" "\"", ltext)
2045 tagend(4, "groundspeak:log")
2048 tagend(3, "groundspeak:logs")
2051 tagstart(3, "groundspeak:travelbugs", "/")
2053 tagend(2, "groundspeak:cache")
2054 tagtext(2, "sym", sym)
2057 # add Additional Waypoints in wpt form
2058 if (!NOWPTS && wplist)
2060 split(wplist, wps, "\n")
2068 # lat lon|prefix|lookup|wpname|url|note
2069 # i.e.: lat="44.888267" lon="-93.159233"|PC|PARK|http://...
2070 # |GCPMG6-Parking (Parking Area)|.31 miles from cache.
2071 debug(1, "wps: " wps[wp])
2072 split(wps[wp], line, "|")
2074 (!NOZERO || (line[1] !~ "lat=\"0.000000\" lon=\"0.000000\"") ) )
2076 # line format: coords|prefix|lookup|wpname|note
2077 tagstart(1, "wpt", line[1])
2078 #tagtext(2, "time", "...")
2079 tagtext(2, "name", line[2] substr(gcid,3))
2080 tagtext(2, "cmt", line[6] ? line[6] : "")
2082 gsub(" \\(.*\\).*", "", statname)
2085 sub(" \\(.*", "", desc)
2086 tagtext(2, "desc", desc)
2088 tagtext(2, "url", line[5])
2091 tagtext(2, "urlname", urlname)
2094 gsub(".*\\(", "", stattype)
2095 gsub("\\).*", "", stattype)
2096 tagtext(2, "sym", stattype)
2097 tagtext(2, "type", "Waypoint|" stattype)
2105 if (!INCR && !first)