source: openpam/trunk/misc/gendoc.pl @ 445

Last change on this file since 445 was 445, checked in by Dag-Erling Smørgrav, 8 years ago

Modernize, and add support for other authors than ThinkSec?.

  • Property svn:keywords set to Id
File size: 16.2 KB
Line 
1#!/usr/bin/perl -w
2#-
3# Copyright (c) 2002-2003 Networks Associates Technology, Inc.
4# Copyright (c) 2004-2011 Dag-Erling Smørgrav
5# All rights reserved.
6#
7# This software was developed for the FreeBSD Project by ThinkSec AS and
8# Network Associates Laboratories, the Security Research Division of
9# Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10# ("CBOSS"), as part of the DARPA CHATS research program.
11#
12# Redistribution and use in source and binary forms, with or without
13# modification, are permitted provided that the following conditions
14# are met:
15# 1. Redistributions of source code must retain the above copyright
16#    notice, this list of conditions and the following disclaimer.
17# 2. Redistributions in binary form must reproduce the above copyright
18#    notice, this list of conditions and the following disclaimer in the
19#    documentation and/or other materials provided with the distribution.
20# 3. The name of the author may not be used to endorse or promote
21#    products derived from this software without specific prior written
22#    permission.
23#
24# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34# SUCH DAMAGE.
35#
36# $Id: gendoc.pl 445 2011-10-22 02:45:26Z des $
37#
38
39use strict;
40use locale;
41use Fcntl;
42use Getopt::Std;
43use POSIX qw(locale_h strftime);
44use vars qw($COPYRIGHT %AUTHORS $TODAY %FUNCTIONS %PAMERR);
45
46$COPYRIGHT = ".\\\"-
47.\\\" Copyright (c) 2001-2003 Networks Associates Technology, Inc.
48.\\\" Copyright (c) 2004-2011 Dag-Erling Smørgrav
49.\\\" All rights reserved.
50.\\\"
51.\\\" This software was developed for the FreeBSD Project by ThinkSec AS and
52.\\\" Network Associates Laboratories, the Security Research Division of
53.\\\" Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
54.\\\" (\"CBOSS\"), as part of the DARPA CHATS research program.
55.\\\"
56.\\\" Redistribution and use in source and binary forms, with or without
57.\\\" modification, are permitted provided that the following conditions
58.\\\" are met:
59.\\\" 1. Redistributions of source code must retain the above copyright
60.\\\"    notice, this list of conditions and the following disclaimer.
61.\\\" 2. Redistributions in binary form must reproduce the above copyright
62.\\\"    notice, this list of conditions and the following disclaimer in the
63.\\\"    documentation and/or other materials provided with the distribution.
64.\\\" 3. The name of the author may not be used to endorse or promote
65.\\\"    products derived from this software without specific prior written
66.\\\"    permission.
67.\\\"
68.\\\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
69.\\\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
70.\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
71.\\\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
72.\\\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
73.\\\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
74.\\\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
75.\\\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
76.\\\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
77.\\\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
78.\\\" SUCH DAMAGE.
79.\\\"
80.\\\" \$" . "Id" . "\$
81.\\\"";
82
83%AUTHORS = (
84    THINKSEC => "ThinkSec AS and Network Associates Laboratories, the
85Security Research Division of Network Associates, Inc.\\& under
86DARPA/SPAWAR contract N66001-01-C-8035
87.Pq Dq CBOSS ,
88as part of the DARPA CHATS research program.",
89    DES => ".An Dag-Erling Sm\\(/orgrav Aq des\@FreeBSD.org .",
90);
91
92%PAMERR = (
93    PAM_SUCCESS                 => "Success",
94    PAM_OPEN_ERR                => "Failed to load module",
95    PAM_SYMBOL_ERR              => "Invalid symbol",
96    PAM_SERVICE_ERR             => "Error in service module",
97    PAM_SYSTEM_ERR              => "System error",
98    PAM_BUF_ERR                 => "Memory buffer error",
99    PAM_CONV_ERR                => "Conversation failure",
100    PAM_PERM_DENIED             => "Permission denied",
101    PAM_MAXTRIES                => "Maximum number of tries exceeded",
102    PAM_AUTH_ERR                => "Authentication error",
103    PAM_NEW_AUTHTOK_REQD        => "New authentication token required",
104    PAM_CRED_INSUFFICIENT       => "Insufficient credentials",
105    PAM_AUTHINFO_UNAVAIL        => "Authentication information is unavailable",
106    PAM_USER_UNKNOWN            => "Unknown user",
107    PAM_CRED_UNAVAIL            => "Failed to retrieve user credentials",
108    PAM_CRED_EXPIRED            => "User credentials have expired",
109    PAM_CRED_ERR                => "Failed to set user credentials",
110    PAM_ACCT_EXPIRED            => "User account has expired",
111    PAM_AUTHTOK_EXPIRED         => "Password has expired",
112    PAM_SESSION_ERR             => "Session failure",
113    PAM_AUTHTOK_ERR             => "Authentication token failure",
114    PAM_AUTHTOK_RECOVERY_ERR    => "Failed to recover old authentication token",
115    PAM_AUTHTOK_LOCK_BUSY       => "Authentication token lock busy",
116    PAM_AUTHTOK_DISABLE_AGING   => "Authentication token aging disabled",
117    PAM_NO_MODULE_DATA          => "Module data not found",
118    PAM_IGNORE                  => "Ignore this module",
119    PAM_ABORT                   => "General failure",
120    PAM_TRY_AGAIN               => "Try again",
121    PAM_MODULE_UNKNOWN          => "Unknown module type",
122    PAM_DOMAIN_UNKNOWN          => "Unknown authentication domain",
123);
124
125sub parse_source($) {
126    my $fn = shift;
127
128    local *FILE;
129    my $source;
130    my $func;
131    my $descr;
132    my $type;
133    my $args;
134    my $argnames;
135    my $man;
136    my $inlist;
137    my $inliteral;
138    my %xref;
139    my @errors;
140    my $author;
141
142    if ($fn !~ m,\.c$,) {
143        warn("$fn: not C source, ignoring\n");
144        return undef;
145    }
146
147    open(FILE, "<", "$fn")
148        or die("$fn: open(): $!\n");
149    $source = join('', <FILE>);
150    close(FILE);
151
152    return undef
153        if ($source =~ m/^ \* NOPARSE\s*$/m);
154
155    $author = 'THINKSEC';
156    if ($source =~ s/^ \* AUTHOR\s+(.*?)\s*$//m) {
157        $author = $1;
158    }
159
160    $func = $fn;
161    $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,;
162    if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) {
163        warn("$fn: can't find $func\n");
164        return undef;
165    }
166    ($descr, $type, $args) = ($1, $2, $3);
167    $descr =~ s,^([A-Z][a-z]),lc($1),e;
168    $descr =~ s,[\.\s]*$,,;
169    while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) {
170        # nothing
171    }
172    $args =~ s/,\s+/, /gs;
173    $args = "\"$args\"";
174
175    %xref = (
176        3 => { 'pam' => 1 },
177    );
178
179    if ($type eq "int") {
180        foreach (split("\n", $source)) {
181            next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/);
182            push(@errors, $1);
183        }
184        ++$xref{3}->{'pam_strerror'};
185    }
186
187    $argnames = $args;
188    # extract names of regular arguments
189    $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g;
190    # extract names of function pointer arguments
191    $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g;
192    # escape metacharacters (there shouldn't be any, but...)
193    $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g;
194    # separate argument names with |
195    $argnames =~ s/\" \"/|/g;
196    # and surround with ()
197    $argnames =~ s/^\"(.*)\"$/($1)/;
198    # $argnames is now a regexp that matches argument names
199    $inliteral = $inlist = 0;
200    foreach (split("\n", $source)) {
201        s/\s*$//;
202        if (!defined($man)) {
203            if (m/^\/\*\*$/) {
204                $man = "";
205            }
206            next;
207        }
208        last if (m/^ \*\/$/);
209        s/^ \* ?//;
210        s/\\(.)/$1/gs;
211        if (m/^$/) {
212            if ($man ne "" && $man !~ m/\.Pp\n$/s) {
213                if ($inliteral) {
214                    $man .= "\0\n";
215                } elsif ($inlist) {
216                    $man .= ".El\n.Pp\n";
217                    $inlist = 0;
218                } else {
219                    $man .= ".Pp\n";
220                }
221            }
222            next;
223        }
224        if (m/^>(\w+)(\s+\d)?$/) {
225            my ($page, $sect) = ($1, $2 ? int($2) : 3);
226            ++$xref{$sect}->{$page};
227            next;
228        }
229        if (s/^\s+([=%]?\w+):\s*/.It $1/) {
230            if ($inliteral) {
231                $man .= ".Ed\n";
232                $inliteral = 0;
233            }
234            if (!$inlist) {
235                $man =~ s/\.Pp\n$//s;
236                $man .= ".Bl -tag -width 18n\n";
237                $inlist = 1;
238            }
239            s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs;
240            $man .= "$_\n";
241            next;
242        } elsif ($inlist && m/^\S/) {
243            $man .= ".El\n.Pp\n";
244            $inlist = 0;
245        } elsif ($inliteral && m/^\S/) {
246            $man .= ".Ed\n";
247            $inliteral = 0;
248        } elsif ($inliteral) {
249            $man .= "$_\n";
250            next;
251        } elsif ($inlist) {
252            s/^\s+//;
253        } elsif (m/^\s+/) {
254            $man .= ".Bd -literal\n";
255            $inliteral = 1;
256            $man .= "$_\n";
257            next;
258        }
259        s/\s*=$func\b\s*/\n.Nm\n/gs;
260        s/\s*=$argnames\b\s*/\n.Fa $1\n/gs;
261        s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
262        s/\s*:([a-z_]+)\b\s*/\n.Va $1\n/gs;
263        s/\s*;([a-z_]+)\b\s*/\n.Dv $1\n/gs;
264        while (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
265            ++$xref{3}->{$1};
266        }
267        s/\s*\"(?=\w)/\n.Do\n/gs;
268        s/\"(?!\w)\s*/\n.Dc\n/gs;
269        s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
270        s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
271        s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
272        $man .= "$_\n";
273    }
274    if (defined($man)) {
275        if ($inlist) {
276            $man .= ".El\n";
277        }
278        if ($inliteral) {
279            $man .= ".Ed\n";
280        }
281        $man =~ s/\%/\\&\%/gs;
282        $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([\.,:;-]\S*)\s*/$1 $2\n/gs;
283        $man =~ s/\s*$/\n/gm;
284        $man =~ s/\n+/\n/gs;
285        $man =~ s/\0//gs;
286        $man =~ s/\n\n\./\n\./gs;
287        chomp($man);
288    } else {
289        $man = "No description available.";
290    }
291
292    $FUNCTIONS{$func} = {
293        'source'        => $fn,
294        'name'          => $func,
295        'descr'         => $descr,
296        'type'          => $type,
297        'args'          => $args,
298        'man'           => $man,
299        'xref'          => \%xref,
300        'errors'        => \@errors,
301        'author'        => $author,
302    };
303    if ($source =~ m/^ \* NODOC\s*$/m) {
304        $FUNCTIONS{$func}->{'nodoc'} = 1;
305    }
306    if ($source !~ m/^ \* XSSO \d/m) {
307        $FUNCTIONS{$func}->{'openpam'} = 1;
308    }
309    expand_errors($FUNCTIONS{$func});
310    return $FUNCTIONS{$func};
311}
312
313sub expand_errors($);
314sub expand_errors($) {
315    my $func = shift;           # Ref to function hash
316
317    my %errors;
318    my $ref;
319    my $fn;
320
321    if (defined($func->{'recursed'})) {
322        warn("$func->{'name'}(): loop in error spec\n");
323        return qw();
324    }
325    $func->{'recursed'} = 1;
326
327    foreach (@{$func->{'errors'}}) {
328        if (m/^(PAM_[A-Z_]+)$/) {
329            if (!defined($PAMERR{$1})) {
330                warn("$func->{'name'}(): unrecognized error: $1\n");
331                next;
332            }
333            $errors{$1} = 1;
334        } elsif (m/^!(PAM_[A-Z_]+)$/) {
335            # treat negations separately
336        } elsif (m/^=([a-z_]+)$/) {
337            $ref = $1;
338            if (!defined($FUNCTIONS{$ref})) {
339                $fn = $func->{'source'};
340                $fn =~ s/$func->{'name'}/$ref/;
341                parse_source($fn);
342            }
343            if (!defined($FUNCTIONS{$ref})) {
344                warn("$func->{'name'}(): reference to unknown $ref()\n");
345                next;
346            }
347            foreach (@{$FUNCTIONS{$ref}->{'errors'}}) {
348                $errors{$_} = 1;
349            }
350        } else {
351            warn("$func->{'name'}(): invalid error specification: $_\n");
352        }
353    }
354    foreach (@{$func->{'errors'}}) {
355        if (m/^!(PAM_[A-Z_]+)$/) {
356            delete($errors{$1});
357        }
358    }
359    delete($func->{'recursed'});
360    $func->{'errors'} = [ sort(keys(%errors)) ];
361}
362
363sub dictionary_order($$) {
364    my ($a, $b) = @_;
365
366    $a =~ s/[^[:alpha:]]//g;
367    $b =~ s/[^[:alpha:]]//g;
368    $a cmp $b;
369}
370
371sub genxref($) {
372    my $xref = shift;           # References
373
374    my $mdoc = '';
375    my @refs = ();
376    foreach my $sect (sort(keys(%{$xref}))) {
377        foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
378            push(@refs, "$page $sect");
379        }
380    }
381    while ($_ = shift(@refs)) {
382        $mdoc .= ".Xr $_" .
383            (@refs ? " ,\n" : "\n");
384    }
385    return $mdoc;
386}
387
388sub gendoc($) {
389    my $func = shift;           # Ref to function hash
390
391    local *FILE;
392    my $mdoc;
393    my $fn;
394
395    return if defined($func->{'nodoc'});
396
397    $mdoc = "$COPYRIGHT
398.Dd $TODAY
399.Dt " . uc($func->{'name'}) . " 3
400.Os
401.Sh NAME
402.Nm $func->{'name'}
403.Nd $func->{'descr'}
404.Sh LIBRARY
405.Lb libpam
406.Sh SYNOPSIS
407.In sys/types.h
408.In security/pam_appl.h
409";
410    if ($func->{'name'} =~ m/_sm_/) {
411        $mdoc .= ".In security/pam_modules.h\n"
412    }
413    if ($func->{'name'} =~ m/openpam/) {
414        $mdoc .= ".In security/openpam.h\n"
415    }
416    $mdoc .= ".Ft \"$func->{'type'}\"
417.Fn $func->{'name'} $func->{'args'}
418.Sh DESCRIPTION
419$func->{'man'}
420";
421    if ($func->{'type'} eq "int") {
422        $mdoc .= ".Sh RETURN VALUES
423The
424.Nm
425function returns one of the following values:
426.Bl -tag -width 18n
427";
428        my @errors = @{$func->{'errors'}};
429        warn("$func->{'name'}(): no error specification\n")
430            unless(@errors);
431        foreach (@errors) {
432            $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
433        }
434        $mdoc .= ".El\n";
435    } else {
436        if ($func->{'type'} =~ m/\*$/) {
437            $mdoc .= ".Sh RETURN VALUES
438The
439.Nm
440function returns
441.Dv NULL
442on failure.
443";
444        }
445    }
446    $mdoc .= ".Sh SEE ALSO\n" . genxref($func->{'xref'});
447    $mdoc .= ".Sh STANDARDS\n";
448    if ($func->{'openpam'}) {
449        $mdoc .= "The
450.Nm
451function is an OpenPAM extension.
452";
453    } else {
454        $mdoc .= ".Rs
455.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
456.%D \"June 1997\"
457.Re
458";
459    }
460    $mdoc .= ".Sh AUTHORS
461The
462.Nm
463function and this manual page were developed for the
464.Fx
465Project by\n" . $AUTHORS{$func->{'author'} // 'THINKSEC_DARPA'} . "\n";
466    $fn = "$func->{'name'}.3";
467    if (open(FILE, ">", $fn)) {
468        print(FILE $mdoc);
469        close(FILE);
470    } else {
471        warn("$fn: open(): $!\n");
472    }
473}
474
475sub readproto($) {
476    my $fn = shift;             # File name
477
478    local *FILE;
479    my %func;
480
481    open(FILE, "<", "$fn")
482        or die("$fn: open(): $!\n");
483    while (<FILE>) {
484        if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) {
485            $func{'Nm'} = $func{'Nm'} || $1;
486        } elsif (m/^\.Ft (\S.*?)\s*$/) {
487            $func{'Ft'} = $func{'Ft'} || $1;
488        } elsif (m/^\.Fn (\S.*?)\s*$/) {
489            $func{'Fn'} = $func{'Fn'} || $1;
490        }
491    }
492    close(FILE);
493    if ($func{'Nm'}) {
494        $FUNCTIONS{$func{'Nm'}} = \%func;
495    } else {
496        warn("No function found\n");
497    }
498}
499
500sub gensummary($) {
501    my $page = shift;           # Which page to produce
502
503    local *FILE;
504    my $upage;
505    my $func;
506    my %xref;
507
508    open(FILE, ">", "$page.3")
509        or die("$page.3: $!\n");
510
511    $page =~ m/(\w+)$/;
512    $upage = uc($1);
513    print FILE "$COPYRIGHT
514.Dd $TODAY
515.Dt $upage 3
516.Os
517.Sh NAME
518";
519    my @funcs = sort(keys(%FUNCTIONS));
520    while ($func = shift(@funcs)) {
521        print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}";
522        print FILE " ,"
523                if (@funcs);
524        print FILE "\n";
525    }
526    print FILE ".Nd Pluggable Authentication Modules Library
527.Sh LIBRARY
528.Lb libpam
529.Sh SYNOPSIS\n";
530    if ($page eq 'pam') {
531        print FILE ".In security/pam_appl.h\n";
532    } else {
533        print FILE ".In security/openpam.h\n";
534    }
535    foreach $func (sort(keys(%FUNCTIONS))) {
536        print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n";
537        print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n";
538    }
539    while (<STDIN>) {
540        if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
541            ++$xref{int($2)}->{$1};
542        }
543        print FILE $_;
544    }
545
546    if ($page eq 'pam') {
547        print FILE ".Sh RETURN VALUES
548The following return codes are defined by
549.In security/pam_constants.h :
550.Bl -tag -width 18n
551";
552        foreach (sort(keys(%PAMERR))) {
553            print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
554        }
555        print FILE ".El\n";
556    }
557    print FILE ".Sh SEE ALSO
558";
559    if ($page eq 'pam') {
560        ++$xref{3}->{'openpam'};
561    }
562    foreach $func (keys(%FUNCTIONS)) {
563        ++$xref{3}->{$func};
564    }
565    print FILE genxref(\%xref);
566    print FILE ".Sh STANDARDS
567.Rs
568.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
569.%D \"June 1997\"
570.Re
571.Sh AUTHORS
572The OpenPAM library and this manual page were developed for the
573.Fx
574Project by ThinkSec AS and Network Associates Laboratories, the
575Security Research Division of Network Associates, Inc.\\& under
576DARPA/SPAWAR contract N66001-01-C-8035
577.Pq Dq CBOSS ,
578as part of the DARPA CHATS research program.
579";
580    close(FILE);
581}
582
583sub usage() {
584
585    print(STDERR "usage: gendoc [-op] source [...]\n");
586    exit(1);
587}
588
589MAIN:{
590    my %opts;
591
592    usage()
593        unless (@ARGV && getopts("op", \%opts));
594    setlocale(LC_ALL, "en_US.UTF-8");
595    $TODAY = strftime("%B %e, %Y", localtime(time()));
596    $TODAY =~ s,\s+, ,g;
597    if ($opts{'o'} || $opts{'p'}) {
598        foreach my $fn (@ARGV) {
599            readproto($fn);
600        }
601        gensummary('openpam')
602            if ($opts{'o'});
603        gensummary('pam')
604            if ($opts{'p'});
605    } else {
606        foreach my $fn (@ARGV) {
607            my $func = parse_source($fn);
608            gendoc($func)
609                if (defined($func));
610        }
611    }
612    exit(0);
613}
Note: See TracBrowser for help on using the repository browser.