Revision 443:350acce374a2 extra
| extra/soundsoftware/SoundSoftware-salted.pm | ||
|---|---|---|
| 1 |
package Apache::Authn::SoundSoftware; |
|
| 2 |
|
|
| 3 |
=head1 Apache::Authn::SoundSoftware |
|
| 4 |
|
|
| 5 |
SoundSoftware - a mod_perl module for Apache authentication against a |
|
| 6 |
Redmine database and optional LDAP implementing the access control |
|
| 7 |
rules required for the SoundSoftware.ac.uk repository site. |
|
| 8 |
|
|
| 9 |
=head1 SYNOPSIS |
|
| 10 |
|
|
| 11 |
This module is closely based on the Redmine.pm authentication module |
|
| 12 |
provided with Redmine. It is intended to be used for authentication |
|
| 13 |
in front of a repository service such as hgwebdir. |
|
| 14 |
|
|
| 15 |
Requirements: |
|
| 16 |
|
|
| 17 |
1. Clone/pull from repo for public project: Any user, no |
|
| 18 |
authentication required |
|
| 19 |
|
|
| 20 |
2. Clone/pull from repo for private project: Project members only |
|
| 21 |
|
|
| 22 |
3. Push to repo for public project: "Permitted" users only (this |
|
| 23 |
probably means project members who are also identified in the hgrc web |
|
| 24 |
section for the repository and so will be approved by hgwebdir?) |
|
| 25 |
|
|
| 26 |
4. Push to repo for private project: "Permitted" users only (as above) |
|
| 27 |
|
|
| 28 |
5. Push to any repo that is tracking an external repo: Refused always |
|
| 29 |
|
|
| 30 |
=head1 INSTALLATION |
|
| 31 |
|
|
| 32 |
Debian/ubuntu: |
|
| 33 |
|
|
| 34 |
apt-get install libapache-dbi-perl libapache2-mod-perl2 \ |
|
| 35 |
libdbd-mysql-perl libauthen-simple-ldap-perl libio-socket-ssl-perl |
|
| 36 |
|
|
| 37 |
Note that LDAP support is hardcoded "on" in this script (it is |
|
| 38 |
optional in the original Redmine.pm). |
|
| 39 |
|
|
| 40 |
=head1 CONFIGURATION |
|
| 41 |
|
|
| 42 |
## This module has to be in your perl path |
|
| 43 |
## eg: /usr/local/lib/site_perl/Apache/Authn/SoundSoftware.pm |
|
| 44 |
PerlLoadModule Apache::Authn::SoundSoftware |
|
| 45 |
|
|
| 46 |
# Example when using hgwebdir |
|
| 47 |
ScriptAlias / "/var/hg/hgwebdir.cgi/" |
|
| 48 |
|
|
| 49 |
<Location /> |
|
| 50 |
AuthName "Mercurial" |
|
| 51 |
AuthType Basic |
|
| 52 |
Require valid-user |
|
| 53 |
PerlAccessHandler Apache::Authn::SoundSoftware::access_handler |
|
| 54 |
PerlAuthenHandler Apache::Authn::SoundSoftware::authen_handler |
|
| 55 |
SoundSoftwareDSN "DBI:mysql:database=redmine;host=localhost" |
|
| 56 |
SoundSoftwareDbUser "redmine" |
|
| 57 |
SoundSoftwareDbPass "password" |
|
| 58 |
Options +ExecCGI |
|
| 59 |
AddHandler cgi-script .cgi |
|
| 60 |
## Optional where clause (fulltext search would be slow and |
|
| 61 |
## database dependant). |
|
| 62 |
# SoundSoftwareDbWhereClause "and members.role_id IN (1,2)" |
|
| 63 |
## Optional prefix for local repository URLs |
|
| 64 |
# SoundSoftwareRepoPrefix "/var/hg/" |
|
| 65 |
</Location> |
|
| 66 |
|
|
| 67 |
See the original Redmine.pm for further configuration notes. |
|
| 68 |
|
|
| 69 |
=cut |
|
| 70 |
|
|
| 71 |
use strict; |
|
| 72 |
use warnings FATAL => 'all', NONFATAL => 'redefine'; |
|
| 73 |
|
|
| 74 |
use DBI; |
|
| 75 |
use Digest::SHA1; |
|
| 76 |
use Authen::Simple::LDAP; |
|
| 77 |
use Apache2::Module; |
|
| 78 |
use Apache2::Access; |
|
| 79 |
use Apache2::ServerRec qw(); |
|
| 80 |
use Apache2::RequestRec qw(); |
|
| 81 |
use Apache2::RequestUtil qw(); |
|
| 82 |
use Apache2::Const qw(:common :override :cmd_how); |
|
| 83 |
use APR::Pool (); |
|
| 84 |
use APR::Table (); |
|
| 85 |
|
|
| 86 |
my @directives = ( |
|
| 87 |
{
|
|
| 88 |
name => 'SoundSoftwareDSN', |
|
| 89 |
req_override => OR_AUTHCFG, |
|
| 90 |
args_how => TAKE1, |
|
| 91 |
errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"', |
|
| 92 |
}, |
|
| 93 |
{
|
|
| 94 |
name => 'SoundSoftwareDbUser', |
|
| 95 |
req_override => OR_AUTHCFG, |
|
| 96 |
args_how => TAKE1, |
|
| 97 |
}, |
|
| 98 |
{
|
|
| 99 |
name => 'SoundSoftwareDbPass', |
|
| 100 |
req_override => OR_AUTHCFG, |
|
| 101 |
args_how => TAKE1, |
|
| 102 |
}, |
|
| 103 |
{
|
|
| 104 |
name => 'SoundSoftwareDbWhereClause', |
|
| 105 |
req_override => OR_AUTHCFG, |
|
| 106 |
args_how => TAKE1, |
|
| 107 |
}, |
|
| 108 |
{
|
|
| 109 |
name => 'SoundSoftwareRepoPrefix', |
|
| 110 |
req_override => OR_AUTHCFG, |
|
| 111 |
args_how => TAKE1, |
|
| 112 |
}, |
|
| 113 |
); |
|
| 114 |
|
|
| 115 |
sub SoundSoftwareDSN {
|
|
| 116 |
my ($self, $parms, $arg) = @_; |
|
| 117 |
$self->{SoundSoftwareDSN} = $arg;
|
|
| 118 |
my $query = "SELECT |
|
| 119 |
hashed_password, salt, auth_source_id, permissions |
|
| 120 |
FROM members, projects, users, roles, member_roles |
|
| 121 |
WHERE |
|
| 122 |
projects.id=members.project_id |
|
| 123 |
AND member_roles.member_id=members.id |
|
| 124 |
AND users.id=members.user_id |
|
| 125 |
AND roles.id=member_roles.role_id |
|
| 126 |
AND users.status=1 |
|
| 127 |
AND login=? |
|
| 128 |
AND identifier=? "; |
|
| 129 |
$self->{SoundSoftwareQuery} = trim($query);
|
|
| 130 |
} |
|
| 131 |
|
|
| 132 |
sub SoundSoftwareDbUser { set_val('SoundSoftwareDbUser', @_); }
|
|
| 133 |
sub SoundSoftwareDbPass { set_val('SoundSoftwareDbPass', @_); }
|
|
| 134 |
sub SoundSoftwareDbWhereClause {
|
|
| 135 |
my ($self, $parms, $arg) = @_; |
|
| 136 |
$self->{SoundSoftwareQuery} = trim($self->{SoundSoftwareQuery}.($arg ? $arg : "")." ");
|
|
| 137 |
} |
|
| 138 |
|
|
| 139 |
sub SoundSoftwareRepoPrefix {
|
|
| 140 |
my ($self, $parms, $arg) = @_; |
|
| 141 |
if ($arg) {
|
|
| 142 |
$self->{SoundSoftwareRepoPrefix} = $arg;
|
|
| 143 |
} |
|
| 144 |
} |
|
| 145 |
|
|
| 146 |
sub trim {
|
|
| 147 |
my $string = shift; |
|
| 148 |
$string =~ s/\s{2,}/ /g;
|
|
| 149 |
return $string; |
|
| 150 |
} |
|
| 151 |
|
|
| 152 |
sub set_val {
|
|
| 153 |
my ($key, $self, $parms, $arg) = @_; |
|
| 154 |
$self->{$key} = $arg;
|
|
| 155 |
} |
|
| 156 |
|
|
| 157 |
Apache2::Module::add(__PACKAGE__, \@directives); |
|
| 158 |
|
|
| 159 |
|
|
| 160 |
my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
|
|
| 161 |
|
|
| 162 |
sub access_handler {
|
|
| 163 |
my $r = shift; |
|
| 164 |
|
|
| 165 |
print STDERR "SoundSoftware.pm: In access handler at " . scalar localtime() . "\n"; |
|
| 166 |
|
|
| 167 |
unless ($r->some_auth_required) {
|
|
| 168 |
$r->log_reason("No authentication has been configured");
|
|
| 169 |
return FORBIDDEN; |
|
| 170 |
} |
|
| 171 |
|
|
| 172 |
my $method = $r->method; |
|
| 173 |
|
|
| 174 |
print STDERR "SoundSoftware.pm: Method: $method, uri " . $r->uri . ", location " . $r->location . "\n"; |
|
| 175 |
print STDERR "SoundSoftware.pm: Accept: " . $r->headers_in->{Accept} . "\n";
|
|
| 176 |
|
|
| 177 |
my $dbh = connect_database($r); |
|
| 178 |
unless ($dbh) {
|
|
| 179 |
print STDERR "SoundSoftware.pm: Database connection failed!: " . $DBI::errstr . "\n"; |
|
| 180 |
return FORBIDDEN; |
|
| 181 |
} |
|
| 182 |
|
|
| 183 |
print STDERR "Connected to db, dbh is " . $dbh . "\n"; |
|
| 184 |
|
|
| 185 |
my $project_id = get_project_identifier($dbh, $r); |
|
| 186 |
|
|
| 187 |
if (!defined $read_only_methods{$method}) {
|
|
| 188 |
print STDERR "SoundSoftware.pm: Method is not read-only\n"; |
|
| 189 |
if (project_repo_is_readonly($dbh, $project_id, $r)) {
|
|
| 190 |
print STDERR "SoundSoftware.pm: Project repo is read-only, refusing access\n"; |
|
| 191 |
return FORBIDDEN; |
|
| 192 |
} else {
|
|
| 193 |
print STDERR "SoundSoftware.pm: Project repo is read-write, authentication handler required\n"; |
|
| 194 |
return OK; |
|
| 195 |
} |
|
| 196 |
} |
|
| 197 |
|
|
| 198 |
my $status = get_project_status($dbh, $project_id, $r); |
|
| 199 |
|
|
| 200 |
$dbh->disconnect(); |
|
| 201 |
undef $dbh; |
|
| 202 |
|
|
| 203 |
if ($status == 0) { # nonexistent
|
|
| 204 |
print STDERR "SoundSoftware.pm: Project does not exist, refusing access\n"; |
|
| 205 |
return FORBIDDEN; |
|
| 206 |
} elsif ($status == 1) { # public
|
|
| 207 |
print STDERR "SoundSoftware.pm: Project is public, no restriction here\n"; |
|
| 208 |
$r->set_handlers(PerlAuthenHandler => [\&OK]) |
|
| 209 |
} else { # private
|
|
| 210 |
print STDERR "SoundSoftware.pm: Project is private, authentication handler required\n"; |
|
| 211 |
} |
|
| 212 |
|
|
| 213 |
return OK |
|
| 214 |
} |
|
| 215 |
|
|
| 216 |
sub authen_handler {
|
|
| 217 |
my $r = shift; |
|
| 218 |
|
|
| 219 |
print STDERR "SoundSoftware.pm: In authentication handler at " . scalar localtime() . "\n"; |
|
| 220 |
|
|
| 221 |
my $dbh = connect_database($r); |
|
| 222 |
unless ($dbh) {
|
|
| 223 |
print STDERR "SoundSoftware.pm: Database connection failed!: " . $DBI::errstr . "\n"; |
|
| 224 |
return AUTH_REQUIRED; |
|
| 225 |
} |
|
| 226 |
|
|
| 227 |
my $project_id = get_project_identifier($dbh, $r); |
|
| 228 |
my $realm = get_realm($dbh, $project_id, $r); |
|
| 229 |
$r->auth_name($realm); |
|
| 230 |
|
|
| 231 |
my ($res, $redmine_pass) = $r->get_basic_auth_pw(); |
|
| 232 |
unless ($res == OK) {
|
|
| 233 |
$dbh->disconnect(); |
|
| 234 |
undef $dbh; |
|
| 235 |
return $res; |
|
| 236 |
} |
|
| 237 |
|
|
| 238 |
print STDERR "SoundSoftware.pm: User is " . $r->user . ", got password\n"; |
|
| 239 |
|
|
| 240 |
my $permitted = is_permitted($dbh, $project_id, $r->user, $redmine_pass, $r); |
|
| 241 |
|
|
| 242 |
$dbh->disconnect(); |
|
| 243 |
undef $dbh; |
|
| 244 |
|
|
| 245 |
if ($permitted) {
|
|
| 246 |
return OK; |
|
| 247 |
} else {
|
|
| 248 |
print STDERR "SoundSoftware.pm: Not permitted\n"; |
|
| 249 |
$r->note_auth_failure(); |
|
| 250 |
return AUTH_REQUIRED; |
|
| 251 |
} |
|
| 252 |
} |
|
| 253 |
|
|
| 254 |
sub get_project_status {
|
|
| 255 |
my $dbh = shift; |
|
| 256 |
my $project_id = shift; |
|
| 257 |
my $r = shift; |
|
| 258 |
|
|
| 259 |
if (!defined $project_id or $project_id eq '') {
|
|
| 260 |
return 0; # nonexistent |
|
| 261 |
} |
|
| 262 |
|
|
| 263 |
my $sth = $dbh->prepare( |
|
| 264 |
"SELECT is_public FROM projects WHERE projects.identifier = ?;" |
|
| 265 |
); |
|
| 266 |
|
|
| 267 |
$sth->execute($project_id); |
|
| 268 |
my $ret = 0; # nonexistent |
|
| 269 |
if (my @row = $sth->fetchrow_array) {
|
|
| 270 |
if ($row[0] eq "1" || $row[0] eq "t") {
|
|
| 271 |
$ret = 1; # public |
|
| 272 |
} else {
|
|
| 273 |
$ret = 2; # private |
|
| 274 |
} |
|
| 275 |
} |
|
| 276 |
$sth->finish(); |
|
| 277 |
undef $sth; |
|
| 278 |
|
|
| 279 |
$ret; |
|
| 280 |
} |
|
| 281 |
|
|
| 282 |
sub project_repo_is_readonly {
|
|
| 283 |
my $dbh = shift; |
|
| 284 |
my $project_id = shift; |
|
| 285 |
my $r = shift; |
|
| 286 |
|
|
| 287 |
if (!defined $project_id or $project_id eq '') {
|
|
| 288 |
return 0; # nonexistent |
|
| 289 |
} |
|
| 290 |
|
|
| 291 |
my $sth = $dbh->prepare( |
|
| 292 |
"SELECT repositories.is_external FROM repositories, projects WHERE projects.identifier = ? AND repositories.project_id = projects.id;" |
|
| 293 |
); |
|
| 294 |
|
|
| 295 |
$sth->execute($project_id); |
|
| 296 |
my $ret = 0; # nonexistent |
|
| 297 |
if (my @row = $sth->fetchrow_array) {
|
|
| 298 |
if (defined($row[0]) && ($row[0] eq "1" || $row[0] eq "t")) {
|
|
| 299 |
$ret = 1; # read-only (i.e. external) |
|
| 300 |
} else {
|
|
| 301 |
$ret = 0; # read-write |
|
| 302 |
} |
|
| 303 |
} |
|
| 304 |
$sth->finish(); |
|
| 305 |
undef $sth; |
|
| 306 |
|
|
| 307 |
$ret; |
|
| 308 |
} |
|
| 309 |
|
|
| 310 |
sub is_permitted {
|
|
| 311 |
my $dbh = shift; |
|
| 312 |
my $project_id = shift; |
|
| 313 |
my $redmine_user = shift; |
|
| 314 |
my $redmine_pass = shift; |
|
| 315 |
my $r = shift; |
|
| 316 |
|
|
| 317 |
my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass); |
|
| 318 |
|
|
| 319 |
my $cfg = Apache2::Module::get_config |
|
| 320 |
(__PACKAGE__, $r->server, $r->per_dir_config); |
|
| 321 |
|
|
| 322 |
my $query = $cfg->{SoundSoftwareQuery};
|
|
| 323 |
my $sth = $dbh->prepare($query); |
|
| 324 |
$sth->execute($redmine_user, $project_id); |
|
| 325 |
|
|
| 326 |
my $ret; |
|
| 327 |
while (my ($hashed_password, $salt, $auth_source_id, $permissions) = $sth->fetchrow_array) {
|
|
| 328 |
|
|
| 329 |
# Test permissions for this user before we verify credentials |
|
| 330 |
# -- if the user is not permitted this action anyway, there's |
|
| 331 |
# not much point in e.g. contacting the LDAP |
|
| 332 |
|
|
| 333 |
my $method = $r->method; |
|
| 334 |
|
|
| 335 |
if ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/)
|
|
| 336 |
|| $permissions =~ /:commit_access/) {
|
|
| 337 |
|
|
| 338 |
# User would be permitted this action, if their |
|
| 339 |
# credentials checked out -- test those now |
|
| 340 |
|
|
| 341 |
print STDERR "SoundSoftware.pm: User $redmine_user has required role, checking credentials\n"; |
|
| 342 |
|
|
| 343 |
unless ($auth_source_id) {
|
|
| 344 |
my $salted_password = Digest::SHA1::sha1_hex($salt.$pass_digest); |
|
| 345 |
if ($hashed_password eq $salted_password) {
|
|
| 346 |
print STDERR "SoundSoftware.pm: User $redmine_user authenticated via password\n"; |
|
| 347 |
$ret = 1; |
|
| 348 |
last; |
|
| 349 |
} |
|
| 350 |
} else {
|
|
| 351 |
my $sthldap = $dbh->prepare( |
|
| 352 |
"SELECT host,port,tls,account,account_password,base_dn,attr_login FROM auth_sources WHERE id = ?;" |
|
| 353 |
); |
|
| 354 |
$sthldap->execute($auth_source_id); |
|
| 355 |
while (my @rowldap = $sthldap->fetchrow_array) {
|
|
| 356 |
my $ldap = Authen::Simple::LDAP->new( |
|
| 357 |
host => ($rowldap[2] eq "1" || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0], |
|
| 358 |
port => $rowldap[1], |
|
| 359 |
basedn => $rowldap[5], |
|
| 360 |
binddn => $rowldap[3] ? $rowldap[3] : "", |
|
| 361 |
bindpw => $rowldap[4] ? $rowldap[4] : "", |
|
| 362 |
filter => "(".$rowldap[6]."=%s)"
|
|
| 363 |
); |
|
| 364 |
if ($ldap->authenticate($redmine_user, $redmine_pass)) {
|
|
| 365 |
print STDERR "SoundSoftware.pm: User $redmine_user authenticated via LDAP\n"; |
|
| 366 |
$ret = 1; |
|
| 367 |
} |
|
| 368 |
} |
|
| 369 |
$sthldap->finish(); |
|
| 370 |
undef $sthldap; |
|
| 371 |
} |
|
| 372 |
} else {
|
|
| 373 |
print STDERR "SoundSoftware.pm: User $redmine_user lacks required role for this project\n"; |
|
| 374 |
} |
|
| 375 |
} |
|
| 376 |
|
|
| 377 |
$sth->finish(); |
|
| 378 |
undef $sth; |
|
| 379 |
|
|
| 380 |
$ret; |
|
| 381 |
} |
|
| 382 |
|
|
| 383 |
sub get_project_identifier {
|
|
| 384 |
my $dbh = shift; |
|
| 385 |
my $r = shift; |
|
| 386 |
|
|
| 387 |
my $location = $r->location; |
|
| 388 |
my ($repo) = $r->uri =~ m{$location/*([^/]+)};
|
|
| 389 |
|
|
| 390 |
return $repo if (!$repo); |
|
| 391 |
|
|
| 392 |
$repo =~ s/[^a-zA-Z0-9\._-]//g; |
|
| 393 |
|
|
| 394 |
# The original Redmine.pm returns the string just calculated as |
|
| 395 |
# the project identifier. That won't do for us -- we may have |
|
| 396 |
# (and in fact already do have, in our test instance) projects |
|
| 397 |
# whose repository names differ from the project identifiers. |
|
| 398 |
|
|
| 399 |
# This is a rather fundamental change because it means that almost |
|
| 400 |
# every request needs more than one database query -- which |
|
| 401 |
# prompts us to start passing around $dbh instead of connecting |
|
| 402 |
# locally within each function as is done in Redmine.pm. |
|
| 403 |
|
|
| 404 |
my $sth = $dbh->prepare( |
|
| 405 |
"SELECT projects.identifier FROM projects, repositories WHERE repositories.project_id = projects.id AND repositories.url LIKE ?;" |
|
| 406 |
); |
|
| 407 |
|
|
| 408 |
my $cfg = Apache2::Module::get_config |
|
| 409 |
(__PACKAGE__, $r->server, $r->per_dir_config); |
|
| 410 |
|
|
| 411 |
my $prefix = $cfg->{SoundSoftwareRepoPrefix};
|
|
| 412 |
if (!defined $prefix) { $prefix = '%/'; }
|
|
| 413 |
|
|
| 414 |
my $identifier = ''; |
|
| 415 |
|
|
| 416 |
$sth->execute($prefix . $repo); |
|
| 417 |
my $ret = 0; |
|
| 418 |
if (my @row = $sth->fetchrow_array) {
|
|
| 419 |
$identifier = $row[0]; |
|
| 420 |
} |
|
| 421 |
$sth->finish(); |
|
| 422 |
undef $sth; |
|
| 423 |
|
|
| 424 |
print STDERR "SoundSoftware.pm: Repository '$repo' belongs to project '$identifier'\n"; |
|
| 425 |
|
|
| 426 |
$identifier; |
|
| 427 |
} |
|
| 428 |
|
|
| 429 |
sub get_realm {
|
|
| 430 |
my $dbh = shift; |
|
| 431 |
my $project_id = shift; |
|
| 432 |
my $r = shift; |
|
| 433 |
|
|
| 434 |
my $sth = $dbh->prepare( |
|
| 435 |
"SELECT projects.name FROM projects WHERE projects.identifier = ?;" |
|
| 436 |
); |
|
| 437 |
|
|
| 438 |
my $name = $project_id; |
|
| 439 |
|
|
| 440 |
$sth->execute($project_id); |
|
| 441 |
my $ret = 0; |
|
| 442 |
if (my @row = $sth->fetchrow_array) {
|
|
| 443 |
$name = $row[0]; |
|
| 444 |
} |
|
| 445 |
$sth->finish(); |
|
| 446 |
undef $sth; |
|
| 447 |
|
|
| 448 |
# be timid about characters not permitted in auth realm and revert |
|
| 449 |
# to project identifier if any are found |
|
| 450 |
if ($name =~ m/[^\w\d\s\._-]/) {
|
|
| 451 |
$name = $project_id; |
|
| 452 |
} |
|
| 453 |
|
|
| 454 |
my $realm = '"Mercurial repository for ' . "'$name'" . '"'; |
|
| 455 |
|
|
| 456 |
$realm; |
|
| 457 |
} |
|
| 458 |
|
|
| 459 |
sub connect_database {
|
|
| 460 |
my $r = shift; |
|
| 461 |
|
|
| 462 |
my $cfg = Apache2::Module::get_config |
|
| 463 |
(__PACKAGE__, $r->server, $r->per_dir_config); |
|
| 464 |
|
|
| 465 |
return DBI->connect($cfg->{SoundSoftwareDSN},
|
|
| 466 |
$cfg->{SoundSoftwareDbUser},
|
|
| 467 |
$cfg->{SoundSoftwareDbPass});
|
|
| 468 |
} |
|
| 469 |
|
|
| 470 |
1; |
|
| extra/soundsoftware/SoundSoftware.pm | ||
|---|---|---|
| 25 | 25 |
|
| 26 | 26 |
4. Push to repo for private project: "Permitted" users only (as above) |
| 27 | 27 |
|
| 28 |
5. Push to any repo that is tracking an external repo: Refused always |
|
| 29 |
|
|
| 28 | 30 |
=head1 INSTALLATION |
| 29 | 31 |
|
| 30 | 32 |
Debian/ubuntu: |
| ... | ... | |
| 172 | 174 |
print STDERR "SoundSoftware.pm: Method: $method, uri " . $r->uri . ", location " . $r->location . "\n"; |
| 173 | 175 |
print STDERR "SoundSoftware.pm: Accept: " . $r->headers_in->{Accept} . "\n";
|
| 174 | 176 |
|
| 175 |
if (!defined $read_only_methods{$method}) {
|
|
| 176 |
print STDERR "SoundSoftware.pm: Method is not read-only, authentication handler required\n"; |
|
| 177 |
return OK; |
|
| 178 |
} |
|
| 179 |
|
|
| 180 | 177 |
my $dbh = connect_database($r); |
| 181 | 178 |
unless ($dbh) {
|
| 182 | 179 |
print STDERR "SoundSoftware.pm: Database connection failed!: " . $DBI::errstr . "\n"; |
| 183 | 180 |
return FORBIDDEN; |
| 184 | 181 |
} |
| 185 | 182 |
|
| 186 |
|
|
| 187 |
print STDERR "Connected to db, dbh is " . $dbh . "\n"; |
|
| 183 |
print STDERR "Connected to db, dbh is " . $dbh . "\n"; |
|
| 188 | 184 |
|
| 189 | 185 |
my $project_id = get_project_identifier($dbh, $r); |
| 186 |
|
|
| 187 |
if (!defined $read_only_methods{$method}) {
|
|
| 188 |
print STDERR "SoundSoftware.pm: Method is not read-only\n"; |
|
| 189 |
if (project_repo_is_readonly($dbh, $project_id, $r)) {
|
|
| 190 |
print STDERR "SoundSoftware.pm: Project repo is read-only, refusing access\n"; |
|
| 191 |
return FORBIDDEN; |
|
| 192 |
} else {
|
|
| 193 |
print STDERR "SoundSoftware.pm: Project repo is read-write, authentication handler required\n"; |
|
| 194 |
return OK; |
|
| 195 |
} |
|
| 196 |
} |
|
| 197 |
|
|
| 190 | 198 |
my $status = get_project_status($dbh, $project_id, $r); |
| 191 | 199 |
|
| 192 | 200 |
$dbh->disconnect(); |
| ... | ... | |
| 271 | 279 |
$ret; |
| 272 | 280 |
} |
| 273 | 281 |
|
| 282 |
sub project_repo_is_readonly {
|
|
| 283 |
my $dbh = shift; |
|
| 284 |
my $project_id = shift; |
|
| 285 |
my $r = shift; |
|
| 286 |
|
|
| 287 |
if (!defined $project_id or $project_id eq '') {
|
|
| 288 |
return 0; # nonexistent |
|
| 289 |
} |
|
| 290 |
|
|
| 291 |
my $sth = $dbh->prepare( |
|
| 292 |
"SELECT repositories.is_external FROM repositories, projects WHERE projects.identifier = ? AND repositories.project_id = projects.id;" |
|
| 293 |
); |
|
| 294 |
|
|
| 295 |
$sth->execute($project_id); |
|
| 296 |
my $ret = 0; # nonexistent |
|
| 297 |
if (my @row = $sth->fetchrow_array) {
|
|
| 298 |
if (defined($row[0]) && ($row[0] eq "1" || $row[0] eq "t")) {
|
|
| 299 |
$ret = 1; # read-only (i.e. external) |
|
| 300 |
} else {
|
|
| 301 |
$ret = 0; # read-write |
|
| 302 |
} |
|
| 303 |
} |
|
| 304 |
$sth->finish(); |
|
| 305 |
undef $sth; |
|
| 306 |
|
|
| 307 |
$ret; |
|
| 308 |
} |
|
| 309 |
|
|
| 274 | 310 |
sub is_permitted {
|
| 275 | 311 |
my $dbh = shift; |
| 276 | 312 |
my $project_id = shift; |
| extra/soundsoftware/convert-external-repos.rb | ||
|---|---|---|
| 1 |
#!/usr/bin/env ruby |
|
| 2 |
|
|
| 3 |
# == Synopsis |
|
| 4 |
# |
|
| 5 |
# convert-external-repos: Update local Mercurial mirrors of external repos, |
|
| 6 |
# by running an external command for each project requiring an update. |
|
| 7 |
# |
|
| 8 |
# == Usage |
|
| 9 |
# |
|
| 10 |
# convert-external-repos [OPTIONS...] -s [DIR] -r [HOST] |
|
| 11 |
# |
|
| 12 |
# == Arguments (mandatory) |
|
| 13 |
# |
|
| 14 |
# -s, --scm-dir=DIR use DIR as base directory for repositories |
|
| 15 |
# -r, --redmine-host=HOST assume Redmine is hosted on HOST. Examples: |
|
| 16 |
# -r redmine.example.net |
|
| 17 |
# -r http://redmine.example.net |
|
| 18 |
# -r https://example.net/redmine |
|
| 19 |
# -k, --key=KEY use KEY as the Redmine API key |
|
| 20 |
# -c, --command=COMMAND use this command to update each external |
|
| 21 |
# repository: command is called with the name |
|
| 22 |
# of the project, the path to its repo, and |
|
| 23 |
# its external repo url as its three args |
|
| 24 |
# |
|
| 25 |
# == Options |
|
| 26 |
# |
|
| 27 |
# --http-user=USER User for HTTP Basic authentication with Redmine WS |
|
| 28 |
# --http-pass=PASSWORD Password for Basic authentication with Redmine WS |
|
| 29 |
# -t, --test only show what should be done |
|
| 30 |
# -h, --help show help and exit |
|
| 31 |
# -v, --verbose verbose |
|
| 32 |
# -V, --version print version and exit |
|
| 33 |
# -q, --quiet no log |
|
| 34 |
|
|
| 35 |
|
|
| 36 |
require 'getoptlong' |
|
| 37 |
require 'rdoc/usage' |
|
| 38 |
require 'find' |
|
| 39 |
require 'etc' |
|
| 40 |
|
|
| 41 |
Version = "1.0" |
|
| 42 |
|
|
| 43 |
opts = GetoptLong.new( |
|
| 44 |
['--scm-dir', '-s', GetoptLong::REQUIRED_ARGUMENT], |
|
| 45 |
['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT], |
|
| 46 |
['--key', '-k', GetoptLong::REQUIRED_ARGUMENT], |
|
| 47 |
['--http-user', GetoptLong::REQUIRED_ARGUMENT], |
|
| 48 |
['--http-pass', GetoptLong::REQUIRED_ARGUMENT], |
|
| 49 |
['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT], |
|
| 50 |
['--test', '-t', GetoptLong::NO_ARGUMENT], |
|
| 51 |
['--verbose', '-v', GetoptLong::NO_ARGUMENT], |
|
| 52 |
['--version', '-V', GetoptLong::NO_ARGUMENT], |
|
| 53 |
['--help' , '-h', GetoptLong::NO_ARGUMENT], |
|
| 54 |
['--quiet' , '-q', GetoptLong::NO_ARGUMENT] |
|
| 55 |
) |
|
| 56 |
|
|
| 57 |
$verbose = 0 |
|
| 58 |
$quiet = false |
|
| 59 |
$redmine_host = '' |
|
| 60 |
$repos_base = '' |
|
| 61 |
$http_user = '' |
|
| 62 |
$http_pass = '' |
|
| 63 |
$test = false |
|
| 64 |
|
|
| 65 |
$mirrordir = '/var/mirror' |
|
| 66 |
|
|
| 67 |
def log(text, options={})
|
|
| 68 |
level = options[:level] || 0 |
|
| 69 |
puts text unless $quiet or level > $verbose |
|
| 70 |
exit 1 if options[:exit] |
|
| 71 |
end |
|
| 72 |
|
|
| 73 |
def system_or_raise(command) |
|
| 74 |
raise "\"#{command}\" failed" unless system command
|
|
| 75 |
end |
|
| 76 |
|
|
| 77 |
begin |
|
| 78 |
opts.each do |opt, arg| |
|
| 79 |
case opt |
|
| 80 |
when '--scm-dir'; $repos_base = arg.dup |
|
| 81 |
when '--redmine-host'; $redmine_host = arg.dup |
|
| 82 |
when '--key'; $api_key = arg.dup |
|
| 83 |
when '--http-user'; $http_user = arg.dup |
|
| 84 |
when '--http-pass'; $http_pass = arg.dup |
|
| 85 |
when '--command'; $command = arg.dup |
|
| 86 |
when '--verbose'; $verbose += 1 |
|
| 87 |
when '--test'; $test = true |
|
| 88 |
when '--version'; puts Version; exit |
|
| 89 |
when '--help'; RDoc::usage |
|
| 90 |
when '--quiet'; $quiet = true |
|
| 91 |
end |
|
| 92 |
end |
|
| 93 |
rescue |
|
| 94 |
exit 1 |
|
| 95 |
end |
|
| 96 |
|
|
| 97 |
if $test |
|
| 98 |
log("running in test mode")
|
|
| 99 |
end |
|
| 100 |
|
|
| 101 |
if ($redmine_host.empty? or $repos_base.empty? or $command.empty?) |
|
| 102 |
RDoc::usage |
|
| 103 |
end |
|
| 104 |
|
|
| 105 |
unless File.directory?($repos_base) |
|
| 106 |
log("directory '#{$repos_base}' doesn't exist", :exit => true)
|
|
| 107 |
end |
|
| 108 |
|
|
| 109 |
begin |
|
| 110 |
require 'active_resource' |
|
| 111 |
rescue LoadError |
|
| 112 |
log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
|
|
| 113 |
end |
|
| 114 |
|
|
| 115 |
class Project < ActiveResource::Base |
|
| 116 |
self.headers["User-agent"] = "SoundSoftware external repository converter/#{Version}"
|
|
| 117 |
end |
|
| 118 |
|
|
| 119 |
log("querying Redmine for projects...", :level => 1);
|
|
| 120 |
|
|
| 121 |
$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
|
|
| 122 |
$redmine_host.gsub!(/\/$/, '') |
|
| 123 |
|
|
| 124 |
Project.site = "#{$redmine_host}/sys";
|
|
| 125 |
Project.user = $http_user; |
|
| 126 |
Project.password = $http_pass; |
|
| 127 |
|
|
| 128 |
begin |
|
| 129 |
# Get all active projects that have the Repository module enabled |
|
| 130 |
projects = Project.find(:all, :params => {:key => $api_key})
|
|
| 131 |
rescue => e |
|
| 132 |
log("Unable to connect to #{Project.site}: #{e}", :exit => true)
|
|
| 133 |
end |
|
| 134 |
|
|
| 135 |
if projects.nil? |
|
| 136 |
log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true)
|
|
| 137 |
end |
|
| 138 |
|
|
| 139 |
log("retrieved #{projects.size} projects", :level => 1)
|
|
| 140 |
|
|
| 141 |
projects.each do |project| |
|
| 142 |
log("treating project #{project.name}", :level => 1)
|
|
| 143 |
|
|
| 144 |
if project.identifier.empty? |
|
| 145 |
log("\tno identifier for project #{project.name}")
|
|
| 146 |
next |
|
| 147 |
elsif not project.identifier.match(/^[a-z0-9\-]+$/) |
|
| 148 |
log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
|
|
| 149 |
next |
|
| 150 |
end |
|
| 151 |
|
|
| 152 |
if !project.respond_to?(:repository) or !project.repository.is_external? |
|
| 153 |
log("\tproject #{project.identifier} does not use an external repository");
|
|
| 154 |
next |
|
| 155 |
end |
|
| 156 |
|
|
| 157 |
external_url = project.repository.external_url; |
|
| 158 |
log("\tproject #{project.identifier} has external repository url #{external_url}");
|
|
| 159 |
|
|
| 160 |
if !external_url.match(/^[a-z][a-z+]{0,8}[a-z]:\/\//)
|
|
| 161 |
log("\tthis doesn't look like a plausible url to me, skipping")
|
|
| 162 |
next |
|
| 163 |
end |
|
| 164 |
|
|
| 165 |
repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR) |
|
| 166 |
|
|
| 167 |
unless File.directory?(repos_path) |
|
| 168 |
log("\tproject repo directory '#{repos_path}' doesn't exist")
|
|
| 169 |
next |
|
| 170 |
end |
|
| 171 |
|
|
| 172 |
system($command, project.identifier, repos_path, external_url) |
|
| 173 |
|
|
| 174 |
$cache_clearance_file = File.join($mirrordir, project.identifier, 'url_changed') |
|
| 175 |
if File.file?($cache_clearance_file) |
|
| 176 |
log("\tproject repo url has changed, requesting cache clearance")
|
|
| 177 |
if project.post(:repository_cache, :key => $api_key) |
|
| 178 |
File.delete($cache_clearance_file) |
|
| 179 |
end |
|
| 180 |
end |
|
| 181 |
|
|
| 182 |
end |
|
| 183 |
|
|
| extra/soundsoftware/extract-docs.sh | ||
|---|---|---|
| 12 | 12 |
redgrp="redmine" |
| 13 | 13 |
|
| 14 | 14 |
apikey="" |
| 15 |
apischeme="https" |
|
| 15 | 16 |
apihost="" |
| 16 | 17 |
apiuser="" |
| 17 | 18 |
apipass="" |
| ... | ... | |
| 22 | 23 |
*) progdir="$(pwd)/$progdir" ;; |
| 23 | 24 |
esac |
| 24 | 25 |
|
| 25 |
types="doxygen javadoc" # Do Doxygen first (it can be used for Java too) |
|
| 26 |
types="doxygen javadoc matlabdocs" # Do Doxygen first (it can be used for Java too)
|
|
| 26 | 27 |
|
| 27 | 28 |
for x in $types; do |
| 28 | 29 |
if [ ! -x "$progdir/extract-$x.sh" ]; then |
| ... | ... | |
| 36 | 37 |
p="$1" |
| 37 | 38 |
if [ -n "$apikey" ]; then |
| 38 | 39 |
if [ -n "$apiuser" ]; then |
| 39 |
sudo -u docgen curl -u "$apiuser":"$apipass" "http://$apihost/sys/projects/$p/embedded.xml?enable=1&key=$apikey" -d ""
|
|
| 40 |
sudo -u docgen curl -u "$apiuser":"$apipass" "$apischeme://$apihost/sys/projects/$p/embedded.xml?enable=1&key=$apikey" -d ""
|
|
| 40 | 41 |
else |
| 41 |
sudo -u docgen curl "http://$apihost/sys/projects/$p/embedded.xml?enable=1&key=$apikey" -d ""
|
|
| 42 |
sudo -u docgen curl "$apischeme://$apihost/sys/projects/$p/embedded.xml?enable=1&key=$apikey" -d ""
|
|
| 42 | 43 |
fi |
| 43 | 44 |
else |
| 44 | 45 |
echo "Can't enable Embedded, API not configured" 1>&2 |
| extra/soundsoftware/extract-matlabdocs.sh | ||
|---|---|---|
| 1 |
#!/bin/bash |
|
| 2 |
|
|
| 3 |
docdir="/var/doc" |
|
| 4 |
|
|
| 5 |
progdir=$(dirname $0) |
|
| 6 |
case "$progdir" in |
|
| 7 |
/*) ;; |
|
| 8 |
*) progdir="$(pwd)/$progdir" ;; |
|
| 9 |
esac |
|
| 10 |
|
|
| 11 |
project="$1" |
|
| 12 |
projectdir="$2" |
|
| 13 |
targetdir="$3" |
|
| 14 |
|
|
| 15 |
if [ -z "$project" ] || [ -z "$targetdir" ] || [ -z "$projectdir" ]; then |
|
| 16 |
echo "Usage: $0 <project> <projectdir> <targetdir>" |
|
| 17 |
exit 2 |
|
| 18 |
fi |
|
| 19 |
|
|
| 20 |
if [ ! -d "$projectdir" ]; then |
|
| 21 |
echo "Project directory $projectdir not found" |
|
| 22 |
exit 1 |
|
| 23 |
fi |
|
| 24 |
|
|
| 25 |
if [ ! -d "$targetdir" ]; then |
|
| 26 |
echo "Target dir $targetdir not found" |
|
| 27 |
exit 1 |
|
| 28 |
fi |
|
| 29 |
|
|
| 30 |
if [ -f "$targetdir/index.html" ]; then |
|
| 31 |
echo "Target dir $targetdir already contains index.html" |
|
| 32 |
exit 1 |
|
| 33 |
fi |
|
| 34 |
|
|
| 35 |
mfile=$(find "$projectdir" -type f -name \*.m -print0 | xargs -0 grep -l '^% ' | head -1) |
|
| 36 |
|
|
| 37 |
if [ -z "$mfile" ]; then |
|
| 38 |
echo "No MATLAB files with comments found for project $project" |
|
| 39 |
exit 1 |
|
| 40 |
fi |
|
| 41 |
|
|
| 42 |
echo "Project $project contains at least one MATLAB file with comments" |
|
| 43 |
|
|
| 44 |
cd "$projectdir" || exit 1 |
|
| 45 |
|
|
| 46 |
perl "$progdir/matlab-docs.pl" -c "$progdir/matlab-docs.conf" -d "$targetdir" |
|
| 47 |
|
|
| extra/soundsoftware/matlab-docs-credit.html | ||
|---|---|---|
| 1 |
<div style="clear: both; float: right"><small><i>Produced by mtree2html by Hartmut Pohlheim</i></small></div> |
|
| extra/soundsoftware/matlab-docs.conf | ||
|---|---|---|
| 1 |
# configuration file for generation of html-docu from m-files |
|
| 2 |
# |
|
| 3 |
# Author: Hartmut Pohlheim |
|
| 4 |
# History: 05.11.2000 file created (parameters for mtree2html2001) |
|
| 5 |
# |
|
| 6 |
# The following options/variables must be changed/adapted: |
|
| 7 |
# dirmfiles |
|
| 8 |
# dirhtml |
|
| 9 |
# csslink |
|
| 10 |
# texttitleframelayout |
|
| 11 |
# texttitlefiles |
|
| 12 |
# |
|
| 13 |
# The following options/variables should be adapted: |
|
| 14 |
# authorfile |
|
| 15 |
# filenametopframe |
|
| 16 |
# codeheadmeta |
|
| 17 |
|
|
| 18 |
#======================================================================== |
|
| 19 |
# Variables (possible keywords: set) |
|
| 20 |
# to use the built-in settings, comment the line using # in first column |
|
| 21 |
#======================================================================== |
|
| 22 |
|
|
| 23 |
#------------------------------------------------------------------------ |
|
| 24 |
# dirmfiles: name of directory containing Matlab m-files |
|
| 25 |
# dirhtml: name of directory to place the html-files into |
|
| 26 |
# exthtml: extension used for the html files (.html or .htm) |
|
| 27 |
# don't forget the point in front of the extension |
|
| 28 |
#------------------------------------------------------------------------ |
|
| 29 |
set dirmfiles = . |
|
| 30 |
set dirhtml = doc-output |
|
| 31 |
set exthtml = .html |
|
| 32 |
|
|
| 33 |
#------------------------------------------------------------------------ |
|
| 34 |
# authorfile: name of file containing info about author (in html) |
|
| 35 |
# if defined, this text is included at the bottom of the |
|
| 36 |
# html files |
|
| 37 |
#------------------------------------------------------------------------ |
|
| 38 |
set authorfile = matlab-docs-credit.html |
|
| 39 |
|
|
| 40 |
#------------------------------------------------------------------------ |
|
| 41 |
# csslink: text for linking to css file (style sheets) |
|
| 42 |
# the text defined here is directly included into the head |
|
| 43 |
# of the html file |
|
| 44 |
#------------------------------------------------------------------------ |
|
| 45 |
#set csslink = <link rel=stylesheet type="text/css" href="CSSFILENAME.css" /> |
|
| 46 |
|
|
| 47 |
#------------------------------------------------------------------------ |
|
| 48 |
# links2filescase: this is a bit difficult |
|
| 49 |
# Matlab is case sensitive on UNIX, but case insensitive |
|
| 50 |
# on Windows. Under UNIX Matlab function calls work |
|
| 51 |
# only, when the case of file name and function call are |
|
| 52 |
# identical, under Windows you can do what you want. |
|
| 53 |
# This scripts help you, to keep an exact case in your |
|
| 54 |
# project. |
|
| 55 |
# exact - internal links are only generated, when case of file |
|
| 56 |
# name and in source code are identical |
|
| 57 |
# all - case doesn't matter |
|
| 58 |
# exactupper - same as exact, additionally links are also vreated to |
|
| 59 |
# all upper case function names in source code (often |
|
| 60 |
# used by Mathworks) |
|
| 61 |
# exactvery - same as exact, additionally info about not matching |
|
| 62 |
# case is written to screen (stdout), this can be very |
|
| 63 |
# helpful in cleaning up the case in a project |
|
| 64 |
#------------------------------------------------------------------------ |
|
| 65 |
set links2filescase = all |
|
| 66 |
|
|
| 67 |
#------------------------------------------------------------------------ |
|
| 68 |
# texttitleframelayout: text of title for frame layout file (whole docu) |
|
| 69 |
#------------------------------------------------------------------------ |
|
| 70 |
set texttitleframelayout = MATLAB Function Documentation |
|
| 71 |
|
|
| 72 |
#------------------------------------------------------------------------ |
|
| 73 |
# texttitle/headerindexalldirs: text of title and header for directory index |
|
| 74 |
#------------------------------------------------------------------------ |
|
| 75 |
set texttitleindexalldirs = Index of Directories |
|
| 76 |
set textheaderindexalldirs = Index of Directories |
|
| 77 |
|
|
| 78 |
#------------------------------------------------------------------------ |
|
| 79 |
# texttitle/headerindex: text of title and header for index file |
|
| 80 |
#------------------------------------------------------------------------ |
|
| 81 |
set texttitleindex = A-Z Index of Functions |
|
| 82 |
set textheaderindex = A-Z Index of Functions |
|
| 83 |
|
|
| 84 |
#------------------------------------------------------------------------ |
|
| 85 |
# texttitle/headerfiles: text of title and header for files |
|
| 86 |
# name of file will be added at the end |
|
| 87 |
#------------------------------------------------------------------------ |
|
| 88 |
set texttitlefiles = Function |
|
| 89 |
set textheaderfiles = Documentation of |
|
| 90 |
|
|
| 91 |
#------------------------------------------------------------------------ |
|
| 92 |
# frames: whether to use frames in layout (yes or no) |
|
| 93 |
#------------------------------------------------------------------------ |
|
| 94 |
set frames = no |
|
| 95 |
|
|
| 96 |
#------------------------------------------------------------------------ |
|
| 97 |
# filenametopframe: name of file including frame layout (highest level file) |
|
| 98 |
# [default: index] |
|
| 99 |
#------------------------------------------------------------------------ |
|
| 100 |
set filenametopframe = index |
|
| 101 |
|
|
| 102 |
#------------------------------------------------------------------------ |
|
| 103 |
# textjumpindexglobal: text displayed for jump to index of all files |
|
| 104 |
# (global) |
|
| 105 |
# textjumpindexlocal: text displayed for jump to index of files in actual |
|
| 106 |
# directory (local) |
|
| 107 |
#------------------------------------------------------------------------ |
|
| 108 |
set textjumpindexglobal = <b>Index of</b> all files: |
|
| 109 |
set textjumpindexlocal = this subdirectory only: |
|
| 110 |
|
|
| 111 |
#------------------------------------------------------------------------ |
|
| 112 |
# includesource: include source of m-files in documentation [YES|no] |
|
| 113 |
#------------------------------------------------------------------------ |
|
| 114 |
set includesource = yes |
|
| 115 |
|
|
| 116 |
#------------------------------------------------------------------------ |
|
| 117 |
# usecontentsm: use contents.m files as well for structured |
|
| 118 |
# (hopefully) index [YES|no] |
|
| 119 |
#------------------------------------------------------------------------ |
|
| 120 |
set usecontentsm = no |
|
| 121 |
|
|
| 122 |
#------------------------------------------------------------------------ |
|
| 123 |
# includesource: write/update contents.m files [yes|NO] |
|
| 124 |
#------------------------------------------------------------------------ |
|
| 125 |
set writecontentsm = no |
|
| 126 |
|
|
| 127 |
#------------------------------------------------------------------------ |
|
| 128 |
# processtree: parse whole directory tree recursively [YES|no] |
|
| 129 |
#------------------------------------------------------------------------ |
|
| 130 |
set processtree = yes |
|
| 131 |
|
|
| 132 |
#------------------------------------------------------------------------ |
|
| 133 |
# producetree: produce tree for html-files in same structure than |
|
| 134 |
# tree of m-files [yes|NO] |
|
| 135 |
# if no, all files are saved in the same directory, often |
|
| 136 |
# easier for outside linking to files |
|
| 137 |
#------------------------------------------------------------------------ |
|
| 138 |
set producetree = yes |
|
| 139 |
|
|
| 140 |
#------------------------------------------------------------------------ |
|
| 141 |
# codebodyindex/files: HTML-code for adding to BODY tag |
|
| 142 |
# can be used for defining colors and |
|
| 143 |
# backgroundimages of the files |
|
| 144 |
# No longer recommended, use the css file |
|
| 145 |
#------------------------------------------------------------------------ |
|
| 146 |
set codebodyindex = |
|
| 147 |
set codebodyfiles = |
|
| 148 |
|
|
| 149 |
#------------------------------------------------------------------------ |
|
| 150 |
# codeheadmeta: HTML-code added in HEAD area, use for supplying META info |
|
| 151 |
#------------------------------------------------------------------------ |
|
| 152 |
set codeheadmeta = |
|
| 153 |
|
|
| 154 |
#------------------------------------------------------------------------ |
|
| 155 |
# codehr: HTML-code used to define a <HR>, do what you want |
|
| 156 |
#------------------------------------------------------------------------ |
|
| 157 |
set codehr = <hr> |
|
| 158 |
|
|
| 159 |
#------------------------------------------------------------------------ |
|
| 160 |
# codeheader: HTML-code added to <H*> tags, use for centering header text |
|
| 161 |
# or changing the colour/size/font of the header text |
|
| 162 |
#------------------------------------------------------------------------ |
|
| 163 |
set codeheader = |
|
| 164 |
|
|
| 165 |
|
|
| 166 |
# End of parameter file |
|
| extra/soundsoftware/matlab-docs.pl | ||
|---|---|---|
| 1 |
@rem = '--*-Perl-*--'; |
|
| 2 |
@rem = ' |
|
| 3 |
@echo off |
|
| 4 |
perl -w -S %0.bat %1 %2 %3 %4 %5 %6 %7 %8 %9 |
|
| 5 |
goto endofperl |
|
| 6 |
@rem '; |
|
| 7 |
# perl -w -S %0.bat "$@" |
|
| 8 |
#!/usr/bin/perl |
|
| 9 |
# |
|
| 10 |
# mtree2html_2000 - produce html files from Matlab m-files. |
|
| 11 |
# use configuration file for flexibility |
|
| 12 |
# can process tree of directories |
|
| 13 |
# |
|
| 14 |
# Copyright (C) 1996-2000 Hartmut Pohlheim. All rights reserved. |
|
| 15 |
# includes small parts of m2html from Jeffrey C. Kantor 1995 |
|
| 16 |
# |
|
| 17 |
# Author: Hartmut Pohlheim |
|
| 18 |
# History: 06.03.1996 file created |
|
| 19 |
# 07.03.1996 first working version |
|
| 20 |
# 08.03.1996 modularized, help text only once included |
|
| 21 |
# 11.03.1996 clean up, some functions rwritten |
|
| 22 |
# 18.04.1996 silent output with writing in one line only |
|
| 23 |
# version 0.20 fixed |
|
| 24 |
# 14.05.1996 start of adding tree structure, could create tree |
|
| 25 |
# 15.05.1996 creating of index files for every directory |
|
| 26 |
# 17.05.1996 first working version except compact A-Z index |
|
| 27 |
# 20.05.1996 cleanup of actual version, more variables and |
|
| 28 |
# configurable settings |
|
| 29 |
# 21.05.1996 reading, update and creation of contents.m added |
|
| 30 |
# 22.05.1996 creation of short index started |
|
| 31 |
# 28.05.1996 jump letters for short index, |
|
| 32 |
# 3 different directory indexes (short/long/contents) |
|
| 33 |
# 29.05.1996 major cleanup, short and long index created from one function |
|
| 34 |
# links for HTML and Indexes from 1 function, |
|
| 35 |
# version 0.9 |
|
| 36 |
# 30.05.1996 contents.m changed to Contents.m (because unix likes it) |
|
| 37 |
# function definition can be in first line of m file before comments |
|
| 38 |
# version 0.91 fixed |
|
| 39 |
# 03.06.1996 contents file can be written as wanted, the links will be correct |
|
| 40 |
# cross references in help block of m-file will be found and |
|
| 41 |
# converted, even if the name of the function is written upper case |
|
| 42 |
# version 0.92 fixed |
|
| 43 |
# 05.06.1996 construction of dependency matrix changed, is able now to process |
|
| 44 |
# even the whole matlab tree (previous version needed to much memory) |
|
| 45 |
# removed warning for contents files in different directories |
|
| 46 |
# version 0.94 fixed |
|
| 47 |
# 06.06.1996 new link name matrices for ConstructHTMLFile created, |
|
| 48 |
# everything is done in ConstructDependencyMatrix, |
|
| 49 |
# both dependencies (calls and called) and matrix |
|
| 50 |
# with all mentioned names in this m-file, thus, much |
|
| 51 |
# less scanning in html construction |
|
| 52 |
# script is now (nearly) linear scalable, thus, matlab-toolbox |
|
| 53 |
# tree takes less than 1 hour on a Pentium120, with source |
|
| 54 |
# version 0.96 fixed |
|
| 55 |
# 10.06.1996 order of creation changed, first all indexes (includes |
|
| 56 |
# update/creation of contents.m) and then ConstructDepency |
|
| 57 |
# thus, AutoAdd section will be linked as well |
|
| 58 |
# excludenames extended, some more common word function names added |
|
| 59 |
# version 0.97 fixed |
|
| 60 |
# 17.02.1998 writecontentsm as command line parameter added |
|
| 61 |
# error of file not found will even appear when silent |
|
| 62 |
# version 1.02 |
|
| 63 |
# 21.05.2000 mark comments in source code specially (no fully correct, |
|
| 64 |
# can't handle % in strings) |
|
| 65 |
# version 1.11 |
|
| 66 |
# 05.11.2000 link also to upper and mixed case m-files |
|
| 67 |
# searching for .m files now really works (doesn't find grep.com any longer) |
|
| 68 |
# file renamed to mtree2html2001 |
|
| 69 |
# generated html code now all lower case |
|
| 70 |
# inclusion of meta-description and meta-keywords in html files |
|
| 71 |
# HTML4 compliance done (should be strict HTML4.0, quite near XHTML) |
|
| 72 |
# version 1.23 |
|
| 73 |
# |
|
| 74 |
# 29.03.2011 (Chris Cannam) add frames option. |
|
| 75 |
|
|
| 76 |
$VERSION = '1.23'; |
|
| 77 |
($PROGRAM = $0) =~ s@.*/@@; $PROGRAM = "\U$PROGRAM\E"; |
|
| 78 |
$debug = 1; |
|
| 79 |
|
|
| 80 |
#------------------------------------------------------------------------ |
|
| 81 |
# Define platform specific things |
|
| 82 |
#------------------------------------------------------------------------ |
|
| 83 |
# suffix for files to search is defined twice |
|
| 84 |
# the first ($suffix) is for string creation and contains the . as well |
|
| 85 |
# the second ($suffixforsearch) is for regular expression, handling of . is quite special |
|
| 86 |
$suffix = ".m"; |
|
| 87 |
$suffixforsearch = "m"; |
|
| 88 |
# the directory separator |
|
| 89 |
$dirsep = "/"; |
|
| 90 |
# what is the current directory |
|
| 91 |
$diract = "."; |
|
| 92 |
|
|
| 93 |
#------------------------------------------------------------------------ |
|
| 94 |
# Define all variables and their standard settings |
|
| 95 |
# documentation of variables is contained in accompanying rc file |
|
| 96 |
#------------------------------------------------------------------------ |
|
| 97 |
%var = |
|
| 98 |
( |
|
| 99 |
'authorfile', '', |
|
| 100 |
'codebodyfiles', '', |
|
| 101 |
'codebodyindex', '', |
|
| 102 |
'codeheadmeta', '<meta name="author of conversion perl script" content="Hartmut Pohlheim" />', |
|
| 103 |
'codehr', '<hr size="3" noshade="noshade" />', |
|
| 104 |
'codeheader', '', |
|
| 105 |
'configfile', 'matlab-docs.conf', |
|
| 106 |
'csslink', '', |
|
| 107 |
'dirmfiles', $diract, |
|
| 108 |
'dirhtml', $diract, |
|
| 109 |
'exthtml', '.html', |
|
| 110 |
'frames', 'yes', |
|
| 111 |
'filenametopframe', 'index', |
|
| 112 |
'filenameindexlongglobal', 'indexlg', |
|
| 113 |
'filenameindexlonglocal', 'indexll', |
|
| 114 |
'filenameindexshortglobal', 'indexsg', |
|
| 115 |
'filenameindexshortlocal', 'indexsl', |
|
| 116 |
'filenameextensionframe', 'f', |
|
| 117 |
'filenameextensionindex', 'i', |
|
| 118 |
'filenameextensionjump', 'j', |
|
| 119 |
'filenamedirshort', 'dirtops', |
|
| 120 |
'filenamedirlong', 'dirtopl', |
|
| 121 |
'filenamedircontents', 'dirtopc', |
|
| 122 |
'includesource', 'yes', |
|
| 123 |
'links2filescase', 'all', |
|
| 124 |
'processtree', 'yes', |
|
| 125 |
'producetree', 'yes', |
|
| 126 |
'textjumpindexlocal', 'Local Index', |
|
| 127 |
'textjumpindexglobal', 'Global Index', |
|
| 128 |
'texttitleframelayout', 'Documentation of Matlab Files', |
|
| 129 |
'texttitleindexalldirs', 'Index of Directories', |
|
| 130 |
'textheaderindexalldirs', 'Index of Directories', |
|
| 131 |
'texttitleindex', '', |
|
| 132 |
'textheaderindex', '', |
|
| 133 |
'texttitlefiles', 'Documentation of ', |
|
| 134 |
'textheaderfiles', 'Documentation of ', |
|
| 135 |
'usecontentsm', 'yes', |
|
| 136 |
'writecontentsm', 'no' |
|
| 137 |
); |
|
| 138 |
|
|
| 139 |
|
|
| 140 |
# define all m-file names, that should be excluded from linking |
|
| 141 |
# however, files will still be converted |
|
| 142 |
@excludenames = ( 'all','ans','any','are', |
|
| 143 |
'cs', |
|
| 144 |
'demo','dos', |
|
| 145 |
'echo','edit','else','elseif','end','exist', |
|
| 146 |
'flag','for','function', |
|
| 147 |
'global', |
|
| 148 |
'help', |
|
| 149 |
'i','if','inf','info', |
|
| 150 |
'j', |
|
| 151 |
'more', |
|
| 152 |
'null', |
|
| 153 |
'return', |
|
| 154 |
'script','strings', |
|
| 155 |
'what','which','while','who','whos','why', |
|
| 156 |
); |
|
| 157 |
|
|
| 158 |
# Text for inclusion in created HTML/Frame files: Doctype and Charset |
|
| 159 |
$TextDocTypeHTML = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">'; |
|
| 160 |
$TextDocTypeFrame = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">'; |
|
| 161 |
$TextMetaCharset = '<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />'; |
|
| 162 |
|
|
| 163 |
#------------------------------------------------------------------------ |
|
| 164 |
# Read the command line arguments |
|
| 165 |
#------------------------------------------------------------------------ |
|
| 166 |
if (@ARGV == 0) {
|
|
| 167 |
&DisplayHelp() if &CheckFileName($var{'configfile'}, 'configuration file');
|
|
| 168 |
} |
|
| 169 |
|
|
| 170 |
# Print provided command line arguments on screen |
|
| 171 |
foreach (@ARGV) { print " $_\n "; }
|
|
| 172 |
|
|
| 173 |
# Get the options |
|
| 174 |
use Getopt::Long; |
|
| 175 |
@options = ('help|h', 'todo|t', 'version|v',
|
|
| 176 |
'authorfile|a=s', 'configfile|c=s', 'dirhtml|html|d=s', |
|
| 177 |
'dirmfiles|mfiles|m=s', 'includesource|i=s', |
|
| 178 |
'processtree|r=s', 'producetree|p=s', |
|
| 179 |
'silent|quiet|q', 'writecontentsm|w=s'); |
|
| 180 |
&GetOptions(@options) || die "use -h switch to display help statement\n"; |
|
| 181 |
|
|
| 182 |
|
|
| 183 |
# Display help or todo list, when requested |
|
| 184 |
&DisplayHelp() if $opt_help; |
|
| 185 |
&DisplayTodo() if $opt_todo; |
|
| 186 |
die "$PROGRAM v$VERSION\n" if $opt_version; |
|
| 187 |
|
|
| 188 |
$exit_status = 0; |
|
| 189 |
|
|
| 190 |
#------------------------------------------------------------------------ |
|
| 191 |
# Read the config file |
|
| 192 |
#------------------------------------------------------------------------ |
|
| 193 |
$var{'configfile'} = $opt_configfile if $opt_configfile;
|
|
| 194 |
&GetConfigFile($var{'configfile'});
|
|
| 195 |
|
|
| 196 |
|
|
| 197 |
#------------------------------------------------------------------------ |
|
| 198 |
# Process/Check the command line otions |
|
| 199 |
#------------------------------------------------------------------------ |
|
| 200 |
$var{'dirhtml'} = $opt_dirhtml if $opt_dirhtml;
|
|
| 201 |
if (!(substr($var{'dirhtml'}, -1, 1) eq $dirsep)) { $var{'dirhtml'} = $var{'dirhtml'}.$dirsep; }
|
|
| 202 |
$var{'dirmfiles'} = $opt_dirmfiles if $opt_dirmfiles;
|
|
| 203 |
if (!(substr($var{'dirmfiles'}, -1, 1) eq $dirsep)) { $var{'dirmfiles'} = $var{'dirmfiles'}.$dirsep; }
|
|
| 204 |
|
|
| 205 |
$var{'authorfile'} = $opt_author if $opt_author;
|
|
| 206 |
$var{'includesource'} = $opt_includesource if $opt_includesource;
|
|
| 207 |
if ($var{'includesource'} ne 'no') { $var{'includesource'} = 'yes'; }
|
|
| 208 |
$var{'processtree'} = $opt_processtree if $opt_processtree;
|
|
| 209 |
if ($var{'processtree'} ne 'no') { $var{'processtree'} = 'yes'; }
|
|
| 210 |
$var{'producetree'} = $opt_producetree if $opt_producetree;
|
|
| 211 |
if ($var{'producetree'} ne 'no') { $var{'producetree'} = 'yes'; }
|
|
| 212 |
if ($var{'processtree'} eq 'no') { $var{'producetree'} = 'no'; }
|
|
| 213 |
if ($var{'frames'} ne 'no') { $var{'frames'} = 'yes'; }
|
|
| 214 |
# if (($var{'processtree'} eq 'yes') && ($var{'producetree'} eq 'no')) { $var{'usecontentsm'} = 'no'; }
|
|
| 215 |
|
|
| 216 |
$var{'writecontentsm'} = $opt_writecontentsm if $opt_writecontentsm;
|
|
| 217 |
|
|
| 218 |
#------------------------------------------------------------------------ |
|
| 219 |
# Do the real stuff |
|
| 220 |
#------------------------------------------------------------------------ |
|
| 221 |
|
|
| 222 |
# Print variables on screen, when not silent |
|
| 223 |
&ListVariables if !$opt_silent; |
|
| 224 |
|
|
| 225 |
# Check the author file |
|
| 226 |
if ($var{'authorfile'} ne '') {
|
|
| 227 |
if (!($var{'authorfile'} =~ m,^/,)) {
|
|
| 228 |
# relative path: treat as relative to config file |
|
| 229 |
my $cfd = $var{'configfile'};
|
|
| 230 |
$cfd =~ s,/[^/]*$,/,; |
|
| 231 |
$cfd =~ s,^[^/]*$,.,; |
|
| 232 |
$var{'authorfile'} = "$cfd/" . $var{'authorfile'};
|
|
| 233 |
} |
|
| 234 |
if (&CheckFileName($var{'authorfile'}, 'author file')) {
|
|
| 235 |
$var{'authorfile'} = '';
|
|
| 236 |
if (!$opt_silent) { print " Proceeding without author information!\n"; }
|
|
| 237 |
} |
|
| 238 |
} |
|
| 239 |
|
|
| 240 |
# Call the function doing all the real work |
|
| 241 |
&ConstructNameMatrix; |
|
| 242 |
|
|
| 243 |
&ConstructDependencyMatrix; |
|
| 244 |
|
|
| 245 |
&ConstructAllIndexFiles; |
|
| 246 |
|
|
| 247 |
&ConstructHTMLFiles; |
|
| 248 |
|
|
| 249 |
exit $exit_status; |
|
| 250 |
|
|
| 251 |
#------------------------------------------------------------------------ |
|
| 252 |
# Construct list of all mfile names and initialize various data arrays. |
|
| 253 |
#------------------------------------------------------------------------ |
|
| 254 |
sub ConstructNameMatrix |
|
| 255 |
{
|
|
| 256 |
local(*MFILE); |
|
| 257 |
local($file, $dirname); |
|
| 258 |
local(@newdirectories); |
|
| 259 |
local(%localnames); |
|
| 260 |
|
|
| 261 |
$RecDeep = 0; |
|
| 262 |
&ParseTreeReadFiles($var{'dirmfiles'}, $RecDeep);
|
|
| 263 |
|
|
| 264 |
foreach $dirname (@directories) {
|
|
| 265 |
if ($dirnumbermfiles{$dirname} > 0) {
|
|
| 266 |
push(@newdirectories, $dirname); |
|
| 267 |
if (! defined($contentsname{$dirname})) {
|
|
| 268 |
$contentsname{$dirname} = 'Contents';
|
|
| 269 |
if (($var{'writecontentsm'} eq 'no') && ($var{'usecontentsm'} eq 'yes')) {
|
|
| 270 |
print "\r ParseTree - for directory $dirname no contents file found!\n"; |
|
| 271 |
print " create one or enable writing of contents file (writecontentsm = yes)!\n"; |
|
| 272 |
} |
|
| 273 |
} |
|
| 274 |
} |
|
| 275 |
} |
|
| 276 |
@alldirectories = @directories; |
|
| 277 |
@directories = @newdirectories; |
|
| 278 |
|
|
| 279 |
foreach $dirname (@directories) {
|
|
| 280 |
if ($debug > 0) { print "Dir: $dirname \t\t $dirnumbermfiles{$dirname} \t$contentsname{$dirname}\n"; }
|
|
| 281 |
} |
|
| 282 |
|
|
| 283 |
@names = sort(keys %mfile); |
|
| 284 |
|
|
| 285 |
# check, if name of directory is identical to name of file |
|
| 286 |
@dirsinglenames = values(%dirnamesingle); |
|
| 287 |
grep($localnames{$_}++, @dirsinglenames);
|
|
| 288 |
@dirandfilename = grep($localnames{$_}, @names);
|
|
| 289 |
if (@dirandfilename) {
|
|
| 290 |
print "\r Name clash between directory and file name: @dirandfilename\n"; |
|
| 291 |
print " These files will be excluded from linking!\n"; |
|
| 292 |
push(@excludenames, @dirandfilename); |
|
| 293 |
} |
|
| 294 |
|
|
| 295 |
# construct names matrix for help text linking |
|
| 296 |
# exclude some common words (and at the same time m-functions) from linking in help text |
|
| 297 |
grep($localnames{$_}++, @excludenames);
|
|
| 298 |
@linknames = grep(!$localnames{$_}, @names);
|
|
| 299 |
|
|
| 300 |
if ($debug > 2) { print "linknames (names of found m-files):\n @linknames\n"; }
|
|
| 301 |
|
|
| 302 |
} |
|
| 303 |
|
|
| 304 |
#------------------------------------------------------------------------ |
|
| 305 |
# Parse tree and collect all Files |
|
| 306 |
#------------------------------------------------------------------------ |
|
| 307 |
sub ParseTreeReadFiles |
|
| 308 |
{
|
|
| 309 |
local($dirname, $localRecDeep) = @_; |
|
| 310 |
local($file, $name, $filewosuffix); |
|
| 311 |
local($dirhtmlname, $dirmode); |
|
| 312 |
local($relpath, $relpathtoindex, $replacevardir); |
|
| 313 |
local(*CHECKDIR, *AKTDIR); |
|
| 314 |
local(@ALLEFILES); |
|
| 315 |
|
|
| 316 |
opendir(AKTDIR, $dirname) || die "ParseTree - Can't open directory $dirname: $!"; |
|
| 317 |
if ($debug > 1) { print "\nDirectory: $dirname\n"; }
|
|
| 318 |
|
|
| 319 |
# create relative path |
|
| 320 |
$_ = $dirname; $replacevardir = $var{'dirmfiles'};
|
|
| 321 |
s/$replacevardir//; $relpath = $_; |
|
| 322 |
s/[^\/]+/../g; $relpathtoindex = $_; |
|
| 323 |
|
|
| 324 |
# producetree no |
|
| 325 |
if ($var{'producetree'} eq 'no') { $relpath = ''; $relpathtoindex = ''; }
|
|
| 326 |
|
|
| 327 |
# names of directories (top-level and below top-level m-file-directory) |
|
| 328 |
push(@directories, $dirname); |
|
| 329 |
$dirnumbermfiles{$dirname} = 0; # set number of m-files for this dir to zero
|
|
| 330 |
# relative path from top-level directory, depends on directory name |
|
| 331 |
$dirnamerelpath{$dirname} = $relpath;
|
|
| 332 |
# relative path from actual directory to top-level directory, depends on directory name |
|
| 333 |
$dirnamerelpathtoindex{$dirname} = $relpathtoindex;
|
|
| 334 |
# recursion level for directory, depends on directory name |
|
| 335 |
$dirnamerecdeep{$dirname} = $localRecDeep;
|
|
| 336 |
|
|
| 337 |
# only the name of the directory, without path |
|
| 338 |
$rindexprint = rindex($dirname, $dirsep, length($dirname)-2); |
|
| 339 |
$rindsub = substr($dirname, $rindexprint+1, length($dirname)-$rindexprint-2); |
|
| 340 |
$dirnamesingle{$dirname} = $rindsub;
|
|
| 341 |
|
|
| 342 |
# create name of html-directories |
|
| 343 |
$_ = $dirname; |
|
| 344 |
s/$var{'dirmfiles'}/$var{'dirhtml'}/;
|
|
| 345 |
$dirhtmlname = $_; |
|
| 346 |
if ($var{'producetree'} eq 'no') { $dirhtmlname = $var{'dirhtml'}; }
|
|
| 347 |
# try to open html directory, if error, then create directory, |
|
| 348 |
# use same mode as for corresponding m-file directory |
|
| 349 |
opendir(CHECKDIR,"$dirhtmlname") || do {
|
|
| 350 |
$dirmode = (stat($dirname))[2]; # print "$dirmode\n"; |
|
| 351 |
mkdir("$dirhtmlname", $dirmode) || die ("Cannot create directory $dirhtmlname: $! !");
|
|
| 352 |
}; |
|
| 353 |
closedir(CHECKDIR); |
|
| 354 |
|
|
| 355 |
|
|
| 356 |
# read everything from this directory and process them |
|
| 357 |
@ALLEFILES = readdir(AKTDIR); |
|
| 358 |
|
|
| 359 |
foreach $file (@ALLEFILES) {
|
|
| 360 |
# exclude . and .. directories |
|
| 361 |
next if $file eq '.'; next if $file eq '..'; |
|
| 362 |
|
|
| 363 |
# test for existense of entry (redundant, used for debugging) |
|
| 364 |
if (-e $dirname.$file) {
|
|
| 365 |
# if it's a directory, call this function recursively |
|
| 366 |
if (-d $dirname.$file) {
|
|
| 367 |
if ($var{'processtree'} eq 'yes') {
|
|
| 368 |
&ParseTreeReadFiles($dirname.$file.$dirsep, $localRecDeep+1); |
|
| 369 |
} |
|
| 370 |
} |
|
| 371 |
# if it's a file - test for m-file, save name and create some arrays |
|
| 372 |
elsif (-f $dirname.$file) {
|
|
| 373 |
if ($file =~ /\.$suffixforsearch$/i) {
|
|
| 374 |
# Remove the file suffix to establish the matlab identifiers |
|
| 375 |
$filewosuffix = $file; |
|
| 376 |
$filewosuffix =~ s/\.$suffixforsearch$//i; |
|
| 377 |
# $filename = $name; |
|
| 378 |
|
|
| 379 |
# Contents file in unix must start with a capital letter (Contents.m) |
|
| 380 |
# ensure, that m-file name is lower case, except the contents file |
|
| 381 |
if (! ($filewosuffix =~ /^contents$/i)) {
|
|
| 382 |
# if ($var{'links2filescase'} eq 'low') { $filewosuffix = "\L$filewosuffix\E"; }
|
|
| 383 |
$filewosuffixlow = "\L$filewosuffix\E"; |
|
| 384 |
} |
|
| 385 |
else { $contentsname{$dirname} = $filewosuffix; }
|
|
| 386 |
|
|
| 387 |
# internal handle name is always lower case |
|
| 388 |
$name = $filewosuffixlow; |
|
| 389 |
# file name is not lower case |
|
| 390 |
$filename = $filewosuffix; |
|
| 391 |
|
|
| 392 |
# if don't use C|contents.m, then forget all C|contents.m |
|
| 393 |
if ($var{'usecontentsm'} eq 'no') { if ($name =~ /contents/i) { next; } }
|
|
| 394 |
|
|
| 395 |
# if m-file with this name already exists, use directory and name for name |
|
| 396 |
# only the first occurence of name will be used for links |
|
| 397 |
if (defined $mfile{$name}) {
|
|
| 398 |
if (! ($name =~ /^contents$/i) ) {
|
|
| 399 |
print "\r ParseTree - Name conflict: $name in $dirname already exists: $mfile{$name} !\n";
|
|
| 400 |
print " $mfile{$name} will be used for links!\n";
|
|
| 401 |
} |
|
| 402 |
$name = $dirname.$name; |
|
| 403 |
} |
|
| 404 |
# mfile name with path |
|
| 405 |
$mfile{$name} = $dirname.$file;
|
|
| 406 |
# mfile name (without path) |
|
| 407 |
$mfilename{$name} = $filename;
|
|
| 408 |
# mfile directory |
|
| 409 |
$mfiledir{$name} = $dirname;
|
|
| 410 |
|
|
| 411 |
# html file name and full path, special extension of Contents files |
|
| 412 |
if ($name =~ /contents/i) { $extrahtmlfilename = $dirnamesingle{$dirname}; }
|
|
| 413 |
else { $extrahtmlfilename = ''; }
|
|
| 414 |
$hfile{$name} = $dirhtmlname.$mfilename{$name}.$extrahtmlfilename.$var{'exthtml'};
|
|
| 415 |
|
|
| 416 |
# save relative html path |
|
| 417 |
# if ($var{'producetree'} eq 'yes') {
|
|
| 418 |
$hfilerelpath{$name} = $relpath;
|
|
| 419 |
# } else { # if no tree to produce, relative path is empty
|
|
| 420 |
# $hfilerelpath{$name} = '';
|
|
| 421 |
# } |
|
| 422 |
|
|
| 423 |
# create relative path from html file to directory with global index file |
|
| 424 |
$hfileindexpath{$name} = $relpathtoindex;
|
|
| 425 |
|
|
| 426 |
# Function declaration, if one exists, set default to script |
|
| 427 |
$synopsis{$name} = "";
|
|
| 428 |
$mtype{$name} = "script";
|
|
| 429 |
|
|
| 430 |
# First comment line |
|
| 431 |
$apropos{$name} = "";
|
|
| 432 |
|
|
| 433 |
# count number of m-files in directories |
|
| 434 |
$dirnumbermfiles{$dirname}++;
|
|
| 435 |
|
|
| 436 |
if ($debug > 1) {
|
|
| 437 |
if ($opt_silent) { print "\r"; }
|
|
| 438 |
print " ParseTree: $name \t\t $mfile{$name} \t\t $hfile{$name}\t\t";
|
|
| 439 |
if (!$opt_silent) { print "\n"; }
|
|
| 440 |
} |
|
| 441 |
} |
|
| 442 |
} |
|
| 443 |
else {
|
|
| 444 |
print "Unknown type of file in $dirname: $file\n"; |
|
| 445 |
} |
|
| 446 |
} |
|
| 447 |
else { print "Error: Not existing file in $dirname: $file\n"; }
|
|
| 448 |
} |
|
| 449 |
|
|
| 450 |
closedir(AKTDIR) |
|
| 451 |
|
|
| 452 |
} |
|
| 453 |
|
|
| 454 |
#------------------------------------------------------------------------ |
|
| 455 |
# Construct Dependency matrix |
|
| 456 |
# $dep{$x,$y} > 0 if $x includes a reference to $y.
|
|
| 457 |
#------------------------------------------------------------------------ |
|
| 458 |
sub ConstructDependencyMatrix |
|
| 459 |
{
|
|
| 460 |
&ConstructDependencyMatrixReadFiles('all');
|
|
| 461 |
&ConstructDependencyMatrixReally; |
|
| 462 |
} |
|
| 463 |
|
|
| 464 |
|
|
| 465 |
#------------------------------------------------------------------------ |
|
| 466 |
# Construct Dependency matrix |
|
| 467 |
# $dep{$x,$y} > 0 if $x includes a reference to $y.
|
|
| 468 |
#------------------------------------------------------------------------ |
|
| 469 |
sub ConstructDependencyMatrixReadFiles |
|
| 470 |
{
|
|
| 471 |
local($whatstring) = @_; |
|
| 472 |
local(*MFILE); |
|
| 473 |
local($name, $inames); |
|
| 474 |
local(%symbolsdep, %symbolsall); |
|
| 475 |
|
|
| 476 |
# Initialize as all zeros. |
|
| 477 |
# foreach $name (@names) { grep($dep{$name,$_}=0,@names); if ($debug > 0) { print "\r DepMatrix anlegen: $name\t$#names\t"; } }
|
|
| 478 |
|
|
| 479 |
# Compute the dependency matrix |
|
| 480 |
$inames = -1; |
|
| 481 |
foreach $name (@names) {
|
|
| 482 |
# Read each file and tabulate the distinct alphanumeric identifiers in |
|
| 483 |
# an array of symbols. Also scan for: |
|
| 484 |
# synopsis: The function declaration line |
|
| 485 |
# apropos: The first line of the help text |
|
| 486 |
|
|
| 487 |
# look for whatstring, if all: process every file, if contents: process only contents files |
|
| 488 |
if ($whatstring eq 'contents') { if (! ($name =~ /contents$/i) ) { next; } }
|
|
| 489 |
elsif ($whatstring eq 'all') { } # do nothing
|
|
| 490 |
else { print "\r ConstructDependency: Unknown parameter whatstring: $whatstring \n"; }
|
|
| 491 |
|
|
| 492 |
undef %symbolsall; undef %symbolsdep; |
|
| 493 |
open(MFILE,"<$mfile{$name}") || die("Can't open $mfile{$name}: $!\n");
|
|
| 494 |
while (<MFILE>) {
|
|
| 495 |
chop; |
|
| 496 |
|
|
Also available in: Unified diff