3 # chem - a groff preprocessor for producing chemical structure diagrams
5 # Source file position: <groff-source>/contrib/chem/chem.pl
6 # Installed position: <prefix>/bin/chem
8 # Copyright (C) 2006, 2009 Free Software Foundation, Inc.
9 # Written by Bernd Warken.
11 # This file is part of `chem', which is part of `groff'.
13 # `groff' is free software; you can redistribute it and/or modify it
14 # under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
18 # `groff' is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 ########################################################################
28 ########################################################################
30 my $Program_Version = '0.3.1';
31 my $Last_Update = '03 Jan 2009';
33 # this setting of the groff version is only used before make is run,
34 # otherwise @VERSION@ will set it.
35 my $Groff_Version_Preset='1.20preset';
37 # test on Perl version
41 ########################################################################
43 ########################################################################
52 # $Bin is the directory where this script is located
62 my $before_make; # script before run of `make'
65 $before_make = 1 if '@VERSION@' eq "${at}VERSION${at}";
71 my $chem_dir = $FindBin::Bin;
72 $at_at{'BINDIR'} = $chem_dir;
74 $File_chem_pic = File::Spec->catfile($chem_dir, 'chem.pic');
75 $File_pic_tmac = File::Spec->catfile($chem_dir, '..', 'pic.tmac');
79 $Groff_Version = '@VERSION@';
80 $at_at{'BINDIR'} = '@BINDIR@';
82 $at_at{'PICDIR'} = '@PICDIR@';
83 $at_at{'TMACDIR'} = '@MACRODIR@';
85 File::Spec->catfile($at_at{'PICDIR'}, 'chem.pic');
86 $File_pic_tmac = File::Spec->catfile($at_at{'TMACDIR'}, 'pic.tmac');
87 $Chem_Name = $at_at{'G'} . 'chem';
93 ########################################################################
94 # check the parameters
95 ########################################################################
98 # process any FOO=bar switches
99 # eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_0-9]+=)(.*)/ && shift;
106 # ignore FOO=bar switches
107 push @filespec, $_ if -f;
112 push @filespec, $_ if -s $_;
114 warn "chem: argument $_ is not an existing file.\n";
127 if (/^-h$/ or '--help' =~ /^$_/) {
131 if (/^-v$/ or '--version' =~ /^$_/) {
136 push @filespec, $_ if -s $_;
140 warn "chem: wrong option ${_}.\n";
142 warn "chem: argument $_ is not an existing file.\n";
152 } else { # @ARGV is empty
153 @ARGV = ('-') unless @ARGV;
157 ########################################################################
159 ########################################################################
161 my %Dc = ( 'up' => 0, 'right' => 90, 'down' => 180, 'left' => 270,
162 'ne' => 45, 'se' => 135, 'sw' => 225, 'nw' => 315,
163 0 => 'n', 90 => 'e', 180 => 's', 270 => 'w',
164 30 => 'ne', 45 => 'ne', 60 => 'ne',
165 120 => 'se', 135 => 'se', 150 => 'se',
166 210 => 'sw', 225 => 'sw', 240 => 'sw',
167 300 => 'nw', 315 => 'nw', 330 => 'nw',
184 'OTHER' => 'O' # manifests
207 my $former_line = '';
217 # for centralizing the pic code
218 open TMAC, "<$File_pic_tmac" and print <TMAC>;
222 $count_minus++ if /^-$/;
225 foreach my $arg (@ARGV) {
232 $File_Name = 'standard input';
234 &main_line($_) foreach @stdin;
237 if ($count_minus <= 1) {
250 } else { # $arg is not -
253 &main_line($_) while <FILE>;
268 # $Last_Type = $Types{'OTHER'};
274 $line = $former_line . $line if $former_line;
275 if ($line =~ /^(.*)\\$/) {
290 $s =~ s/\s+#.*$// if $is_pic;
293 $line =~ s/^\s*|\s*$//g;
296 $s =~ /^([^"]*)\s("[^"]*"?\S*)(.*)$/;
301 $s1 =~ s/^\s*|\s*$//g;
302 push @Words, split(/\s+/, $s1) if $s1;
306 $s =~ s/^\s*|\s*$//g;
307 push @Words, split(/\s+/, $s) if $s;
312 # @Words = split(/\s+/, $s);
313 return 1 unless @Words;
314 # foreach my $i (0..$#Words) {
315 # if ($Words[$i] =~ /^\s*#/) {
320 # return 1 unless @Words;
323 if ($line =~ /^([\.']\s*PS\s*)|([\.']\s*PS\s.+)$/) {
332 if ( $line =~ /^([\.']\s*PE\s*)|([\.']\s*PE\s.+)$/ ) {
341 if ($line =~ /^[\.']\s*cstart\s*$/) {
344 &error("additional `.cstart'; chem is already active.");
351 $is_chem = '.cstart';
356 if ($line =~ /^\s*begin\s+chem\s*$/) {
360 &error("additional `begin chem'; chem is already active.");
363 $is_chem = 'begin chem';
370 if ($line =~ /^[\.']\s*cend\s*/) {
373 &error("you end chem with `.cend', but started it with `begin chem'.")
374 if $is_chem eq 'begin chem';
375 if ($is_pic eq 'by chem') {
385 if ($line =~ /^\s*end\s*$/) {
388 &error("you end chem with `end', but started it with `.cstart'.")
389 if $is_chem eq '.cstart';
390 if ($is_pic eq 'by chem') {
406 if ($line =~ /^[.']/) {
412 if ($Words[0] eq 'pic') {
414 return 1 if $#Words == 0;
416 $s =~ /^\s*pic\s*(.*)$/;
419 $Last_Type = $Types{'OTHER'};
420 $Define{ $Words[2] } = 1 if $#Words >= 2 && $Words[1] eq 'define';
424 if ($Words[0] eq 'textht') {
426 &error("`textht' needs a single argument.");
429 &error("only the last argument is taken for `textht', " .
430 "all others are ignored.")
431 unless $#Words <= 1 or ($#Words == 2 && $Words[1] =~ /^=/);
432 $Params{'textht'} = $Words[$#Words];
436 if ($Words[0] eq 'cwid') { # character width
438 &error("`cwid' needs a single argument.");
441 &error("only the last argument is taken for `cwid', " .
442 "all others are ignored.")
443 unless $#Words <= 1 or ($#Words == 2 && $Words[1] =~ /^=/);
444 $Params{'cwid'} = $Words[$#Words];
447 if ($Words[0] eq 'db') { # bond length
449 &error("`db' needs a single argument.");
452 &error("only the last argument is taken for `db', " .
453 "all others are ignored.")
454 unless $#Words <= 1 or ($#Words == 2 && $Words[1] =~ /^=/);
455 $Params{'db'} = $Words[$#Words];
458 if ($Words[0] eq 'size') { # size for all parts of the whole diagram
461 &error("`size' needs a single argument.");
464 &error("only the last argument is taken for `size', " .
465 "all others are ignored.")
466 unless $#Words <= 1 or ($#Words == 2 && $Words[1] =~ /^=/);
467 if ($Words[$#Words] <= 4) {
468 $size = $Words[$#Words];
470 $size = $Words[$#Words] / 10;
477 print "\n#", $Line, "\n"; # debugging, etc.
479 # $Last_Type = $Types{'OTHER'};
482 if ($Words[0] =~ /^[A-Z].*:$/) {
483 # label; falls thru after shifting left
486 $Last_Name =~ s/:$//;
491 $line =~ s/^\s*$w\s*//;
498 if ($Words[0] eq 'define') {
500 $Define{ $Words[1] } = 1 if $#Words >= 1;
501 $Last_Type = $Types{'OTHER'};
504 if ($Words[0] =~ /^[\[\]{}]/) {
506 $Last_Type = $Types{'OTHER'};
510 if ($Words[0] =~ /^"/) {
511 print 'Last: ', $line, "\n";
512 $Last_Type = $Types{'OTHER'};
516 if ($Words[0] =~ /bond/) {
522 if ($Words[0] =~ /^(double|triple|front|back)$/ &&
523 $Words[1] eq 'bond') {
524 my $w = shift @Words;
525 $Words[0] = $w . $Words[0];
529 if ($Words[0] eq 'aromatic') {
530 my $temp = $Words[0];
531 $Words[0] = $Words[1] ? $Words[1] : '';
536 if ($Words[0] =~ /ring|benz/) {
540 if ($Words[0] eq 'methyl') {
541 # left here as an example
545 if ($Words[0] =~ /^[A-Z]/) {
549 if ($Words[0] eq 'left') {
551 $left{++$stack} = &fields(1, $#Words);
552 printf (("Last: [\n"));
555 if ($Words[0] eq 'right') {
560 if ($Words[0] eq 'label') { # prints the vertex numbers in a ring
561 if ( exists $Labtype{$Words[1]} and
562 $Labtype{$Words[1]} =~ /^$Types{'RING'}/ ) {
563 my $v = substr($Labtype{$Words[1]}, 1, 1);
564 $Words[1] = '' unless $Words[1];
565 foreach my $i ( 1..$v ) {
566 printf "\"\\s-3%d\\s0\" at 0.%d<%s.C,%s.V%d>\n", $i, $v + 2,
567 $Words[1], $Words[1], $i;
570 &error("$Words[1] is not a ring.");
575 if ( exists $Define{ $Words[0] } ) {
577 $Last_Type = $Types{'OTHER'};
580 return 1 unless $line;
581 # print STDERR "# $Line\n";
582 # &error('This is not a chem command. To include a command for pic, ' .
583 # "add `pic' as the first word to the command.");
585 $Last_Type = $Types{'OTHER'};
591 ########################################################################
593 ########################################################################
599 # convert CH3 to atom(...)
601 my ($i, $n, $nsub, $cloc, $nsubc, @s);
607 $cloc = index($s, 'C');
608 if (! defined($cloc) || $cloc < 0) {
616 $nsubc++ if $i < $cloc;
620 $s =~ s/([0-9]+\.[0-9]+)|([0-9]+)/\\s-3\\d$&\\u\\s+3/g;
621 if ($s =~ /([^0-9]\.)|(\.[^0-9])/) { # centered dot
622 $s =~ s/\./\\v#-.3m#.\\v#.3m#/g;
624 sprintf( "atom(\"%s\", %g, %g, %g, %g, %g, %g)",
625 $s, ($n - $nsub / 2) * $Params{'cwid'}, $Params{'textht'},
626 ($cloc - $nsubc / 2 + 0.5) * $Params{'cwid'}, $Params{'crh'},
627 $Params{'crw'}, $Params{'dav'}
637 my ($i, $moiety, $from, $leng);
639 for ($i = 1; $i <= $#Words; $i++) {
640 if ($Words[$i] eq ';') {
641 &error("a colon `;' must be followed by a space and a single word.")
642 if $i != $#Words - 1;
643 $moiety = $Words[$i + 1] if $#Words > $i;
648 $leng = $Params{'db'}; # bond length
650 for ($Word_Count = 1; $Word_Count <= $#Words; ) {
651 if ($Words[$Word_Count] =~
652 /(\+|-)?\d+|up|down|right|left|ne|se|nw|sw/) {
653 $Dir = &cvtdir($Dir);
654 } elsif ($Words[$Word_Count] =~ /^leng/) {
655 $leng = $Words[$Word_Count + 1] if $#Words > $Word_Count;
657 } elsif ($Words[$Word_Count] eq 'to') {
659 $from = &fields($Word_Count, $#Words);
661 } elsif ($Words[$Word_Count] eq 'from') {
664 } elsif ($Words[$Word_Count] =~ /^#/) {
665 $Word_Count = $#Words + 1;
668 $from = &fields($Word_Count, $#Words);
673 if ($from =~ /( to )|^to/) { # said "from ... to ...", so zap length
675 } elsif (! $from) { # no from given at all
676 $from = 'from Last.' . &leave($Last_Type, $Dir) . ' ' .
677 &fields($Word_Count, $#Words);
679 printf "Last: %s(%g, %g, %s)\n", $type, $leng, $Dir, $from;
680 $Last_Type = $Types{'BOND'};
681 $Labtype{$Last_Name} = $Last_Type if $Last_Name;
695 if ($Words[1] && $Words[1] eq ')') {
700 printf "%s from last [].sw+(%g,0) to last [].sw to last [].nw to last " .
701 "[].nw+(%g,0)\n", $t, $Params{'dbrack'}, $Params{'dbrack'};
702 printf "%s from last [].se-(%g,0) to last [].se to last [].ne to last " .
703 "[].ne-(%g,0)\n", $t, $Params{'dbrack'}, $Params{'dbrack'};
704 if ($Words[2] && $Words[2] eq 'sub') {
705 printf "\" %s\" ljust at last [].se\n", &fields(3, $#Words);
713 # Return the corner name next to the given angle.
717 $Dc{ (45 * int(($d + 22.5) / 45)) % 360 };
724 # Maps "[pointing] somewhere" to degrees.
728 if ($Words[$Word_Count] eq 'pointing') {
731 if ($Words[$Word_Count] =~ /^[+\\-]?\d+/) {
732 return ( $Words[$Word_Count++] % 360 );
733 } elsif ($Words[$Word_Count] =~ /left|right|up|down|ne|nw|se|sw/) {
734 return ( $Dc{$Words[$Word_Count++]} % 360 );
748 # should canonicalize to i,i+1 mod v
749 $d = $Words[$Word_Count];
750 for ($Word_Count++; $Word_Count <= $#Words &&
751 $Words[$Word_Count] =~ /^[1-9]/; $Word_Count++) {
752 $v1 = substr($Words[$Word_Count], 0, 1);
753 $v2 = substr($Words[$Word_Count], 2, 1);
754 if ($v2 == $v1 + 1 || $v1 == $v && $v2 == 1) { # e.g., 2,3 or 5,1
756 } elsif ($v1 == $v2 + 1 || $v2 == $v && $v1 == 1) { # e.g., 3,2 or 1,5
759 &error(sprintf("weird %s bond in\n\t%s", $d, $_));
770 $Word_Count++; # skip "from"
771 $n = $Words[$Word_Count];
772 if (defined $Labtype{$n}) { # "from Thing" => "from Thing.V.s"
773 return 'from ' . $n . '.' . &leave($Labtype{$n}, $Dir);
775 if ($n =~ /^\.[A-Z]/) { # "from .V" => "from Last.V.s"
776 return 'from Last' . $n . '.' . &corner($Dir);
778 if ($n =~ /^[A-Z][^.]*\.[A-Z][^.]*$/) { # "from X.V" => "from X.V.s"
779 return 'from ' . $n . '.' . &corner($Dir);
781 &fields($Word_Count - 1, $#Words);
790 printf STDERR "chem: error in %s on line %d: %s\n",
791 $File_Name, $Line_No, $s;
804 foreach my $i ($n1..$n2) {
805 if ($Words[$i] =~ /^#/) {
808 $s = $s . $Words[$i] . ' ';
819 printf "copy \"%s\"\n", $File_chem_pic;
820 printf "\ttextht = %g; textwid = .1; cwid = %g\n",
821 $Params{'textht'}, $Params{'cwid'};
822 printf "\tlineht = %g; linewid = %g\n",
823 $Params{'lineht'}, $Params{'linewid'};
826 printf "Last: 0,0\n";
827 $Last_Type = $Types{'OTHER'};
838 # return vertex of $last in direction $d
839 if ( $last eq $Types{'BOND'} ) {
843 if ( $last =~ /^$Types{'RING'}/ ) {
844 return &ringleave($last, $d);
846 if ( $last eq $Types{'MOL'} ) {
847 if ($d == 0 || $d == 180) {
849 } elsif ($d > 0 && $d < 180) {
854 if (defined $Dc{$d}) {
859 return sprintf('%s.%s', $c, $c1);
861 if ( $last eq $Types{'OTHER'} ) {
869 # makering(<type>, <pt>, <v>)
872 my ($type, $pt, $v) = @_;
873 my ($i, $j, $a, $r, $rat, $fix, $c1, $c2);
874 if ($type =~ /flat/) {
879 $r = $Params{'ringside'} / (2 * sin(pi / $v));
881 for ($i = 0; $i <= $v + 1; $i++) {
882 $a = (($i - 1) / $v * 360 + $pt) / 57.29578; # 57. is $deg
883 printf "\tV%d: (%g,%g)\n", $i, $r * sin($a), $r * cos($a);
885 if ($type =~ /flat/) {
886 printf "\tV4: V5; V5: V6\n";
892 for ($i = 1; $i <= $v; $i++) {
894 if ($Put{$i} ne '') {
895 printf "\tV%d: ellipse invis ht %g wid %g at V%d\n",
896 $i, $Params{'crh'}, $Params{'crw'}, $i;
897 printf "\t%s at V%d\n", $Put{$i}, $i;
905 if ($Put{$j} ne '') {
908 printf "\tline from V%d to V%d chop %g chop %g\n", $i, $j, $c1, $c2;
909 if ($Dbl{$i} ne '') {
911 if ($type =~ /flat/ && $i == 3) {
918 if ($Put{$i} eq '') {
921 $c1 = $Params{'cr'} / $fix;
923 if ($Put{$j} eq '') {
926 $c2 = $Params{'cr'} / $fix;
928 printf "\tline from %g<C,V%d> to %g<C,V%d> chop %g chop %g\n",
929 $rat, $i, $rat, $j, $c1, $c2;
930 if ($Dbl{$i} eq 'triple') {
931 printf "\tline from %g<C,V%d> to %g<C,V%d> chop %g chop %g\n",
932 2 - $rat, $i, 2 - $rat, $j, $c1, $c2;
939 for ($i = 1; $i <= $v; $i++) {
944 printf "\tline from V%d to V%d\n", $i, $j;
945 if ($Dbl{$i} ne '') {
947 if ($type =~ /flat/ && $i == 3) {
952 printf "\tline from %g<C,V%d> to %g<C,V%d>\n",
954 if ($Dbl{$i} eq 'triple') {
955 printf "\tline from %g<C,V%d> to %g<C,V%d>\n",
956 2 - $rat, $i, 2 - $rat, $j;
962 # punt on triple temporarily
964 if ($type =~ /benz/ || $Aromatic > 0) {
965 if ($type =~ /flat/) {
970 printf "\tcircle rad %g at 0,0\n", $r;
983 $Words[0] = "\"\" ht 0 wid 0";
984 $type = $Types{'OTHER'};
986 $Words[0] = &atom($n);
987 $type = $Types{'MOL'};
990 $n =~ s/[^A-Za-z0-9]//g; # for stuff like C(OH3): zap non-alnum
992 printf "Last: %s: %s with .%s at Last.%s\n",
993 $n, join(' ', @Words), &leave($type, $Dir + 180),
994 &leave($Last_Type, $Dir);
998 printf "Last: %s: %s with .%s at Last.%s\n",
999 $n, join(' ', @Words), &leave($type, $Dir + 180),
1000 &leave($Last_Type, $Dir);
1001 } elsif ($#Words >= 1 and $Words[1] eq 'below') {
1002 $Words[2] = '' if ! $Words[2];
1003 printf "Last: %s: %s with .n at %s.s\n", $n, $Words[0], $Words[2];
1004 } elsif ($#Words >= 1 and $Words[1] eq 'above') {
1005 $Words[2] = '' if ! $Words[2];
1006 printf "Last: %s: %s with .s at %s.n\n", $n, $Words[0], $Words[2];
1007 } elsif ($#Words >= 2 and $Words[1] eq 'left' && $Words[2] eq 'of') {
1008 $Words[3] = '' if ! $Words[3];
1009 printf "Last: %s: %s with .e at %s.w+(%g,0)\n",
1010 $n, $Words[0], $Words[3], $Params{'dew'};
1011 } elsif ($#Words >= 2 and $Words[1] eq 'right' && $Words[2] eq 'of') {
1012 $Words[3] = '' if ! $Words[3];
1013 printf "Last: %s: %s with .w at %s.e-(%g,0)\n",
1014 $n, $Words[0], $Words[3], $Params{'dew'};
1016 printf "Last: %s: %s\n", $n, join(' ', @Words);
1023 $Labtype{$Last_Name} = $Last_Type;
1025 $Labtype{$n} = $Last_Type;
1030 # print_hash(<hash_or_ref>)
1032 # print the elements of a hash or hash reference
1038 print STDERR "empty hash\n;";
1041 if (ref($_[0]) eq 'HASH') {
1044 warn 'print_hash(): the argument is not a hash or hash reference;';
1049 warn 'print_hash(): the arguments are not a hash;';
1059 print STDERR "empty hash\n";
1062 print STDERR "hash (ignore the ^ characters):\n";
1063 for my $k (sort keys %$hr) {
1065 print STDERR " $k => ";
1067 print STDERR "^$hk^";
1069 print STDERR "undef";
1097 # collect "put Mol at n"
1101 $mol = $Words[$Word_Count++];
1102 if ($Words[$Word_Count] eq 'at') {
1105 $n = $Words[$Word_Count];
1106 if ($n !~ /^\d+$/) {
1108 $n = 0 if $n !~ /^\d+$/;
1109 error('use single digit as argument for "put at"');
1111 if ($n >= 1 && $n <= $v) {
1113 $m =~ s/[^A-Za-z0-9]//g;
1114 $Put{$n} = $m . ':' . &atom($mol);
1116 error('argument of "put at" must be a single digit');
1118 error('argument of "put at" is too large');
1129 my ($typeint, $pt, $verts, $i, $other, $fused, $withat);
1130 $pt = 0; # points up by default
1131 if ($type =~ /([1-8])$/) {
1133 } elsif ($type =~ /flat/) {
1138 $fused = $other = '';
1139 for ($i = 1; $i <= $verts; $i++) {
1140 $Put{$i} = $Dbl{$i} = '';
1142 $Nput = $Aromatic = $withat = 0;
1143 for ($Word_Count = 1; $Word_Count <= $#Words; ) {
1144 if ($Words[$Word_Count] eq 'pointing') {
1146 } elsif ($Words[$Word_Count] eq 'double' ||
1147 $Words[$Word_Count] eq 'triple') {
1149 } elsif ($Words[$Word_Count] =~ /arom/) {
1151 $Word_Count++; # handled later
1153 } elsif ($Words[$Word_Count] eq 'put') {
1156 } elsif ($Words[$Word_Count] =~ /^#/) {
1157 $Word_Count = $#Words + 1;
1160 if ($Words[$Word_Count] eq 'with' || $Words[$Word_Count] eq 'at') {
1163 $other = $other . ' ' . $Words[$Word_Count];
1167 $typeint = $Types{'RING'} . $verts . $pt; # RING | verts | dir
1169 # join a ring to something
1170 if ( $Last_Type =~ /^$Types{'RING'}/ ) {
1172 if (substr($typeint, 2) eq substr($Last_Type, 2)) {
1173 # fails if not 6-sided
1174 $fused = 'with .V6 at Last.V2';
1178 $fused = sprintf('with .%s at Last.%s',
1179 &leave($typeint, $Dir + 180), &leave($Last_Type, $Dir));
1182 &makering($type, $pt, $verts);
1183 printf "] %s %s\n", $fused, $other;
1184 $Last_Type = $typeint;
1185 $Labtype{$Last_Name} = $Last_Type if $Last_Name;
1190 # ringleave(<last>, <d>)
1193 my ($last, $d) = @_;
1195 # return vertex of ring in direction d
1196 $verts = substr($last, 1, 1);
1197 $rd = substr($last, 2);
1198 sprintf('V%d.%s', int( (($d - $rd) % 360) / (360 / $verts)) + 1,
1204 # setparams(<scale>)
1208 $Params{'lineht'} = $scale * 0.2;
1209 $Params{'linewid'} = $scale * 0.2;
1210 $Params{'textht'} = $scale * 0.16;
1211 $Params{'db'} = $scale * 0.2; # bond length
1212 $Params{'cwid'} = $scale * 0.12; # character width
1213 $Params{'cr'} = $scale * 0.08; # rad of invis circles at ring vertices
1214 $Params{'crh'} = $scale * 0.16; # ht of invis ellipse at ring vertices
1215 $Params{'crw'} = $scale * 0.12; # wid
1216 $Params{'dav'} = $scale * 0.015; # vertical shift up for atoms in atom macro
1217 $Params{'dew'} = $scale * 0.02; # east-west shift for left of/right of
1218 $Params{'ringside'} = $scale * 0.3; # side of all rings
1219 $Params{'dbrack'} = $scale * 0.1; # length of bottom of bracket
1226 # Print usage information for --help.
1233 Usage: $Chem_Name [option]... [filespec]...
1235 $Chem_Name is a groff preprocessor for producing chemical structure
1236 diagrams. The output suits to the pic preprocessor.
1238 "filespec" is one of
1239 "filename" name of a readable file
1240 "-" for standard input
1242 All available options are
1244 -h --help print this usage message.
1245 -v --version print version information.
1254 # Get version information from version.sh and print a text with this.
1257 $Groff_Version = $Groff_Version_Preset unless $Groff_Version;
1258 my $year = $Last_Update;
1261 $Chem_Name $Program_Version of $Last_Update (Perl version)
1262 is part of groff version $Groff_Version.
1263 Copyright (C) $year Free Software Foundation, Inc.
1264 GNU groff and chem come with ABSOLUTELY NO WARRANTY.
1265 You may redistribute copies of groff and its subprograms
1266 under the terms of the GNU General Public License.