#! /usr/bin/perl -w # # Class name: DocReference # Synopsis: Used by gatherHeaderDoc.pl to hold references to doc # for individual headers and classes # Last Updated: $Date: 2011/03/18 16:07:18 $ # # Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # # This file contains Original Code and/or Modifications of Original Code # as defined in and that are subject to the Apple Public Source License # Version 2.0 (the 'License'). You may not use this file except in # compliance with the License. Please obtain a copy of the License at # http://www.opensource.apple.com/apsl/ and read it before using this # file. # # The Original Code and all software distributed under the License are # distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, # INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. # # @APPLE_LICENSE_HEADER_END@ # ###################################################################### # /*! @header # @abstract # DocReference class package file. # @discussion # This file contains the DocReference class, a # simple data structure used to hold references to documents in # {@link //apple_ref/doc/header/gatherHeaderDoc.pl gatherHeaderDoc}. # # See the class documentation below for more information. # @indexgroup HeaderDoc Miscellaneous Helpers # */ # /*! @abstract # Describes the properties of an API or document reference # (apple_ref). # @discussion # A data structure used for holding references to documents in # {@link //apple_ref/doc/header/gatherHeaderDoc.pl gatherHeaderDoc}. # # Each object contains the name of a header, class, or API symbol, # along with its path, its unique identifier (API refeference) and # bits of related information like the symbol's abstract, # discussion, and declaration (where available). # @var OUTPUTFORMAT # Unused. # @var UID # The API reference (UID) for this doc reference, # e.g. //apple_ref/c/func/foo. # # See the # {@linkdoc //apple_ref/doc/uid/TP40001215 HeaderDoc User Guide} # for more information about API symbol markers. # @var NAME # The "long name" (name specified in a tag such as # \@header, \@function, etc. # @var SHORTNAME # The "short name" (code symbol name or raw # filename) for this doc reference. # @var GROUP # The index group for this doc reference. # (From the \@indexgroup tag.) # @var TYPE # The type part of the UID (e.g. cl for class). # # See the # {@linkdoc //apple_ref/doc/uid/TP40001215 HeaderDoc User Guide} # for more information about API symbol markers. # @var PATH # The filesystem path to the file containing this # doc reference. # @var LANGUAGE # The language part of the UID for this doc reference. # @var DECLARATION # The declaration (code) associated with this doc # reference. # @var ABSTRACT # The \@abstract associated with this doc reference. # @var DISCUSSION # The \@discussion associated with this doc reference. # @var PUSHED # Set to 1 when the doc reference has already been # added to the doc reference array so that it doesn't # get added multiple times when the discussion and # abstract are located. # @var MANSRC # Holds information about which set of manual pages a # man page came from. Access this variable with the # {@link mansrc} function. # */ package HeaderDoc::DocReference; use strict; use vars qw($VERSION @ISA); # /*! # @abstract # The revision control revision number for this module. # @discussion # In the git repository, contains the number of seconds since # January 1, 1970. # */ $HeaderDoc::DocReference::VERSION = '$Revision: 1300489638 $'; ################ General Constants ################################### my $debugging = 0; my %docRefUIDCache = (); my %docRefUIDFileCache = (); my %docRefNameToUIDCache = (); # /*! # @abstract # Creates a new DocReference object. # @param param # A reference to the relevant package object (e.g. # HeaderDoc::DocReference->new() to allocate # a new instance of this class). # */ sub new { my($param) = shift; my($class) = ref($param) || $param; my $self = {}; bless($self, $class); $self->_initialize(); # Now grab any key => value pairs passed in my (%attributeHash) = @_; foreach my $key (keys(%attributeHash)) { $self->{$key} = $attributeHash{$key}; } return ($self); } # /*! # @abstract # Initializes an instance of a DocReference object. # @param self # The object to initialize. # */ sub _initialize { my($self) = shift; $self->{OUTPUTFORMAT} = undef; $self->{UID} = undef; $self->{NAME} = undef; $self->{GROUP} = " "; $self->{TYPE} = undef; # Header, CPPClass, etc $self->{PATH} = undef; $self->{LANGUAGE} = ""; } # /*! # @abstract # Gets/sets the path associated with this doc reference. # @param self # The DocReference object. # @param path # The path to set. (Optional.) # */ sub path { my $self = shift; if (@_) { $self->{PATH} = shift; } return $self->{PATH}; } # /*! # @abstract # Gets/sets the language associated with this doc reference. # @param self # The DocReference object. # @param language # The language to set. (Optional.) # */ sub language { my $self = shift; if (@_) { $self->{LANGUAGE} = shift; } return $self->{LANGUAGE}; } # /*! # @abstract # Returns whether this doc reference has a "doc" API reference. # @param self # The DocReference object. # */ sub isDoc { my $self = shift; if ($self->language() eq "doc") { return 1; } return 0; } # /*! # @abstract # Gets/sets the output format for this doc reference. # @param self # The DocReference object. # @param outputformat # The output format value to set. (Optional.) # */ sub outputformat { my $self = shift; if (@_) { $self->{OUTPUTFORMAT} = shift; } return $self->{OUTPUTFORMAT}; } # /*! # @abstract # Gets/sets the discussion associated with this doc reference. # @param self # The DocReference object. # @param discussion # The discussion to set. (Optional.) # */ sub discussion { my $self = shift; if (@_) { $self->{DISCUSSION} = shift; } return $self->{DISCUSSION} || ""; } # /*! # @abstract # Quotes a string for passing to grep. # @param string # The string to quote. # */ sub quoteForGrep { my $string = shift; $string =~ s/([]\\\/[])/\\$1/sg; $string =~ s/'/'"'"'/sg; $string =~ s/^(\^)/\\$1/sg; $string =~ s/(\$)$/\\$1/sg; return $string; } # /*! # @abstract # Quotes a string for passing to whatis. # @param string # The string to quote. # */ sub quoteForWhatIs { my $string = shift; return quoteForGrep($string); # Note: The whatis command currently uses grep and does # not quote the arguments. If that ever changes, # this function should the code below instead. # $string =~ s/'/'"'"'/sg; # $string =~ s/^(\^)/\\$1/sg; # $string =~ s/(\$)$/\\$1/sg; # return $string; } my %manpageAbstracts = (); # /*! # @abstract # Gets/sets the abstract associated with this doc reference. # @param self # The DocReference object. # @param abstract # The abstract to set. (Optional.) # @discussion # For manual pages, this goes out and requests the abstract # using whois. # */ sub abstract { my $self = shift; if (@_) { $self->{ABSTRACT} = shift; } my $ret = $self->{ABSTRACT} || ""; if ($HeaderDoc::useWhatIs && (!length($ret))) { if ($manpageAbstracts{$self->{PATH}}) { return $manpageAbstracts{$self->{PATH}}; } my $temp = $/; $/ = undef; # print "NAME: ".$self->name()."\nMPNAME: ".$self->mpname()."\n"; my $mpn = quoteForGrep($self->mpname()); if ($mpn) { # print $self->uid()." is a man page\n"; # print "/usr/bin/whatis '".quoteForWhatIs($self->name())."' | grep -v \"nothing appropriate\" | grep '$mpn' |"; open(WI, "/usr/bin/whatis '".quoteForWhatIs($self->name())."' | grep -v \"nothing appropriate\" | grep '$mpn' |") || die("Could not run /usr/bin/whatis\n"); } else { # print $self->uid()." is NOT a man page\n"; # print "/usr/bin/whatis '".quoteForWhatIs($self->name())."' | grep -v \"nothing appropriate\" | grep '".quoteForGrep($self->name())."([23]' |\n"; open(WI, "/usr/bin/whatis '".quoteForWhatIs($self->name())."' | grep -v \"nothing appropriate\" | grep '".quoteForGrep($self->name())."([23]' |") || die("Could not run /usr/bin/whatis\n"); } $ret = ; $ret = getCorrectWhoIsInfo($ret, $self->name()); $/ = $temp; # print "RAW: $ret\n"; $ret =~ s/.*?- //s; # print "RAW: $ret\n"; $ret =~ s/[\n\r]//s; # print "RAW: $ret\n"; $ret =~ s/^[-\x{2014}\x{2013}[:space:]]*//s; # Nuke any leading hyphens, # dashes, or whitespace. # print "RAW: $ret\n"; $manpageAbstracts{$self->{PATH}} = $ret; } return $ret; } # /*! # @abstract # Splits up the results from whois, prioritizing single entries over # collective ones and earlier collective results over later ones. # */ sub getCorrectWhoIsInfo { my $line = shift; my $name = shift; my @parts = split(/\n/, $line); my $result = ""; my $altresult = ""; my $inResult = 0; foreach my $part (@parts) { # print STDERR "PART: $part\n"; if ($part =~ /^\s*$name\([^)]*\)\s*-/) { $result = $part; } elsif ($part =~ /(^|\s)$name\(.*\).*-/) { if (!$altresult) { $altresult = $part; } } } # print STDERR "RESULT: \"$result\"\n"; # print STDERR "ALTRESULT: \"$altresult\"\n"; if ($result) { return $result; } return $altresult; } # /*! # @abstract # Gets/sets the discussion associated with this doc reference. # @param self # The DocReference object. # @param discussion # The discussion to set. (Optional.) # */ sub declaration { my $self = shift; if (@_) { $self->{DECLARATION} = shift; } if ($self->{DECLARATION}) { return $self->{DECLARATION}; } else { if ($HeaderDoc::useWhatIs) { my $refsfile = $self->{PATH}.".decs"; if (-f $refsfile) { open(REFSFILE, "<$refsfile"); my $temp = $/; $/ = undef; my $data = ; close(REFSFILE); $/ = $temp; my $uid = $self->uid(); # print "SEARCH: $uid\n"; # print "DATA: $data\n"; $data =~ s/(^|.*\n)\Q$uid\E\n//s; # print "POSTDATA: $data\n"; if ($data !~ /^X/) { # print "NOT FOUND\n"; return ""; } $data =~ s/\n[^X].*$//s; # print "RAWDEC:\n$data\n"; $data =~ s/^X//s; $data =~ s/\nX/\n/sg; # print "FINALDATA:\n$data\n"; return $data; } } return ""; # return $self->{NAME}; } } # /*! # @abstract # Gets/sets the uid associated with this doc reference. # @param self # The DocReference object. # @param uid # The uid to set. (Optional.) # @discussion # If called repeatedly, any non-doc apple_ref will stick with # higher precedence than any doc apple_ref. # @returns # Returns any existing doc reference object for the # specified doc ID from the current file. # */ sub uid { my $self = shift; if (@_) { my $uid = shift; my $file = shift; if ($uid =~ /\/\/[^\/]+\/c\/([^\/]+)\/([^\/]+)/) { my $type = $1; my $name = $2; # print STDERR "MAPPING $name to $uid\n"; $docRefNameToUIDCache{$name} = $uid; } else { # print STDERR "BAD UID $uid\n"; } $self->{UID} = $uid; my %temphash = (); if ($docRefUIDFileCache{$uid}) { %temphash = %{$docRefUIDFileCache{$uid}}; } if ($temphash{$file}) { # print STDERR "$uid/$file FOUND IN HASH\n"; return $temphash{$file}; # } else { # print STDERR "$uid/$file NOT FOUND IN HASH\n"; } $temphash{$file} = $self; $docRefUIDFileCache{$uid} = \%temphash; # Fill up the non-per-file hash for man pages, too. $docRefUIDCache{$uid} = $self; return $self; } return $self->{UID}; } # /*! # @abstract # Returns whether a UID has been seen. # */ sub isInCache { my $self = shift; my $uid = shift; if ($docRefUIDCache{$uid}) { return $docRefUIDCache{$uid}; } return undef; } # /*! # @abstract # Gets/sets the name associated with this doc reference. # @param self # The DocReference object. # @param name # The name to set. (Optional.) # */ sub name { my $self = shift; if (@_) { if (!$self->{NAME}) { $self->{NAME} = shift; # if ($self->uid() !~ /\/\/[^\/]+\/doc\/man\//) { # $docRefNameToUIDCache{$self->{NAME}} = $self->uid(); # } } } return $self->{NAME}; } # /*! # @abstract # Sets the man page name associated with this doc reference. # @param self # The DocReference object. # @discussion # For doc references of man pages, this returns the name in # the standard man page format, e.g. name(number). # For non-man-page references, this returns an empty string. # */ sub mpname { my $self = shift; my $uid = $self->uid(); my @uidparts = split(/\//, $uid); if ($uidparts[3] ne "doc") { # warn("NOT: 3 is \"".$uidparts[3]."\" ($uid)\n"); return ""; } if ($uidparts[4] ne "man") { # warn("NOT: 4 is \"".$uidparts[4]."\" ($uid)\n"); return ""; } return $uidparts[6]."(".$uidparts[5].")"; } # /*! # @abstract # Gets/sets the group associated with this doc reference. # @param self # The DocReference object. # @param group # The group to set. (Optional.) # */ sub group { my $self = shift; if (@_) { my $newgroupname = shift; if (!length($newgroupname)) { $newgroupname = " "; } $self->{GROUP} = $newgroupname; } return $self->{GROUP}; } # /*! # @abstract # Gets/sets the short name associated with this doc reference. # @param self # The DocReference object. # @param shortname # The short name to set. (Optional.) # @discussion # The short name refers to the name of a framework (without the # trailing ".framework" or any other path parts). # */ sub shortname { my $self = shift; if (@_) { $self->{SHORTNAME} = shift; } return $self->{SHORTNAME}; } # /*! # @abstract # Gets/sets the mansrc value associated with this doc reference. # @param self # The DocReference object. # @param type # The mansrc value to set. (Optional.) # @discussion # The mansrc value refers to the part of a doc navigator comment after # the "mansrc=" part. It is used to hold information about which # set of man pages a man page came from (e.g. base, server, etc.). # */ sub mansrc { my $self = shift; if (@_) { $self->{MANSRC} = shift; } return $self->{MANSRC}; } # /*! # @abstract # Gets/sets the type associated with this doc reference. # @param self # The DocReference object. # @param type # The type to set. (Optional.) # @discussion # The type refers to the part of a doc navigator comment after # the "headerDoc=" part. # */ sub type { my $self = shift; if (@_) { $self->{TYPE} = shift; } return $self->{TYPE}; } # /*! # @abstract # Gets/sets the "pushed" state for this doc reference. # @param self # The DocReference object. # @param pushed # The pushed state to set. (Optional.) # @discussion # The pushed state refers to whether the doc reference has # already been pushed into the array of doc references in # {@link //apple_ref/doc/header/gatherHeaderDoc.pl gatherHeaderDoc} # or not. This is needed because subsequent discussions and # abstracts get folded back into the same doc reference, and you # don't want to end up pushing it twice. # */ sub pushed { my $self = shift; if (@_) { $self->{PUSHED} = shift; } return $self->{PUSHED}; } # /*! # @abstract # Prints a DocReference object for debugging purposes. # @param self # The object to initialize. # */ sub dbprint { my $self = shift; $self->printObject(); } # /*! # @abstract # */ sub seealso { my $self = shift; my @returnrefs = (); my $relatedfile = $self->{PATH}.".xrefs"; if (-f $relatedfile) { open(RELATEDFILE, "<$relatedfile"); my $temp = $/; $/ = undef; my $data = ; close(RELATEDFILE); $/ = $temp; my @arr = split(/\n/, $data); my %refs = (); foreach my $ref (@arr) { $refs{$ref} = $ref; } foreach my $ref (keys %refs) { if ($self->isInCache($ref)) { # print STDERR "RELATED REF: $ref\n"; if ($ref =~ /\/\/[^\/]+\/doc\/man\//) { push(@returnrefs, translateManRef($ref)); } else { push(@returnrefs, $ref); } } else { # print STDERR "NOT FOUND RELATED REF: $ref\n"; } } } return @returnrefs; } # /*! # @abstract # Alias to dbprint. # @param self # The object to initialize. # */ sub printObject { my $self = shift; print STDERR "----- DocReference Object ------\n"; print STDERR "uid: $self->{UID}\n"; print STDERR "name: $self->{NAME}\n"; print STDERR "type: $self->{TYPE}\n"; print STDERR "path: $self->{PATH}\n"; print STDERR "language: $self->{LANGUAGE}\n"; print STDERR "abstract: ".($self->{ABSTRACT} || "")."\n"; print STDERR "discussion: ".($self->{DISCUSSION} || "")."\n"; print STDERR "declaration: ".($self->{DECLARATION} || "")."\n"; print STDERR "\n"; } # /*! # @abstract # Maps a man page doc reference to a C reference. # @param ref # The input doc reference. # @discussion # Uses the docRefNameToUIDCache cache, which is populated # in {@link uid}. # */ sub translateManRef { my $ref = shift; if ($ref =~ /\/\/[^\/]+\/doc\/man\/([^\/]+)\/(.*?)$/) { my $section = $1; my $name = $2; # print STDERR "NAME: $name REF: $ref\n"; if (($section =~ /^3/) && ($docRefNameToUIDCache{$name})) { # print STDERR "TRANSLATE: $ref -> ".$docRefNameToUIDCache{$name}."\n"; return $docRefNameToUIDCache{$name}; } } # print STDERR "TRANSLATE FAILED: $ref\n"; return $ref; } # /*! # @abstract # Prevents C API reference symbols from showing up in man page output. # */ sub hidden { my $self = shift; if (@_) { $self->{HIDDEN} = shift; } return $self->{HIDDEN}; } 1;