one of the documented procedures in this installation of the ACS
apm_read_package_info_file   path
What it does:
Reads a .info file, returning an array containing the following items: This routine will typically be called like so:
array set version_properties [apm_read_package_info_file $path]
to populate the version_properties array.

If the .info file cannot be read or parsed, this routine throws a descriptive error.

Defined in: /web/philip/packages/acs-core/apm-procs.tcl

Source code:

    global ad_conn

    # If the .info file hasn't changed since last read (i.e., has the same
    # mtime), return the cached info list.
    set mtime [file mtime $path]
    if { [nsv_exists apm_version_properties $path] } {
	set cached_version [nsv_get apm_version_properties $path]
	if { [lindex $cached_version 0] == $mtime } {
	    return [lindex $cached_version 1]

    # Set the path and mtime in the array.
    set properties(path) $path
    set properties(mtime) $mtime


    ns_log "Notice" "Reading specification file at $path"

    set file [open $path]
    set xml_data [read $file]
    close $file

    set tree [dom::DOMImplementation parse $xml_data]
    set package [dom::node cget $tree -firstChild]
    if { ![string equal [dom::node cget $package -nodeName] "package"] } {
	error "Expected <package> as root node"
    set properties(package.key) [apm_required_attribute_value $package key]
    set properties(package.url) [apm_required_attribute_value $package url]

    set versions [dom::element getElementsByTagName $package version]
    if { [llength $versions] != 1 } {
	error "Package must contain exactly one <version> node"
    set version [lindex $versions 0]
    set properties(name) [apm_required_attribute_value $version name]
    set properties(url) [apm_required_attribute_value $version url]

    # Set an entry in the properties array for each of these tags.
    foreach property_name { package-name option summary description distribution release-date vendor group } {
	set node [lindex [dom::element getElementsByTagName $version $property_name] 0]
	if { ![empty_string_p $node] } {
	    set properties($property_name) [dom::node cget [dom::node cget $node -firstChild] -nodeValue]
	} else {
	    set properties($property_name) ""

    # Set an entry in the properties array for each of these attributes:
    #   <vendor url="...">           -> vendor.url
    #   <description format="...">   -> description.format

    foreach { property_name attribute_name } {
	vendor url
	description format
    } {
	set node [lindex [dom::element getElementsByTagName $version $property_name] 0]
	if { ![empty_string_p $node] } {
	    set properties($property_name.$attribute_name) [dom::element getAttribute $node $attribute_name]
	} else {
	    set properties($property_name.$attribute_name) ""

    # We're done constructing the properties array - save the properties into the
    # moby array which we're going to return.

    set properties(properties) [array get properties]

    # Build lists of the services provided by and required by the package.

    set properties(provides) [list]
    set properties(requires) [list]

    foreach dependency_type { provides requires } {
	foreach node [dom::element getElementsByTagName $version $dependency_type] {
	    set service_url [apm_required_attribute_value $node url]
	    set service_version [apm_required_attribute_value $node version]
	    lappend properties($dependency_type) [list $service_url $service_version]

    # Build a list of the files contained in the package.

    set properties(files) [list]

    foreach node [dom::element getElementsByTagName $version "files"] {
	foreach file_node [dom::element getElementsByTagName $node "file"] {
	    set file_path [apm_required_attribute_value $file_node path]
	    set type [dom::element getAttribute $file_node type]
	    # Validate the file type: it must be null (unknown type) or
	    # some value in [apm_file_type_keys].
	    if { ![empty_string_p $type] && [lsearch -exact [apm_file_type_keys] $type] < 0 } {
		error "Invalid file type \"$type\""
	    lappend properties(files) [list $file_path $type]

    # Build a list of the package's owners (if any).

    set properties(owners) [list]

    foreach node [dom::element getElementsByTagName $version "owner"] {
	set url [dom::element getAttribute $node url]
	set name [dom::node cget [dom::node cget $node -firstChild] -nodeValue]
	lappend properties(owners) [list $url $name]

    # Build a list of the packages included by this package (if any).

    set properties(includes) [list]

    foreach node [dom::element getElementsByTagName $version "include"] {
	lappend properties(includes) [dom::element getAttribute $node url]

    # Serialize the array into a list.
    set return_value [array get properties]

    # Cache the property info based on $mtime.
    nsv_set apm_version_properties $path [list $mtime $return_value]

    return $return_value