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
110function 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
192function 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
211function die
212{
213	echo "$PROGNAME: $@" >&2
214	exit 1
215}
216
217function usage
218{
219	echo "Usage: $PROGNAME -t tmplfile header [header ...]" >&2
220	exit 2
221}
222
223PROGNAME=$(basename "$0")
224
225while getopts t: c ; do
226	case $c in
227	    t)
228		mapfile_skel=$OPTARG
229		;;
230	    ?)
231		usage
232	esac
233done
234
235[[ -z "$mapfile_skel" ]] && usage
236[[ ! -f $mapfile_skel ]] && die "Couldn't open template $tmplfile"
237
238shift $(($OPTIND - 1))
239
240[[ $# -lt 1 ]] && usage
241
242for file in $@ ; do
243	[[ ! -f $file ]] && die "Can't open input file $file"
244done
245
246extract_section $mapfile_skel PROLOGUE
247
248for 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
255done
256
257extract_section $mapfile_skel EPILOGUE
258