1#!/usr/bin/tclsh
2
3#    Copyright (C) 2000 artofcode LLC.  All rights reserved.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the
7# Free Software Foundation; either version 2 of the License, or (at your
8# option) any later version.
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
13# Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc.,
17# 59 Temple Place, Suite 330, Boston, MA, 02111-1307.
18
19# $Id: hrefcov.tcl,v 1.5.4.1.2.1 2003/04/12 14:02:39 giles Exp $
20
21# hrefcov.tcl - check that the hrefs in an HTML document mention all of a
22# set of files.  The requirement is that the union of all the docfiles
23# must somewhere reference all the files.  Usage:
24set USAGE {Usage:
25    hrefcov (+src | +lib | [+-]from <docfile> | [+-]to (<directory> | <file>))*
26}
27# +from or +to adds files; -from or -to removes them;
28# +src and +lib execute SRC_LIST and LIB_LIST below.
29
30# Define the Ghostscript-specific parameter lists.
31set SRC_LIST [list\
32	+from doc/Develop.htm\
33	+to lib src\
34	-to lib/CVS src/CVS\
35	-to src/*.mak.tcl\
36	-to lib/*.upp\
37	-to lib/*.ps +to lib/gs_*.ps lib/pdf_*.ps
38]
39set LIB_LIST [list\
40	+from doc/Psfiles.htm\
41	+to examples/*.ps lib/*.ps
42]
43
44# Global variables:
45#   TO(file) is defined for every file in the "to" list
46#   TO_DIR(dir) is defined for every directory in the "to" list
47#   FROM(file) is defined for every file mentioned in a "from" document
48# In both cases, path names are normalized by removing ./ and/or ../
49# whenever possible, to produce file names relative to the directory where
50# this program is being run.
51
52# Initialize the internal data base.
53proc init {} {
54    global FROM TO TO_DIR
55
56    catch {unset FROM}
57    catch {unset TO}
58    catch {unset TO_DIR}
59}
60
61# Normalize a file name by removing all occurrences of ./ and
62# all occurrences of <dir>/../.
63proc normalize_fname {fname} {
64    set name $fname
65				# Remove a trailing /
66    regsub {/$} $name "" name
67				# Remove occurrences of ./
68    while {[regsub {^\./} $name "" name]} {}
69    while {[regsub {/\./} $name / name]} {}
70    while {[regsub {/\.$} $name "" name]} {}
71    if {$name == ""} {return /}
72				# Remove occurrences of <dir>/../
73    while {[regsub {(^|/)([^./]|.[^./])[^/]*/../} $name {\1} name]} {}
74    if {$name == ""} {return .}
75    return $name
76}
77
78# Add or remove a file, or all the files in a directory, to/from TO.
79proc add_to {to} {
80    global TO TO_DIR
81
82    if {[file isfile $to]} {
83	set TO($to) 1
84    } elseif {[file isdirectory $to]} {
85	set TO_DIR($to) 1
86	foreach f [glob $to/*] {add_to $f}
87    }
88}
89proc remove_to {to} {
90    global TO TO_DIR
91
92    if {[file isfile $to]} {
93	catch {unset TO($to)}
94    } elseif {[file isdirectory $to]} {
95	catch {unset TO_DIR($to)}
96	foreach f [glob $to/*] {remove_to $f}
97    }
98}
99
100# Add or remove all the files mentioned in a document to/from FROM.
101# Note that we only add/remove files mentioned as a whole, i.e., without #.
102proc for_from {doc proc} {
103    set lines ""
104    set prefix ""
105    regexp {^(.*/)[^/]+$} $doc skip prefix
106    catch {set lines [split [exec egrep -i {href="[^#]} $doc] "\n"]}
107    set href_exp {href="([^"#]*)"(.*)$}
108    foreach line $lines {
109	while {[regexp -nocase $href_exp $line skip ref line]} {
110	    $proc [normalize_fname $prefix$ref]
111	}
112    }
113}
114proc add_from {doc} {for_from $doc add1_from}
115proc add1_from {from} {global FROM; set FROM($from) 1}
116proc remove_from {doc} {for_from $doc remove1_from}
117proc remove1_from {from} {global FROM; catch {unset FROM($from)}}
118
119# Main program.
120proc main_args {arglist} {
121    global FROM TO SRC_LIST LIB_LIST
122
123    foreach arg $arglist {
124	switch -glob -- $arg {
125	    +src {main_args $SRC_LIST}
126	    +lib {main_args $LIB_LIST}
127	    +from {set do add_from}
128	    -from {set do remove_from}
129	    +to {set do add_to}
130	    -to {set do remove_to}
131	    {[+-]*} {
132		puts stderr "Unknown switch: $arg"
133		exit 1
134	    }
135	    default {
136		if {[regexp {[*]} $arg]} {
137		    foreach a [glob -nocomplain $arg] {$do $a}
138		} else {
139		    $do $arg
140		}
141	    }
142	}
143    }
144}
145proc main {argv} {
146    global FROM TO TO_DIR
147
148    init
149    main_args $argv
150    set dirs_exp {^$}
151    foreach dir [array names TO_DIR] {
152	append dirs_exp "|$dir/"
153    }
154    set list {}
155    foreach f [array names TO] {
156	if {![info exists FROM($f)]} {lappend list $f}
157    }
158    if {$list != {}} {
159	puts "        ****** Files defined but not referenced ******"
160	foreach f [lsort $list] {puts $f}
161    }
162    set list {}
163    foreach f [array names FROM] {
164	if {![info exists TO($f)]} {
165	    # Only report files that should be in a scanned directory.
166	    if {[regexp "(${dirs_exp})\[^/\]+$" $f]} {
167		lappend list $f
168	    }
169	}
170    }
171    if {$list != {}} {
172	puts "        ****** Files referenced but not defined ******"
173	foreach f [lsort $list] {puts $f}
174    }
175}
176
177main $argv
178