1 #!/bin/ksh
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 #
  23 # Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  24 # Use is subject to license terms.
  25 #
  26 #ident  "%Z%%M% %I%     %E% SMI"
  27 #
  28 
  29 #
  30 # Given a header file, extract function prototypes and global variable
  31 # declarations in a form that can be used in a mapfile.  The list of extracted
  32 # functions and variables will be combined with a user-specified template to
  33 # create a complete mapfile.
  34 #
  35 # Template
  36 # --------
  37 #
  38 # The template contains two sections - the prologue, and the epilogue.  These
  39 # sections are used, verbatim, as the beginning and the end of the mapfile.
  40 # Sections begin and end with single-line comments whose sole contents are
  41 # "/* BEGIN $section */" and "/* END $section */".
  42 #
  43 # Template example:
  44 #
  45 # /* BEGIN PROLOGUE */
  46 # [ ... prologue goes here ... ]
  47 # /* END PROLOGUE */
  48 # /* BEGIN EPILOGUE */
  49 # [ ... epilogue goes here ... ]
  50 # /* END EPILOGUE */
  51 #
  52 # Selective Exportation
  53 # ---------------------
  54 #
  55 # Some header files will have a public/private interface mix that is strongly
  56 # biased towards private interfaces.  That is, of the interfaces declared by
  57 # a given header file, the majority of them are private.  Only a small subset
  58 # of interfaces are to be exported publicly.  Using Selective Exportation, a
  59 # special comment is included in the header file, declaring to this script that
  60 # only a subset of interfaces - those with a marking declared in the comment -
  61 # should be included in the mapfile.  The marking is itself a special comment,
  62 # whose format is declared using a directive like this:
  63 #
  64 #       MAPFILE: export "Driver OK"
  65 #
  66 # Using the above directive, only those function prototypes and variable
  67 # declarations with "/* Driver OK */" comments included in the mapfile.  Note
  68 # that the comment must be at the end of the first line.  If the declaration
  69 # spans multiple lines, the exportation comment must appear on the first line.
  70 #
  71 # Examples of functions selected for exportation:
  72 #
  73 # MAPFILE: export "Driver OK"
  74 #
  75 # extern int foo(int);          /* Driver OK */
  76 # extern void bar(int, int,     /* Driver OK */
  77 #     int, void *);
  78 #
  79 # Selective Exportation may not be used in the same file as Selective Exclusion.
  80 #
  81 # Selective Exclusion
  82 # -------------------
  83 #
  84 # Selective Exclusion is to be used in cases where the public/private interface
  85 # mix is reversed - where public interfaces greatly outnumber the private ones.
  86 # In this case, we want to be able to mark the private ones, thus telling this
  87 # script that the marked interfaces are to be excluded from the mapfile.
  88 # Marking is accomplished via a process similar to that used for Selective
  89 # Exportation.  A directive is included in a comment, and is formatted like
  90 # this:
  91 #
  92 #       MAPFILE: exclude "Internal"
  93 #
  94 # Using the above directive, function prototypes and variable declarations with
  95 # "/* Internal */" comments would be excluded.  Note that the comment must be at
  96 # the end of the first line.  If the declaration spans multiple lines, the
  97 # exclusion comment must appear on the first line.
  98 #
  99 # Examples of functions excluded from exportation:
 100 #
 101 # MAPFILE: exclude "Internal"
 102 #
 103 # extern int foo(int);          /* Internal */
 104 # extern void bar(int, int,     /* Internal */
 105 #       int, void *);
 106 #
 107 # Selective Exclusion may not be used in the same file as Selective Exportation.
 108 #
 109 
 110 function extract_prototypes
 111 {
 112         typeset header="$1"
 113         typeset prefix="$2"
 114 
 115         nawk -v prefix="$prefix" <$header '
 116                 /^.*MAPFILE: export \"[^\"]*\"$/ {
 117                         if (protoexclude) {
 118                                 print "ERROR: export after exclude\n";
 119                                 exit(1);
 120                         }
 121                 
 122                         sub(/^[^\"]*\"/, "");
 123                         sub(/\"$/, "");
 124 
 125                         exportmark=sprintf("/* %s */", $0);
 126                         next;
 127                 }
 128 
 129                 /^.*MAPFILE: exclude \"[^\"]*\"$/ {
 130                         if (protomatch) {
 131                                 print "ERROR: exclude after export";
 132                                 exit(1);
 133                         }
 134 
 135                         sub(/^[^\"]*\"/, "");
 136                         sub(/\"$/, "");
 137 
 138                         excludemark=sprintf("/* %s */", $0);
 139                         next;
 140                 }
 141 
 142                 exportmark {
 143                         # Selective Exportation has been selected (exportmark is
 144                         # set), so exclude this line if it does not have the
 145                         # magic export mark.
 146                         if (length($0) < length(exportmark) ||
 147                             substr($0, length($0) - length(exportmark) + 1) != \
 148                             exportmark)
 149                                 next;
 150                 }
 151 
 152                 excludemark {
 153                         # Selective Exclusion has been selected (excludemark is
 154                         # set), so exclude this line only if it has the magic
 155                         # exclude mark.
 156                         if (length($0) > length(excludemark) &&
 157                             substr($0, \
 158                             length($0) - length(excludemark) + 1) == \
 159                             excludemark)
 160                                 next;
 161                 }
 162 
 163                 # Functions
 164                 /^extern.*\(/ {
 165                         for (i = 1; i <= NF; i++) {
 166                                 if (sub(/\(.*$/, "", $i)) {
 167                                         sub(/^\*/, "", $i);
 168                                         if (!seenfn[$i]) {
 169                                                 printf("%s%s;\n", prefix, $i);
 170                                                 seenfn[$i] = 1;
 171                                         }
 172                                         break;
 173                                 }
 174                         }
 175                         next;
 176                 }
 177 
 178                 # Global variables
 179                 /^extern[^\(\)]*;/ {
 180                         for (i = 1; i <= NF; i++) {
 181                                 if (match($i, /;$/)) {
 182                                         printf("%s%s; /* variable */\n", prefix,
 183                                             substr($i, 1, length($i) - 1));
 184                                         break;
 185                                 }
 186                         }
 187                         next;
 188                 }
 189         ' || die "Extraction failed"
 190 }
 191 
 192 function extract_section
 193 {
 194         typeset skel="$1"
 195         typeset secname="$2"
 196 
 197         nawk <$skel -v name=$secname -v skel=$skel '
 198             /\/\* [^ ]* [^ ]* \*\// && $3 == name {
 199                 if ($2 == "BEGIN") {
 200                         printing = 1;
 201                 } else {
 202                         printing = 0;
 203                 }
 204                 next;
 205             }
 206 
 207             printing != 0 { print; }
 208         '
 209 }
 210 
 211 function die
 212 {
 213         echo "$PROGNAME: $@" >&2
 214         exit 1
 215 }
 216 
 217 function usage
 218 {
 219         echo "Usage: $PROGNAME -t tmplfile header [header ...]" >&2
 220         exit 2
 221 }
 222 
 223 PROGNAME=$(basename "$0")
 224 
 225 while getopts t: c ; do
 226         case $c in
 227             t)
 228                 mapfile_skel=$OPTARG
 229                 ;;
 230             ?)
 231                 usage
 232         esac
 233 done
 234 
 235 [[ -z "$mapfile_skel" ]] && usage
 236 [[ ! -f $mapfile_skel ]] && die "Couldn't open template $tmplfile"
 237 
 238 shift $(($OPTIND - 1))
 239 
 240 [[ $# -lt 1 ]] && usage
 241 
 242 for file in $@ ; do
 243         [[ ! -f $file ]] && die "Can't open input file $file"
 244 done
 245 
 246 extract_section $mapfile_skel PROLOGUE
 247 
 248 for file in $@ ; do
 249         echo "\t\t/*"
 250         echo "\t\t * Exported functions and variables from:"
 251         echo "\t\t *  $file"
 252         echo "\t\t */"
 253         extract_prototypes $file "\t\t"
 254         echo
 255 done
 256 
 257 extract_section $mapfile_skel EPILOGUE