1#!/usr/bin/perl
2#
3# Copyright (c) 2003-2004,2006,2008,2012,2014 Apple Inc. All Rights Reserved.
4#
5# @APPLE_LICENSE_HEADER_START@
6#
7# This file contains Original Code and/or Modifications of Original Code
8# as defined in and that are subject to the Apple Public Source License
9# Version 2.0 (the 'License'). You may not use this file except in
10# compliance with the License. Please obtain a copy of the License at
11# http://www.opensource.apple.com/apsl/ and read it before using this
12# file.
13#
14# The Original Code and all software distributed under the License are
15# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19# Please see the License for the specific language governing rights and
20# limitations under the License.
21#
22# @APPLE_LICENSE_HEADER_END@
23#
24# generateErrStrings.pl - create error strings files from the Security header files
25#
26# Usage:
27#
28#	perl generateErrStrings.pl <GENDEBUGSTRS?> <NAME_OF_STRINGS_FILE> <input files>
29#
30#	Currently supported files are SecBase.h, SecureTransport.h,cssmapple.h,
31#	cssmerr.h and Authorization.h. These are used by:
32#
33#		void cssmPerror(const char *how, CSSM_RETURN error);
34#
35#	which is in SecBase.cpp.
36#
37# Paths of input files:
38#
39#	./libsecurity_authorization/lib/Authorization.h
40#	./libsecurity_cssm/lib/cssmapple.h
41#	./libsecurity_cssm/lib/cssmerr.h
42#	./libsecurity_keychain/lib/SecBase.h
43#	./libsecurity_ssl/lib/SecureTransport.h
44#
45# Sample run:
46#
47#	perl generateErrStrings.pl "YES" "SecErrorMessages.strings" Authorization.h SecBase.h \
48#		cssmapple.h cssmerr.h SecureTransport.h
49#
50# Input to script: header file(s) containing enum declarations
51# Output: C++ program with one cout statement per decl
52#
53# The input headers are scanned for enums containing error numbers and
54# optional comments. Only certain prefixes for the identifiers in the
55# enums are considered, to avoid non-error message type defines. See
56# the line in the file with CSSM_ERRCODE for acceptable prefixes.
57#
58# There are three styles of comments that this script parses:
59#
60#	Style A [see /System/Library/Frameworks/Security.framework/Headers/SecBase.h]:
61#
62#		errSSLProtocol				= -9800,	/* SSL protocol error */
63#
64#	Style B [see /System/Library/Frameworks/Security.framework/Headers/cssmapple.h]:
65#
66#		/* a code signature match failed */
67#		CSSMERR_CSP_APPLE_SIGNATURE_MISMATCH = CSSM_CSP_PRIVATE_ERROR + 2,
68#
69#	Style C [see /System/Library/Frameworks/Security.framework/Headers/cssmerr.h]:
70#
71#		CSSM_CSSM_BASE_CSSM_ERROR =
72#			CSSM_CSSM_BASE_ERROR + CSSM_ERRORCODE_COMMON_EXTENT + 0x10,
73#		CSSMERR_CSSM_SCOPE_NOT_SUPPORTED =				CSSM_CSSM_BASE_CSSM_ERROR + 1,
74#
75# Style A has the comment after the comment. Style has the comment before the value,
76# and Style C has no comment. In cases where both Style A and B apply, the
77# comment at the end of the line is used.
78#
79# The final output after the generated Objective-C++ program is run looks like:
80#
81#		/* errSSLProtocol */
82#		"-9800" = "SSL protocol error";
83#
84#		/* errSSLNegotiation */
85#		"-9801" = "Cipher Suite negotiation failure";
86#
87# The appropriate byte order marker for UTF-16 is written to the start of the file.
88# Note that the list of errors must be numerically unique across all input files,
89# or the strings file will be invalid. Comments in "Style B" may span multiple lines.
90# C++ style comments are not supported. Double quotes in a comment are hardened with
91# "\" in the output.
92#
93# The English versions of the error messages can be seen with:
94#
95#	cat /System/Library/Frameworks/Security.framework/Resources/English.lproj/SecErrorMessages.strings
96#
97# find -H -X -x . -name "*.h" -print0 2>/dev/null | xargs -0 grep -ri err
98# -----------------------------------------------------------------------------------
99
100# Style questions:
101#	- what should I make PROGNAME?
102#	- should I use a special call to make the temp file in the .mm file?
103#
104
105#use strict;
106#use warnings;
107
108die "Usage:  $0 <gendebug> <tmpdir> <.strings file> <list of headers>\n" if ($#ARGV < 3);
109
110$GENDEBUGSTRINGS=$ARGV[0];			# If "YES", include all strings & don't localize
111$TMPDIR=$ARGV[1];					# temporary directory for program compile, link, run
112$TARGETSTR=$ARGV[2];				# path of .strings file, e.g.
113									#	${DERIVED_SRC}/English.lproj/SecErrorMessages.strings
114@INPUTFILES=@ARGV[3 .. 9999];		# list of input files
115
116$#INPUTFILES = $#ARGV - 3;			# truncate to actual number of files
117
118print "gend: $GENDEBUGSTRINGS, tmpdir: $TMPDIR, targetstr: $TARGETSTR\n";
119$PROGNAME="${TMPDIR}/generateErrStrings.mm";
120open PROGRAM,"> $PROGNAME"  or die "can't open $PROGNAME: $!";
121select PROGRAM;
122
123printAdditionalIncludes();
124printInputIncludes();
125printMainProgram();
126
127# -----------------------------------------------------------------------------------
128# Parse error headers and build array of all relevant lines
129open(ERR, "cat " . join(" ", @INPUTFILES) . "|") or die "Cannot open error header files";
130$/="\};";	#We set the section termination string - very important
131processInput();
132close(ERR);
133# -----------------------------------------------------------------------------------
134
135printTrailer();
136select STDOUT;
137close PROGRAM;
138
139compileLinkAndRun();
140
141# 4: Done!
142exit;
143
144# -----------------------------------------------------------------------------------
145#			Subroutines
146# -----------------------------------------------------------------------------------
147
148sub processInput
149{
150	# 3: Read input, process each line, output it.
151	while ( $line = <ERR>)
152	{
153		($enum) = ($line =~ /\n\s*enum\s*{\s*([^}]*)};/);
154		while ($enum ne '')	#basic filter for badly formed enums
155		{
156			#Drop leading whitespace
157			$enum =~ s/^\s+//;
158	#	print "A:", $enum,"\n";
159			($leadingcomment) = ($enum =~ m%^(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)%);
160			if ($leadingcomment ne '')
161			{
162				$enum = substr($enum, length($leadingcomment));
163				$leadingcomment = substr($leadingcomment, 2);		# drop leading "/*"
164				$leadingcomment = substr($leadingcomment, 0, -2);	# drop trailing "*/"
165				$leadingcomment = cleanupComment($leadingcomment);
166			}
167			next if ($enum eq '');	#basic filter for badly formed enums
168
169			# Check for C++ style comments at start of line
170			if ($enum =~ /\s*(\/\/)/)
171			{
172				#Drop everything before the end of line
173				$enum =~ s/[^\n]*[\n]*//;
174				next;
175			}
176			($identifier) = ($enum =~ /\s*([_A-Za-z][_A-Za-z0-9]*)/);
177
178#			print "identifier: ", $identifier,"\n" if ($identifier ne '');
179
180			#Drop everything before the comma
181			$enum =~ s/[^,]*,//;
182
183			# Now look for trailing comment. We only consider them
184			# trailing if they come before the end of the line
185			($trailingcomment) = ($enum =~ /^[ \t]*\/\*((.)*)?\*\//);
186		#	($trailingcomment) = ($enum =~ m%^(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)%);
187			$trailingcomment = cleanupComment($trailingcomment);
188
189			#Drop everything before the end of line
190			$enum =~ s/[^\n]*[\n]*//;
191	#	print "B:", $enum,"\n";
192	#	print "lc:$leadingcomment, id:$identifier, tc:$trailingcomment\n";
193	#	print "===========================================\n";
194
195			writecomment($leadingcomment, $identifier, $trailingcomment);
196		}
197	}
198}
199
200sub writecomment
201{
202	# Leading comment, id, trailing comment
203	# To aid localizers, we will not output a line with no comment
204	#
205	# Output is e.g.
206	#	tmp << "/* errAuthorizationSuccess */\n\"" << errAuthorizationSuccess
207	#		<< "\" = \"The operation completed successfully.\"\n" << endl;
208
209	my($mylc,$myid,$mytc) = @_;
210	if ($myid =~ /(CSSM_ERRCODE|CSSMERR_|errSec|errCS|errAuth|errSSL)[_A-Za-z][_A-Za-z0-9]*/)
211	{
212		$errormessage = '';
213		if ($mytc ne '')
214		{	$errormessage = $mytc; }
215		elsif ($mylc ne '')
216		{	$errormessage = $mylc; }
217		elsif ($GENDEBUGSTRINGS eq "YES")
218		{	$errormessage = $myid; }
219
220		if ($errormessage ne '')
221		{
222			print "\ttmp << \"/* ", $myid, " */\\n\\\"\" << ";
223			print $myid, " << \"\\\" = \\\"";
224			print $errormessage, "\\\";\\n\" << endl;\n";
225		}
226	}
227};
228
229
230sub printAdditionalIncludes
231{
232	#This uses the "here" construct to dump out lines verbatim
233	print <<"AdditionalIncludes";
234
235#include <iostream>
236#include <fstream>
237#include <CoreFoundation/CoreFoundation.h>
238#include <Foundation/Foundation.h>
239
240using namespace std;
241AdditionalIncludes
242}
243
244sub printInputIncludes
245{
246	#Now "#include" each of the input files
247	print "\n#include \"$_\"" foreach @INPUTFILES;
248	print "\n";
249}
250
251sub printMainProgram
252{
253	#Output the main part of the program using the "here" construct
254	print <<"MAINPROGRAM";
255
256void writeStrings(const char *stringsFileName);
257void createStringsTemp();
258
259int main (int argc, char * const argv[])
260{
261	const char *stringsFileName = NULL;
262
263	if (argc == 2)
264		stringsFileName = argv[1];
265	else
266	if (argc == 1)
267		stringsFileName = "SecErrorMessages.strings";
268	else
269		return -1;
270
271	cout << "Strings file to create: " << stringsFileName << endl;
272	createStringsTemp();
273	writeStrings(stringsFileName);
274}
275
276void writeStrings(const char *stringsFileName)
277{
278	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
279	NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:@"generateErrStrings.tmp"];
280	NSData *rawstrings = [fh readDataToEndOfFile];
281	UInt32 encoding = CFStringConvertEncodingToNSStringEncoding (kCFStringEncodingUTF8);
282	NSString *instring = [[NSString alloc] initWithData:rawstrings encoding:(NSStringEncoding)encoding];
283
284	if (instring)
285	{
286		NSString *path = [NSString stringWithUTF8String:stringsFileName];
287		NSFileManager *fm = [NSFileManager defaultManager];
288		if ([fm fileExistsAtPath:path])
289			[fm removeFileAtPath:path handler:nil];
290		BOOL bx = [fm createFileAtPath:path contents:nil attributes:nil];
291		NSFileHandle *fs = [NSFileHandle fileHandleForWritingAtPath:path];
292		[fs writeData:[instring dataUsingEncoding:NSUnicodeStringEncoding]];
293	}
294
295	[pool release];
296}
297
298void createStringsTemp()
299{
300	ofstream tmp("generateErrStrings.tmp") ;
301
302MAINPROGRAM
303}
304
305sub cleanupComment
306{
307	my $comment = shift @_;
308#	print "A:",$comment,"\n";
309	if ($comment ne '')
310	{
311		$comment =~ s/\s\s+/ /g; 	# Squeeze multiple spaces to one
312		$comment =~ s/^\s+//;		# Drop leading whitespace
313		$comment =~ s/\s+$//;		# Drop trailing whitespace
314		$comment =~ s/[\"]/\\\\\\"/g; 	# Replace double quotes with \" (backslash is sextupled to make it through regex and printf)
315	}
316#	print "B:",$comment,"\n";
317	$comment;
318}
319
320sub printTrailer
321{
322	print "	tmp.close();\n";
323	print "}\n";
324}
325
326sub compileLinkAndRun
327{
328	$status = system( <<"MAINPROGRAM");
329(cd ${TMPDIR} ; /usr/bin/cc -x objective-c++  -pipe -Wno-trigraphs -fpascal-strings -fasm-blocks -g -O0 -Wreturn-type -fmessage-length=0 -F$ENV{'BUILT_PRODUCTS_DIR'} -I$ENV{'BUILT_PRODUCTS_DIR'}/SecurityPieces/Headers -I$ENV{'BUILT_PRODUCTS_DIR'}/SecurityPieces/PrivateHeaders -c generateErrStrings.mm -o generateErrStrings.o)
330MAINPROGRAM
331	die "$compile exited funny: $?" unless $status == 0;
332
333	$status = system( <<"LINKERSTEP");
334(cd ${TMPDIR} ; /usr/bin/clang++ -o generateErrStrings generateErrStrings.o -framework Foundation )
335LINKERSTEP
336	die "$linker exited funny: $?" unless $status == 0;
337
338	$status = system( <<"RUNSTEP");
339(cd ${TMPDIR} ; ./generateErrStrings $TARGETSTR )
340RUNSTEP
341	die "$built program exited funny: $?" unless $status == 0;
342}
343
344