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