* /trunk/src/gpstools/GPST.pm
[gpstools.git] / gpst
blob4c46c50ead62fa47ebbdd9826b46880c7a28a527
1 #!/usr/bin/perl -w
3 #=======================================================================
4 # $Id$
5 # File ID: 082106ec-f924-11dd-b757-0001805bf4b1
6 # Converts between various GPS formats
8 # Character set: UTF-8
9 # ©opyleft 2002– Øyvind A. Holm <sunny@sunbase.org>
10 # License: GNU General Public License version 2 or later, see end of
11 # file for legal stuff.
12 #=======================================================================
14 BEGIN {
15 our @version_array;
18 use strict;
19 use Getopt::Long;
20 use Time::Local qw { timegm_nocheck };
22 BEGIN {
23 push(@INC, "$ENV{'HOME'}/bin/src/gpstools");
24 our @version_array;
27 use GPST;
28 use GPSTdate;
29 use GPSTdebug;
30 use GPSTgeo;
31 use GPSTxml;
33 $| = 1;
35 our $Debug = 0;
37 our %Opt = (
38 # Initial values for command line arguments {{{
40 'chronology' => 0,
41 'create-breaks' => 0,
42 'debug' => 0,
43 'double-y-scale' => 0,
44 'epoch' => 0,
45 'fix' => 0,
46 'from-date' => "",
47 'help' => 0,
48 'inside' => 0,
49 'near' => "",
50 'output-format' => "gpsml",
51 'outside' => 0,
52 'pos1' => "",
53 'pos2' => "",
54 'require' => "",
55 'round' => "",
56 'save-to-file' => "\n", # \n = undefined, it’s banned in filenames anyway.
57 'short-date' => 0,
58 'skip-dups' => 0,
59 'strip-whitespace' => 0,
60 'time-shift' => 0,
61 'undefined' => "",
62 'verbose' => 0,
63 'version' => 0,
65 # }}}
68 our $progname = $0;
69 $progname =~ s/^.*\/(.*?)$/$1/;
71 my $rcs_id = '$Id$';
72 my $id_date = $rcs_id;
73 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
75 push(@main::version_array, $rcs_id);
77 Getopt::Long::Configure("bundling");
78 GetOptions(
79 # Command line options {{{
81 "chronology" => \$Opt{'chronology'},
82 "create-breaks|t" => \$Opt{'create-breaks'},
83 "debug" => \$Opt{'debug'},
84 "double-y-scale|y" => \$Opt{'double-y-scale'},
85 "epoch|e" => \$Opt{'epoch'},
86 "fix" => \$Opt{'fix'},
87 "from-date=s" => \$Opt{'from-date'},
88 "help|h" => \$Opt{'help'},
89 "inside" => \$Opt{'inside'},
90 "near" => \$Opt{'near'},
91 "output-format|o=s" => \$Opt{'output-format'},
92 "outside" => \$Opt{'outside'},
93 "pos1=s" => \$Opt{'pos1'},
94 "pos2=s" => \$Opt{'pos2'},
95 "require|r=s" => \$Opt{'require'},
96 "round|R=s" => \$Opt{'round'},
97 "save-to-file|S=s" => \$Opt{'save-to-file'},
98 "short-date|s" => \$Opt{'short-date'},
99 "skip-dups|d" => \$Opt{'skip-dups'},
100 "strip-whitespace|w" => \$Opt{'strip-whitespace'},
101 "time-shift|T=i" => \$Opt{'time-shift'},
102 "undefined|n=s" => \$Opt{'undefined'},
103 "verbose|v+" => \$Opt{'verbose'},
104 "version" => \$Opt{'version'},
106 # }}}
107 ) || die("$progname: Option error. Use -h for help.\n");
109 my %Dat;
111 my $PAUSE_LIMIT = 2 * 60; # Antall sekunder mellom to punkter det må til før en move legges inn.
112 my $Udef = "?";
113 my $DIGIT = '[0-9\.\-\+]'; # Used in regexps
114 $GPST::Spc = $Opt{'strip-whitespace'} ? "" : " ";
115 my $Spc = $GPST::Spc; # FIXME
116 my $found_move = 0; # Settes til 1 hvis en /^# move$/ blir funnet.
117 my $first_time = 0;
118 my $last_time = 0;
119 my ($last_lon, $last_lat, $last_line) =
120 ( 1000, 1000, ""); # Vi kan jo teoretisk sett være i Greenwich eller på ekvator
121 my ($lat1, $lon1, $lat2, $lon2) =
122 (-1000, -1000, 1000, 1000);
124 our %Cmd = (
125 'gpsbabel' => '/usr/local/bin/gpsbabel',
128 my %Poscount = ();
130 if ($Opt{'output-format'} eq "pgtab") {
131 $Opt{'require'} .= "p";
133 my %Req = (
134 'ele' => ($Opt{'require'} =~ /e/) ? 1 : 0,
135 'position' => ($Opt{'require'} =~ /p/) ? 1 : 0,
136 'time' => ($Opt{'require'} =~ /t/) ? 1 : 0,
138 $Opt{'require'} =~ /[^ept]/
139 && die("$0: Unknown flag in --require (-r) value\n");
141 $Opt{'debug'} && ($Debug = 1);
142 $Opt{'help'} && usage(0);
143 if ($Opt{'version'}) {
144 print_version();
145 exit(0);
148 if ($Opt{'pos1'} =~ /^($DIGIT+),($DIGIT+)$/) {
149 $lat1 = $1;
150 $lon1 = $2;
152 if ($Opt{'pos2'} =~ /^($DIGIT+),($DIGIT+)$/) {
153 $lat2 = $1;
154 $lon2 = $2;
156 if ($lat1 > $lat2) {
157 my $Tmp = $lat1;
158 $lat1 = $lat2;
159 $lat2 = $Tmp;
161 if ($lon1 > $lon2) {
162 my $Tmp = $lon1;
163 $lon1 = $lon2;
164 $lon2 = $Tmp;
167 if ($Opt{'epoch'} && $Opt{'short-date'}) {
168 die("$progname: Cannot mix the --epoch (-e) and --short-date (-s) options\n");
171 if ($Opt{'inside'} && $Opt{'outside'}) {
172 die("$progname: Cannot mix the --inside and --outside options\n");
175 # To avoid printing out extra "/> at the start of svg output:
176 my $svg_start_thing = "";
178 my %Round = ();
180 if (defined($Opt{'round'})) {
181 my $R = $Opt{'round'};
182 $R =~ s/([a-z]+)=(\d+)/($Round{$1}=$2, "")/eg;
185 length($Opt{'undefined'}) && ($Udef = $Opt{'undefined'});
187 $Opt{'save-to-file'} eq "\n" && print_header(*STDOUT);
189 my @first_lines;
190 my $xml_data;
191 my $data_line = "";
192 our $curr_file = "";
194 my $from_stdin = scalar(@ARGV) ? 0 : 1;
196 $from_stdin && push(@ARGV, "-");
198 for $curr_file (@ARGV) {
199 # Scan through stdin or specified files and send every GPS entry to
200 # print_entry()
201 # {{{
202 print(STDERR "$progname: Opening \"$curr_file\" for read\n") if $Opt{'verbose'};
203 if (open(CurrFP, "<$curr_file")) {
204 # {{{
205 while (<CurrFP>) {
206 $data_line = $_;
207 %Dat = (
208 'year' => '', 'month' => '', 'day' => '',
209 'hour' => '', 'min' => '', 'sec' => '',
210 'epoch' => '',
211 'date-format' => '',
212 'lat' => '', 'lon' => '',
213 'ele' => '',
214 'desc' => '',
215 'error' => "",
216 'what' => 'tp',
219 $Opt{'epoch'} && ($Dat{'date-format'} = "epoch");
220 $Opt{'short-date'} && ($Dat{'date-format'} = "short");
222 if ($Opt{'save-to-file'} ne "\n") {
223 push(@first_lines, $_);
225 s/^# error // && ($Dat{'error'} = "error");
226 s/^# ?// && ($Dat{'error'} = "desc");
227 $xml_data = "";
228 if (m#^<(e?tp)\b(.*?)>(.*?)</(e?tp)>\s*$#) {
229 # gpsml — The main storage format {{{
230 my ($Elem, $Props, $Data) =
231 ( $1, $2, $3);
232 my $err_str = ($Props =~ /\berr="(.*?)"/) ? $1 : "error";
233 $Elem eq "etp" && ($Dat{'error'} = $err_str);
234 my $Time = "";
235 $Data =~ m#<time>(.*?)</time># && ($Time = $1);
236 $Time =~ s{
237 (\d\d\d\d)-?(\d\d)-?(\d\d)[T ](\d\d):?(\d\d):?([\d\.]+?)Z
239 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
240 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
241 ( $1, $2, $3,
242 $4, $5, $6);
244 }ex;
245 $Data =~ m#<lat>($DIGIT*?)</lat># && ($Dat{'lat'} = $1);
246 $Data =~ m#<lon>($DIGIT*?)</lon># && ($Dat{'lon'} = $1);
247 $Data =~ m#<ele>($DIGIT*?)</ele># && ($Dat{'ele'} = $1);
248 $Data =~ m#<desc>(.*?)</desc># && ($Dat{'desc'} = $1);
249 print_entry(%Dat);
250 # }}}
251 } elsif (m#^<break\b.*?/>#) {
252 $found_move = 1;
253 } elsif (m#^<(title|pause)\b.*?>(.*?)</(title|pause)>#) {
254 $Dat{'what'} = $1;
255 $Dat{$1} = $2;
256 print_entry(%Dat);
257 } elsif (m#^<desc\b.*?>(.*$)#s) {
258 $Dat{'what'} = "desc";
259 my $Txt = $1;
260 until ($Txt =~ m#</desc>#s) {
261 $Txt .= <CurrFP>;
263 $Txt =~ s#^(.*)(</desc>.*$)#$1#s;
264 $Dat{'desc'} = $Txt;
265 print_entry(%Dat);
266 } elsif (/<gpx\b/) {
267 $xml_data = $_;
268 $xml_data .= join("", <CurrFP>);
269 if (!length($Opt{'output-format'})) {
270 $Opt{'output-format'} = "gpx";
271 print_header(*STDOUT);
273 read_xmlfile($xml_data);
274 last;
275 } elsif (/^move$/) {
276 $found_move = 1;
277 } elsif (m#^(\d+)\t($DIGIT+)\t($DIGIT+)\t($DIGIT)#) {
278 # CSV format, epoch style {{{
279 my ($ep_time, $lon_val, $lat_val, $Alt) =
280 ( $1, $2, $3, $4);
281 $Dat{'epoch'} = $ep_time;
282 ($Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
283 $Dat{'day'}, $Dat{'month'}, $Dat{'year'},
284 $Dat{'wday'}, $Dat{'yday'}) = gmtime($ep_time);
285 $Dat{'month'}++; # Urgh Ⅰ
286 $Dat{'year'} += 1900; # Urgh Ⅱ
287 print_entry(%Dat);
288 # }}}
289 } elsif (
291 (\d\d\d\d)-?(\d\d)-?(\d\d)[T\ ](\d\d):?(\d\d):?(\d\d)Z?\t
292 ($DIGIT+)\t($DIGIT+)\t($DIGIT)
295 # CSV format, human-readable date format {{{
296 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
297 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
298 $Dat{'lon'}, $Dat{'lat'}, $Dat{'ele'}) =
299 ($1, $2, $3,
300 $4, $5, $6,
301 $7, $8, $9);
302 print_entry(%Dat);
303 # }}}
304 } elsif (/^Trackpoint\t/) {
305 # Trackpoint\tN60.41630 E5.31675\t09.02.2006 20:24:37 (UTC)\t13.6 m\t\t93.9 m\t00:00:06\t56 kph\t123° true {{{
307 # Trackpoint\t
308 # N60.41630 E5.31675\t
309 # 09.02.2006 20:24:37 (UTC)\t
310 # 13.6 m\t
311 # \t
312 # 93.9 m\t
313 # 00:00:06\t
314 # 56 kph\t
315 # 123° true
316 my $Orig = $_;
317 $Orig =~ s/[\r\n]+$//;
318 my ($Marker_f, $Position_f, $Time_f, $Alt_f, $Depth_f,
319 $Leglength_f, $Legtime_f, $Legspeed_f, $Legcourse_f) =
320 split(/\t/, $Orig .
321 # Nødløsning for å unngå at variabler
322 # blir udefinert.
323 "\t\t\t\t\t\t\t\t\t\t"
325 # D(join("",
326 # "Position_f=\"$Position_f\" \x7B\x7B\x7B\n",
327 # "Time_f=\"$Time_f\"\n",
328 # "Alt_f=\"$Alt_f\"\n",
329 # "Depth_f=\"$Depth_f\"\n",
330 # "Leglength_f=\"$Leglength_f\"\n",
331 # "Legtime_f=\"$Legtime_f\"\n",
332 # "Legspeed_f=\"$Legspeed_f\"\n",
333 # "Legcourse_f=\"$Legcourse_f\" \x7D\x7D\x7D\n",
334 # ));
335 my ($NS, $WE,
336 $Alt_unit,
337 $Leglength,
338 $Legtime_hour, $Legtime_min, $Legtime_sec,
339 $Legspeed, $Legspeed_unit,
340 $Legcourse
341 ) = ("", "", "", "", "", "", "", "", "", "", "", "", "",
342 "", "", "", "", "", "", "", "", "", "");
343 ($Position_f =~ /^(N|S)([\d\.]+) (W|E)([\d\.]+)/) &&
344 ($NS = $1, $Dat{'lat'} = $2, $WE = $3, $Dat{'lon'} = $4);
345 ($Time_f =~ /^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+) \((.+?)\)/) &&
346 ($Dat{'day'} = $1, $Dat{'month'} = $2, $Dat{'year'} = $3,
347 $Dat{'hour'} = $4, $Dat{'min'} = $5, $Dat{'sec'} = $6);
348 ($Alt_f =~ /^($DIGIT+) (.*?)/) &&
349 ($Dat{'ele'} = $1, $Alt_unit = $2);
350 # D("ele = \"$Dat{'ele'}\"");
351 ($NS eq "S") && ($Dat{'lat'} = 0-$Dat{'lat'});
352 ($WE eq "W") && ($Dat{'lon'} = 0-$Dat{'lon'});
353 # MapSource in win xp writes YYYY, but YY in win98se.
355 defined($Dat{'year'})
356 && $Dat{'year'} =~ /\d/
357 && $Dat{'year'} < 1900
358 ) && ($Dat{'year'} += 2000);
359 print_entry(%Dat);
360 # }}}
361 } elsif (/^Track\t(.*?)\t/) {
362 $Dat{'title'} = txt_to_xml($1);
363 $Dat{'what'} = "title";
364 $found_move = 1;
365 print_entry(%Dat);
366 } elsif (
369 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)\t
370 (.+)\xB0(.+)'(.+)"\t
371 (.+)\xB0(.+)'(.+)"
374 # T 09/01/2002 11:51:26 60°23'36.3" 5°19'35.9" {{{
375 my ($lat_d, $lat_m, $lat_s, $lon_d, $lon_m, $lon_s);
376 ($Dat{'month'}, $Dat{'day'}, $Dat{'year'},
377 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'},
378 $lat_d, $lat_m, $lat_s,
379 $lon_d, $lon_m, $lon_s) =
380 ($1, $2, $3,
381 $4, $5, $6,
382 $7, $8, $9,
383 $10, $11, $12);
384 my $Flat = defined($Round{'lat'}) ? ".$Round{'lat'}" : "";
385 my $Flon = defined($Round{'lon'}) ? ".$Round{'lon'}" : "";
386 $Dat{'lat'} = sprintf("%${Flat}f",
387 1.0*($lat_d+($lat_m/60)+($lat_s/3600)));
388 $Dat{'lon'} = sprintf("%${Flon}f",
389 1.0*$lon_d+($lon_m/60)+($lon_s/3600));
390 print_entry(%Dat);
391 # }}}
392 } elsif (
394 1\ (\S+)\ (\S+)\ (\S+)\ (\S+)\x20
395 (\d\d)/(\d\d)/(\d\d\d\d)\ (\d\d):(\d\d):(\d\d)
398 # 1 60.3938222 5.3238754 17.3 0 09/01/2002 14:18:23 {{{
399 ($Dat{'lat'}, $Dat{'lon'}, $Dat{'speed'},
400 $Dat{'unkn'},
401 $Dat{'month'}, $Dat{'day'}, $Dat{'year'},
402 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
403 ($1, $2, $3,
405 $5, $6, $7,
406 $8, $9, $10);
407 print_entry(%Dat);
408 # }}}
409 } elsif (/^
410 # @020721221336N6048353E00701826S015-00001E4859N1673U0000 {{{
411 # Regexp {{{
412 (@) # @
413 (\d\d) # Year
414 (\d\d) # Month
415 (\d\d) # Day
416 (\d\d) # Hours
417 (\d\d) # Minutes
418 (\d\d) # Seconds
419 ([NS]) # N|S
420 (\d\d) # Latitude degree
421 (\d\d) # Latitude minute
422 (\d\d\d) # Latitude minute decimals
423 ([EW]) # E|W
424 (\d\d\d) # Longitude degree
425 (\d\d) # Longitude minute
426 (\d\d\d) # Longitude minute degree
427 (....) # Accurancy
428 (......) # Elevation
429 (...............)
430 # }}}
431 /x) {
432 my ($NS, $EW, $lat_deg, $lat_degmin, $lat_mindec, $lon_deg,
433 $lon_degmin, $lon_mindec);
434 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'}, $Dat{'hour'},
435 $Dat{'min'}, $Dat{'sec'}, $NS, $lat_deg,
436 $lat_degmin, $lat_mindec, $EW,
437 $lon_deg, $lon_degmin, $lon_mindec,
438 $Dat{'accur'}, $Dat{'ele'}, $Dat{'unknown'}) =
439 ($2+2000, $3, $4, $5,
440 $6, $7, $8, $9,
441 $10, $11, $12,
442 $13, $14, $15,
443 $16, $17, $18);
444 my $ep_time = timegm_nocheck(
445 $Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
446 $Dat{'day'}, $Dat{'month'}-1, $Dat{'year'}
448 $last_time = $ep_time;
449 my $Flat = defined($Round{'lat'}) ? ".$Round{'lat'}" : "";
450 my $Flon = defined($Round{'lon'}) ? ".$Round{'lon'}" : "";
451 my $tmp_lon = sprintf(
452 "%${Flon}f",
453 $lon_deg +
454 $lon_degmin/60 +
455 $lon_mindec/60000);
456 my $tmp_lat = sprintf("%${Flat}f",
457 $lat_deg +
458 $lat_degmin/60 +
459 $lat_mindec/60000);
460 ($NS eq "S") && ($tmp_lat = 0-$tmp_lat);
461 ($EW eq "W") && ($tmp_lon = 0-$tmp_lon);
462 $Dat{'lat'} = $tmp_lat;
463 $Dat{'lon'} = $tmp_lon;
464 print_entry(%Dat);
465 # }}}
466 } elsif (/^(@)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(_{42})/) {
467 # @020721221336__________________________________________ {{{
468 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
469 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}, $Dat{'rest'}) =
470 ($2+2000, $3, $4,
471 $5, $6, $7, $8);
472 $Dat{'error'} = "nosignal";
473 print_entry(%Dat);
474 # }}}
475 } elsif (/^xmaplog /) {
476 # NOP
477 } elsif (/^$/) {
478 ($Opt{'output-format'} eq "csv")
479 && ($Opt{'save-to-file'} eq "\n")
480 && print("\n");
481 } elsif (/^Pause: /) {
482 # NOP, is here to cope with old files I’ve lying around.
483 } elsif ($Dat{'error'} eq "desc") {
484 my $Comment = $_;
485 if (defined($Comment)) {
486 $Comment =~ s/^\s*(.*?)\s*$/$1/;
487 if ($Opt{'output-format'} eq "gpsml") {
488 $Dat{'desc'} = txt_to_xml($Comment);
489 $Dat{'what'} = "desc";
490 print_entry(%Dat);
493 } else {
494 $Opt{'verbose'} && warn("Line $.: Unknown: \"$_\"\n");
497 # }}}
498 } else {
499 warn("$progname: $curr_file: Cannot open file for read: $!\n");
501 # }}}
504 print_footer(*STDOUT);
506 exit(0);
508 sub read_xmlfile {
509 # {{{
510 my $Txt = join("", @_);
511 $Txt =~ s/<!--(.*?)-->//gs;
512 $Txt =~ s#(<gpx\b.*?>.*?</gpx>)#print_gpx($1)#gse;
513 # }}}
516 sub print_gpx {
517 # {{{
518 my $Orig = shift;
519 my $Str = $Orig;
520 # D("print_xml_gps(\"$Orig\")\n");
521 $Str =~ s/<!--(.*?)-->//gs;
522 my $fromdate_str = "";
523 if ($Opt{'from-date'}) {
524 $fromdate_str = "date >= '$Opt{'from-date'}' AND ";
526 if ($Opt{'output-format'} =~ /^(pgwtab|pgwupd)$/) {
527 # {{{
528 $Str =~
530 <wpt\b(.*?)>(.*?)</wpt>
533 my $attr_wpt = $1;
534 my $el_wpt = $2;
535 my ($Lat, $Lon, $Name, $Ele, $Type, $Time, $Cmt, $Desc, $Src, $Sym) =
536 ('\N', '\N', '\N', '\N', '\N', '\N', '\N', '\N', '\N', '\N');
538 $attr_wpt =~ /.*lat="($DIGIT+?)"/s &&
539 ($Lat = postgresql_copy_safe($1));
540 $attr_wpt =~ /.*lon="($DIGIT+?)"/s &&
541 ($Lon = postgresql_copy_safe($1));
542 $el_wpt =~ /.*<name\b(.*?)>(.*?)<\/name>/s &&
543 ($Name = postgresql_copy_safe(xml_to_txt($2)));
544 $el_wpt =~ /.*<ele\b(.*?)>(.*?)<\/ele>/s &&
545 ($Ele = postgresql_copy_safe(xml_to_txt($2)));
546 $el_wpt =~ /.*<type\b(.*?)>(.*?)<\/type>/s &&
547 ($Type = postgresql_copy_safe(xml_to_txt($2)));
548 $el_wpt =~ /.*<time\b(.*?)>(.*?)<\/time>/s &&
549 ($Time = postgresql_copy_safe(xml_to_txt($2)));
550 $el_wpt =~ /.*<cmt\b(.*?)>(.*?)<\/cmt>/s &&
551 ($Cmt = postgresql_copy_safe(xml_to_txt($2)));
552 $el_wpt =~ /.*<desc\b(.*?)>(.*?)<\/desc>/s &&
553 ($Desc = postgresql_copy_safe(xml_to_txt($2)));
554 $el_wpt =~ /.*<src\b(.*?)>(.*?)<\/src>/s &&
555 ($Src = postgresql_copy_safe(xml_to_txt($2)));
556 $el_wpt =~ /.*<sym\b(.*?)>(.*?)<\/sym>/s &&
557 ($Sym = postgresql_copy_safe(xml_to_txt($2)));
559 if (length($Opt{'round'})) {
560 if (defined($Round{'lat'}) && length($Lat)) {
561 ($Lat = 1.0 * sprintf("%.$Round{'lat'}f", $Lat));
563 if (defined($Round{'lon'}) && length($Lon)) {
564 ($Lon = 1.0 * sprintf("%.$Round{'lon'}f", $Lon));
566 if (defined($Round{'ele'}) && $Ele ne '\N') {
567 ($Ele = 1.0 * sprintf("%.$Round{'ele'}f", $Ele));
571 if ($Opt{'output-format'} eq "pgwtab") {
572 print(
573 join("\t",
574 "($Lat,$Lon)",
575 $Name,
576 $Ele,
577 $Type,
578 $Time,
579 $Cmt,
580 $Desc,
581 $Src,
582 $Sym
583 ) . "\n"
585 } elsif ($Opt{'output-format'} eq "pgwupd") {
586 $Name =~ s/'/''/gs;
587 print(join("\n",
588 "BEGIN;",
589 "$Spc${Spc}UPDATE logg SET name = clname(coor) " .
590 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
591 "$Spc${Spc}UPDATE logg SET dist = cldist(coor) " .
592 "WHERE $fromdate_str(point($Lat,$Lon) <-> coor) < 0.05;",
593 "COMMIT;"
594 ) . "\n");
597 }gsex;
598 # }}}
599 } else {
600 # {{{
601 $Str =~
603 <trk\b(.*?)>(.*?)</trk>
606 my $el_trk = $2;
607 $el_trk =~
609 <name\b(.*?)>(.*?)</name>
611 my %tmp_dat = ();
612 $tmp_dat{'title'} = $2;
613 $tmp_dat{'what'} = "title";
614 $tmp_dat{'error'} = "";
615 print_entry(%tmp_dat);
617 }sex;
618 $el_trk =~
620 <trkseg\b(.*?)>(.*?)</trkseg>
623 my $el_trkseg = $2;
624 $el_trkseg =~
626 <trkpt\b(.*?)>(.*?)</trkpt>
629 my ($attr_trkpt, $el_trkpt) =
630 ( $1, $2);
631 %Dat = (
632 'year' => '', 'month' => '', 'day' => '',
633 'hour' => '', 'min' => '', 'sec' => '',
634 'epoch' => '',
635 'date-format' => '',
636 'lat' => '', 'lon' => '',
637 'ele' => '',
638 'desc' => '',
639 'error' => "",
640 'what' => 'tp',
642 ($attr_trkpt =~ /\blon="(.*?)"/) && ($Dat{'lon'} = $1);
643 ($attr_trkpt =~ /\blat="(.*?)"/) && ($Dat{'lat'} = $1);
644 ($el_trkpt =~ m#<ele\b.*?>(.*?)</ele>#) && ($Dat{'ele'} = $1);
645 if (
646 $el_trkpt =~
648 <time>(\d\d\d\d)-?(\d\d)-?(\d\d)T
649 (\d\d):?(\d\d):?([\d\.]+)Z</time>
652 ($Dat{'year'}, $Dat{'month'}, $Dat{'day'},
653 $Dat{'hour'}, $Dat{'min'}, $Dat{'sec'}) =
654 ($1, $2, $3, $4, $5, $6);
656 print_entry(%Dat);
658 }gsex;
659 $found_move = 1;
660 }gsex;
661 $found_move = 1;
662 }gsex;
663 # }}}
665 # }}}
668 sub print_header {
669 # {{{
670 local *OutFP = shift;
671 if ($Opt{'output-format'} eq "gpsml") {
672 print(OutFP join("",
673 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
674 "<gpsml>\n",
675 "<track>\n",
677 } elsif ($Opt{'output-format'} eq "gpstrans") {
678 print(OutFP "Format: DMS UTC Offset: 0.00 hrs " .
679 "Datum[100]: WGS 84\n");
680 } elsif ($Opt{'output-format'} eq "gpx") {
681 print(OutFP join("",
682 qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n},
683 qq{<gpx\n},
684 qq{$Spc${Spc}version="1.1"\n},
685 qq{$Spc${Spc}creator="gpst - http://svn.sunbase.org/repos/utils/trunk/src/gpstools/"\n},
686 qq{$Spc${Spc}xmlns="http://www.topografix.com/GPX/1/1"\n},
687 qq{$Spc${Spc}xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n},
688 qq{$Spc${Spc}xsi:schemaLocation="http://www.topografix.com/GPX/1/1 },
689 qq{http://www.topografix.com/GPX/1/1/gpx.xsd"\n},
690 qq{>\n},
691 qq{$Spc$Spc<trk>\n},
692 qq{$Spc$Spc$Spc$Spc<trkseg>\n},
694 } elsif ($Opt{'output-format'} eq "ps") {
695 print(OutFP ps_header(532, 6034, 533, 6040));
696 print(OutFP "*u\n");
697 } elsif ($Opt{'output-format'} eq "svg") {
698 print(OutFP join("",
699 "<?xml version=\"1.0\" standalone=\"no\"?>\n",
700 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
701 "$Spc$Spc\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
702 "<svg height=\"1000\" width=\"1000\" viewBox=\"23 70 2 2\"\n",
703 "$Spc${Spc}xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
704 "$Spc$Spc<title></title>\n",
705 "$Spc$Spc<desc></desc>\n",
708 # }}}
711 sub print_footer {
712 # Print footer {{{
713 local *OutFP = shift;
714 if ($Opt{'output-format'} eq "gpsml") {
715 print(OutFP join("",
716 "</track>\n",
717 "</gpsml>\n",
719 } elsif ($Opt{'output-format'} eq "gpx") {
720 print(OutFP join("",
721 "$Spc$Spc$Spc$Spc</trkseg>\n",
722 "$Spc$Spc</trk>\n",
723 "</gpx>\n",
725 } elsif ($Opt{'output-format'} eq "poscount") {
726 while (my ($l_name, $l_val) = each %Poscount) {
727 $l_name =~ /^(.+?),(.+?)$/
728 && print(OutFP "$1\t$2\t$l_val\n");
730 } elsif ($Opt{'output-format'} eq "ps") {
731 print(OutFP join("",
732 "*U\n",
733 "%%Trailer\n",
734 "%%EOF\n",
736 } elsif ($Opt{'output-format'} eq "svg") {
737 print(OutFP "\"/>\n</svg>\n");
739 # }}}
742 sub print_entry {
743 # Print a GPS entry with time, latitude, longitude and elevation in
744 # various formats
745 # {{{
746 my %Dat = @_;
747 defined($Dat{'desc'}) || ($Dat{'desc'} = "");
748 defined($Dat{'ele'}) || ($Dat{'ele'} = "");
749 defined($Dat{'lat'}) || ($Dat{'lat'} = "");
750 defined($Dat{'lon'}) || ($Dat{'lon'} = "");
751 defined($Dat{'year'}) || ($Dat{'year'} = "");
752 my $print_time = length($Dat{'year'}) ? 1 : 0;
753 my $print_pos;
754 if (!$Req{'position'} && $Opt{'output-format'} eq "gpsml") {
755 $print_pos = (length($Dat{'lat'}) || length($Dat{'lon'})) ? 1 : 0;
756 } else {
757 $print_pos = (length($Dat{'lat'}) && length($Dat{'lon'})) ? 1 : 0;
759 if (!$print_pos) {
760 $Dat{'lat'} = $Dat{'lon'} = "";
762 my $print_ele = length($Dat{'ele'}) ? 1 : 0;
763 my $print_desc = length($Dat{'desc'}) ? 1 : 0;
764 my $Line = "";
765 # D("print_entry(\"" . join("\", \"", @_) . "\");");
766 my $ep_time;
768 if ($Opt{'near'} && $print_pos) {
769 $Line .= sprintf("%s ",
770 list_nearest_waypoints($Dat{'lat'}, $Dat{'lon'}));
773 if (length($Opt{'round'})) {
774 for my $Tmp (qw{ lat lon ele }) {
775 if (defined($Round{$Tmp}) && length($Dat{$Tmp})) {
776 # D("Tmp = '$Tmp'");
777 ($Dat{$Tmp} = 1.0 * sprintf("%.$Round{$Tmp}f", $Dat{$Tmp}));
782 if ($Opt{'output-format'} eq "poscount") {
783 if (!length($Dat{'error'})) {
784 my $Name = "$Dat{'lon'},$Dat{'lat'}";
785 defined($Poscount{$Name}) || ($Poscount{$Name} = 0);
786 $Poscount{$Name}++;
788 return;
791 if ($print_time) {
792 $ep_time = timegm_nocheck(
793 $Dat{'sec'}, $Dat{'min'}, $Dat{'hour'},
794 $Dat{'day'}, $Dat{'month'} - 1, $Dat{'year'}
796 if ($Opt{'time-shift'}) {
797 # D("ep_time før: '$ep_time'");
798 $ep_time += $Opt{'time-shift'};
799 # D("ep_time etter: '$ep_time'");
800 ($Dat{'sec'}, $Dat{'min'},$Dat{'hour'}, $Dat{'day'},
801 $Dat{'month'}, $Dat{'year'}) = gmtime($ep_time);
802 $Dat{'year'} += 1900;
803 $Dat{'month'}++;
805 $Dat{'epoch'} = $ep_time;
806 $Dat{'year'} = sprintf("%04u", $Dat{'year'});
807 $Dat{'month'} = sprintf("%02u", $Dat{'month'});
808 $Dat{'day'} = sprintf("%02u", $Dat{'day'});
809 $Dat{'hour'} = sprintf("%02u", $Dat{'hour'});
810 $Dat{'min'} = sprintf("%02u", $Dat{'min'});
811 $Dat{'sec'} = sprintf("%02u", $Dat{'sec'});
812 if ($Opt{'chronology'}) {
813 if ($last_time > $ep_time && !length($Dat{'error'})) {
814 warn(sprintf(
815 "%s: $curr_file: \"%sZ\": Next date is %s in the past (%sZ)\n",
816 $progname, sec_to_string($last_time, "T"),
817 sec_to_readable($last_time-$ep_time),
818 sec_to_string($ep_time, "T")
820 # FIXME: Make --fix work with gpx.
821 if ($Opt{'fix'} && ($Opt{'output-format'} !~ /^gpx$/)) {
822 $Dat{'error'} = "chrono";
824 } elsif ($last_time == $ep_time && !length($Dat{'error'})) {
825 warn(sprintf(
826 "%s: $curr_file: \"%sZ\": Duplicated time\n",
827 $progname, sec_to_string($last_time, "T")
829 # FIXME: Make --fix work with gpx.
830 if ($Opt{'fix'} && ($Opt{'output-format'} !~ /^gpx$/)) {
831 $Dat{'error'} = "duptime";
835 } else {
836 $ep_time = 0;
837 $Dat{'year'} = 0;
838 $Dat{'month'} = 0;
839 $Dat{'day'} = 0;
840 $Dat{'hour'} = 0;
841 $Dat{'min'} = 0;
842 $Dat{'sec'} = 0;
845 if ($Opt{'save-to-file'} ne "\n") {
846 # {{{
847 $print_time || return;
848 my $base_name = "$Dat{'year'}$Dat{'month'}$Dat{'day'}T" .
849 "$Dat{'hour'}$Dat{'min'}$Dat{'sec'}Z" .
850 "$Opt{'save-to-file'}";
851 my $file_name = $base_name;
852 if (-e $file_name) {
853 for (my $a = 1; (-e $file_name) && ($a < 1000); $a++) {
854 $file_name = "$base_name.dup_$a";
856 if (-e $file_name) {
857 die("$progname: $base_name: File already exists, and ran " .
858 "out of attempts to create unique file name\n");
860 if ($Opt{'verbose'}) {
861 warn("$progname: $base_name: File already exists, using " .
862 "unique name \"$file_name\" instead\n");
865 if (open(ToFP, ">", $file_name)) {
866 print_header(*ToFP);
867 print(ToFP (
868 $from_stdin
869 ? @first_lines
870 : ()),
871 (length($xml_data)
872 ? $xml_data
873 : <>)
874 ) || die("$progname: $file_name: Cannot write to file: $!\n");
875 print_footer(*ToFP);
876 close(ToFP);
877 if ($Opt{'output-format'} eq "gpsml") {
878 printf("<include>%s</include>\n",
879 txt_to_xml($file_name));
880 } elsif ($Opt{'output-format'} eq "gpx") {
881 printf("<!-- Saved unconverted data to \"%s\" -->\n",
882 txt_to_xml($file_name));
883 } else {
884 print("$progname: Saved unconverted data to \"$file_name\"\n");
886 exit 0;
887 } else {
888 die("$progname: $file_name: Cannot create file: $!\n");
890 # }}}
893 my $pause_len = 0;
894 my $do_print = 1;
896 if ($Dat{'what'} eq "tp") {
897 # {{{
898 if ($Opt{'require'}) {
899 $Req{'time'} && !$print_time && return;
900 $Req{'position'} && !$print_pos && return;
901 $Req{'ele'} && !$print_ele && return;
904 if ($Opt{'inside'} || $Opt{'outside'}) {
905 if (
906 ($Dat{'lat'} < $lat1) ||
907 ($Dat{'lat'} > $lat2) ||
908 ($Dat{'lon'} < $lon1) ||
909 ($Dat{'lon'} > $lon2)
911 $Opt{'inside'} && return;
912 } else {
913 $Opt{'outside'} && return;
917 if ($Opt{'output-format'} eq "ps") {
918 $Dat{'lon'} *= 100;
919 $Dat{'lat'} *= 100;
922 if (
923 $Opt{'skip-dups'}
924 && ($Dat{'lon'} eq $last_lon)
925 && ($Dat{'lat'} eq $last_lat)
927 if ($Opt{'output-format'} eq 'gpsml') {
928 $Dat{'error'} = "dup";
929 } else {
930 $do_print = 0;
932 } else {
933 $do_print = 1;
936 if (
937 $Opt{'create-breaks'}
938 && $ep_time-$last_time > $PAUSE_LIMIT
939 && $last_time
941 $pause_len = $ep_time-$last_time;
942 # D("pause_len set to '$pause_len'");
945 if ($pause_len) {
946 if ($Opt{'output-format'} eq "gpsml") {
947 $Line .= sprintf("<pause>%s</pause>\n",
948 sec_to_readable($ep_time-$last_time));
949 } elsif ($Opt{'output-format'} eq "clean") {
950 $pause_len && ($Line .= "\n");
951 } elsif ($Opt{'output-format'} eq "csv") {
952 $Line .= sprintf("# Pause: %s\n# move\n",
953 sec_to_readable($ep_time-$last_time));
954 } elsif ($Opt{'output-format'} eq "xgraph") {
955 $pause_len && ($Line .= "move ");
958 # }}}
961 if ($do_print) {
962 # Valid data was found, send to stdout {{{
963 unless ($first_time) {
964 $first_time = $ep_time;
966 if ($Opt{'double-y-scale'} && length($Dat{'lat'})) {
967 $Dat{'lat'} *= 2;
969 if ($Opt{'output-format'} eq "gpsml") {
970 if ($Dat{'what'} eq "tp") {
971 $Dat{'format'} = "gpsml";
972 $Line .= trackpoint(%Dat);
973 } elsif ($Dat{'what'} =~ /^(pause|desc|title)$/) {
974 $Line .= sprintf("<%s>%s</%s>\n",
976 $Dat{$1},
977 $1);
979 } elsif ($Opt{'output-format'} eq "pgtab") {
980 if ($Dat{'what'} eq "tp" && !length($Dat{'error'})) {
981 $Dat{'format'} = "pgtab";
982 $Line .= trackpoint(%Dat);
984 } elsif ($Opt{'output-format'} eq "xgraph") {
985 if ($print_pos && !length($Dat{'error'})) {
986 $Dat{'format'} = "xgraph";
987 $Line .= trackpoint(%Dat);
989 } elsif($Opt{'output-format'} eq "gpstrans") {
990 if ($print_pos && !length($Dat{'error'})) {
991 $Dat{'format'} = "gpstrans";
992 $Line .= trackpoint(%Dat);
994 } elsif($Opt{'output-format'} eq "gpx") {
995 if ($Dat{'what'} eq "tp") {
996 $Dat{'format'} = "gpx";
997 $Line .= trackpoint(%Dat);
999 } elsif ($Opt{'output-format'} eq "clean") {
1000 if ($Dat{'what'} eq "tp" && !length($Dat{'error'})) {
1001 $Dat{'format'} = "clean";
1002 $Line .= trackpoint(%Dat);
1004 } elsif ($Opt{'output-format'} eq "ps") {
1005 $Line .= (
1006 $pause_len
1007 ? "f\n$Dat{'lon'} $Dat{'lat'} m\n"
1008 : "$Dat{'lon'} $Dat{'lat'} l\n"
1010 } elsif ($Opt{'output-format'} eq "svg") {
1011 $Line .= (
1012 ($last_lon == 1000) || $pause_len
1013 ? join("",
1014 "$svg_start_thing<path\n",
1015 " stroke=\"blue\"\n",
1016 " stroke-width=\"0.001\"\n",
1017 " fill=\"none\"\n",
1018 " d=\"\n",
1019 "M $Dat{'lon'} $Dat{'lat'}\n")
1020 : "L $Dat{'lon'} $Dat{'lat'}\n"
1022 } elsif ($Opt{'output-format'} eq "ygraph") {
1023 if (!length($Dat{'error'})) {
1024 my $Time = $print_time ? ($ep_time - $first_time) * 1 : 0;
1025 $Line .= "\"Time = $Time.0\n$Dat{'lon'} $Dat{'lat'}\n\n";
1027 } elsif ($Opt{'output-format'} eq "csv") {
1028 # {{{
1029 if (!length($Dat{'error'})) {
1030 $Dat{'format'} = "csv";
1031 $Line .= join("\t",
1032 $print_time
1033 ? $Opt{'epoch'}
1034 ? $ep_time
1035 : $Opt{'short-date'}
1036 ? "$Dat{'year'}$Dat{'month'}$Dat{'day'}T" .
1037 "$Dat{'hour'}$Dat{'min'}$Dat{'sec'}Z"
1038 : "$Dat{'year'}-$Dat{'month'}-$Dat{'day'}T" .
1039 "$Dat{'hour'}:$Dat{'min'}:$Dat{'sec'}Z"
1040 : "",
1041 $Dat{'lon'},
1042 $Dat{'lat'},
1043 $print_ele ? $Dat{'ele'} : "", # Elevation
1044 "\n"
1047 # }}}
1048 } elsif ($Opt{'output-format'} eq "pgwtab") {
1049 # FIXME: NOP at the moment.
1050 } else {
1051 die("$progname: \"$Opt{'output-format'}\": " .
1052 "Unknown output format\n");
1054 # }}}
1057 if (!$last_time && $Opt{'output-format'} eq "ps") {
1058 $Line .= "$Dat{'lon'} $Dat{'lat'} m\n";
1061 if ($do_print) {
1062 if ($found_move) {
1063 if ($Opt{'output-format'} eq "gpsml") {
1064 $Line = "<break/>\n$Line";
1066 (!$pause_len && ($Opt{'output-format'} eq "xgraph"))
1067 && ($Line .= "move $Line");
1068 ($Opt{'output-format'} eq "clean") && ($Line .= "\n");
1069 if ($Opt{'output-format'} eq "gpx") {
1070 $Line .= "$Spc$Spc$Spc$Spc</trkseg>\n" .
1071 "$Spc$Spc$Spc$Spc<trkseg>\n";
1073 $found_move = 0;
1075 print($Line);
1077 $print_time && ($last_time = $ep_time);
1078 if ($print_pos) {
1079 $last_lon = $Dat{'lon'};
1080 $last_lat = $Dat{'lat'};
1082 $last_line = $data_line;
1083 $svg_start_thing = "\"/>\n";
1084 # }}}
1087 sub ps_header {
1088 # Send a Postscript header to stdout {{{
1089 my ($bl_lon, $bl_lat, $br_lon, $br_lat) = @_;
1090 my $Date = sec_to_string(time);
1091 return(join("",
1092 "%!PS-Adobe-3.0 EPSF-3.0\n",
1093 "%%Creator: $rcs_id\n",
1094 "%%Title:\n",
1095 "%%CreationDate: $Date\n",
1096 "%%BoundingBox: $bl_lon $bl_lat $br_lon $br_lat\n",
1097 "%%DocumentData: Clean7Bit\n",
1098 "%%EndComments\n",
1099 "%%BeginProlog\n",
1100 "/bd { bind def } bind def\n",
1101 "/incompound false def\n",
1102 "/m { moveto } bd\n",
1103 "/l { lineto } bd\n",
1104 "/c { curveto } bd\n",
1105 "/F { incompound not {fill} if } bd\n",
1106 "/f { closepath F } bd\n",
1107 "/S { stroke } bd\n",
1108 "/*u { /incompound true def } bd\n",
1109 "/*U { /incompound false def f} bd\n",
1110 "/k { setcmykcolor } bd\n",
1111 "/K { k } bd\n",
1112 "%%EndProlog\n",
1113 "%%BeginSetup\n",
1114 "%%EndSetup\n",
1116 # }}}
1119 sub print_version {
1120 # Print program version {{{
1121 for (@main::version_array) {
1122 print("$_\n");
1124 # }}}
1125 } # print_version()
1127 sub usage {
1128 # Send the help message to stdout {{{
1129 my $Retval = shift;
1131 if ($Opt{'verbose'}) {
1132 print("\n");
1133 print_version();
1135 print(<<END);
1137 Converts between various GPS formats.
1139 Usage: $progname [options] [file [files [...]]]
1140 $progname -S [file [files [...]]]
1141 $progname -u [file [files [...]]]
1143 Options:
1145 --chronology
1146 Check for broken chronology, warn about entries with an old
1147 timestamp.
1148 -d, --skip-dups
1149 Skip duplicated coordinates.
1150 -e, --epoch
1151 Use seconds since 1970-01-01 00:00:00 GMT as date format.
1152 --fix
1153 Comment out entries which is obviously wrong. Use together with
1154 --chronology to fix those kind of errors. Does not work with GPX
1155 output yet.
1156 --from-date x
1157 Used by the pgwupd format. Specifies from which date waypoints
1158 should be updated. No checks for valid date format here, let
1159 PostgreSQL take care of that. All variants it understands can be
1160 used here.
1161 -h, --help
1162 Show this help.
1163 --inside
1164 Print only trackpoints inside a rectangle specified by --pos1 and
1165 --pos2.
1166 -n, --undefined x
1167 Use x as undefined value. Default: "$Udef".
1168 --near
1169 Add names of the three closest waypoints to the trackpoint.
1170 Unfinished and experimental, needs gpsbabel, which is called from
1171 the program as "$Cmd{'gpsbabel'}".
1172 -o, --output-format x
1173 Use output format x:
1174 clean
1176 gpsml (Default)
1177 gpstrans
1178 gpx (Not complete)
1179 pgtab
1180 pgwtab
1181 pgwupd
1182 poscount
1183 ps (Unfinished)
1184 svg (Unfinished)
1185 xgraph
1186 ygraph
1187 --outside
1188 Print only trackpoints outside a rectangle specified by --pos1 and
1189 --pos2.
1190 --pos1 x
1191 --pos2 x
1192 Specifies one corner where x is in "lat,lon" format (decimal
1193 degrees, negative for west or south) of area rectangle used by the
1194 --inside and --outside options.
1195 -r, --require x
1196 Specify requirements for trackpoints to be written. x is a string
1197 with the following flags:
1199 Print only waypoints which have an elevation.
1201 Print only waypoints which have a position.
1203 Print only waypoints which have a timestamp.
1204 -R, --round x=y[,x2=y2[...]]
1205 Round trackpoint element x to y decimals. Example:
1206 --round lat=4,lon=5,ele=1
1207 -s, --short-date
1208 Use short date format.
1209 -S, --save-to-file x
1210 Save the unconverted data to a file with a filename starting with
1211 the timestamp of the first trackpoint. The parameter string x is
1212 added at the end of the filename. For the time being this option
1213 will ignore all other options. Note: If several files are specified
1214 on the command line, all data will be saved into only one file. This
1215 behaviour may change in the future.
1216 -t, --create-breaks
1217 Create breaks in track between points with a difference more than
1218 $PAUSE_LIMIT seconds.
1219 -T x, --time-shift x
1220 Move time of trackpoint x seconds forwards or backwards. x can be a
1221 positive or negative integer.
1222 -v, --verbose
1223 Increase level of verbosity. Can be repeated.
1224 --version
1225 Print version information.
1226 -w, --strip-whitespace
1227 Strip all unnecessary whitespace.
1228 -y, --double-y-scale
1229 Double Y scale (latitude) to get it right in gnuplot.
1230 --debug
1231 Print debugging messages.
1234 exit($Retval);
1235 # }}}
1236 } # usage()
1238 sub msg {
1239 # Print a status message to stderr based on verbosity level {{{
1240 my ($verbose_level, $Txt) = @_;
1242 if ($Opt{'verbose'} >= $verbose_level) {
1243 print(STDERR "$progname: $Txt\n");
1245 # }}}
1246 } # msg()
1248 __END__
1250 # Law talk {{{
1251 # Copyleft © Øyvind A. Holm <sunny@sunbase.org>
1253 # This program is free software; you can redistribute it and/or modify
1254 # it under the terms of the GNU General Public License as published by
1255 # the Free Software Foundation; either version 2 of the License, or (at
1256 # your option) any later version.
1258 # This program is distributed in the hope that it will be useful, but
1259 # WITHOUT ANY WARRANTY; without even the implied warranty of
1260 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1261 # See the GNU General Public License for more details.
1263 # You should have received a copy of the GNU General Public License
1264 # along with this program; if not, write to the Free Software
1265 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1266 # USA
1267 # }}}
1269 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :
1270 # End of file $Id$