#!/usr/bin/perl # # Copyright (c) 2003-2004,2006,2008,2012,2014 Apple 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@ # # generateErrStrings.pl - create error strings files from the Security header files # # Usage: # # perl generateErrStrings.pl <GENDEBUGSTRS?> <NAME_OF_STRINGS_FILE> <input files> # # Currently supported files are SecBase.h, SecureTransport.h,cssmapple.h, # cssmerr.h and Authorization.h. These are used by: # # void cssmPerror(const char *how, CSSM_RETURN error); # # which is in SecBase.cpp. # # Paths of input files: # # ./libsecurity_authorization/lib/Authorization.h # ./libsecurity_cssm/lib/cssmapple.h # ./libsecurity_cssm/lib/cssmerr.h # ./libsecurity_keychain/lib/SecBase.h # ./libsecurity_ssl/lib/SecureTransport.h # # Sample run: # # perl generateErrStrings.pl "YES" "SecErrorMessages.strings" Authorization.h SecBase.h \ # cssmapple.h cssmerr.h SecureTransport.h # # Input to script: header file(s) containing enum declarations # Output: C++ program with one cout statement per decl # # The input headers are scanned for enums containing error numbers and # optional comments. Only certain prefixes for the identifiers in the # enums are considered, to avoid non-error message type defines. See # the line in the file with CSSM_ERRCODE for acceptable prefixes. # # There are three styles of comments that this script parses: # # Style A [see /System/Library/Frameworks/Security.framework/Headers/SecBase.h]: # # errSSLProtocol = -9800, /* SSL protocol error */ # # Style B [see /System/Library/Frameworks/Security.framework/Headers/cssmapple.h]: # # /* a code signature match failed */ # CSSMERR_CSP_APPLE_SIGNATURE_MISMATCH = CSSM_CSP_PRIVATE_ERROR + 2, # # Style C [see /System/Library/Frameworks/Security.framework/Headers/cssmerr.h]: # # CSSM_CSSM_BASE_CSSM_ERROR = # CSSM_CSSM_BASE_ERROR + CSSM_ERRORCODE_COMMON_EXTENT + 0x10, # CSSMERR_CSSM_SCOPE_NOT_SUPPORTED = CSSM_CSSM_BASE_CSSM_ERROR + 1, # # Style A has the comment after the comment. Style has the comment before the value, # and Style C has no comment. In cases where both Style A and B apply, the # comment at the end of the line is used. # # The final output after the generated Objective-C++ program is run looks like: # # /* errSSLProtocol */ # "-9800" = "SSL protocol error"; # # /* errSSLNegotiation */ # "-9801" = "Cipher Suite negotiation failure"; # # The appropriate byte order marker for UTF-16 is written to the start of the file. # Note that the list of errors must be numerically unique across all input files, # or the strings file will be invalid. Comments in "Style B" may span multiple lines. # C++ style comments are not supported. Double quotes in a comment are hardened with # "\" in the output. # # The English versions of the error messages can be seen with: # # cat /System/Library/Frameworks/Security.framework/Resources/English.lproj/SecErrorMessages.strings # # find -H -X -x . -name "*.h" -print0 2>/dev/null | xargs -0 grep -ri err # ----------------------------------------------------------------------------------- # Style questions: # - what should I make PROGNAME? # - should I use a special call to make the temp file in the .mm file? # #use strict; #use warnings; die "Usage: $0 <gendebug> <tmpdir> <.strings file> <list of headers>\n" if ($#ARGV < 3); $GENDEBUGSTRINGS=$ARGV[0]; # If "YES", include all strings & don't localize $TMPDIR=$ARGV[1]; # temporary directory for program compile, link, run $TARGETSTR=$ARGV[2]; # path of .strings file, e.g. # ${DERIVED_SRC}/English.lproj/SecErrorMessages.strings @INPUTFILES=@ARGV[3 .. 9999]; # list of input files $#INPUTFILES = $#ARGV - 3; # truncate to actual number of files print "gend: $GENDEBUGSTRINGS, tmpdir: $TMPDIR, targetstr: $TARGETSTR\n"; $PROGNAME="${TMPDIR}/generateErrStrings.mm"; open PROGRAM,"> $PROGNAME" or die "can't open $PROGNAME: $!"; select PROGRAM; printAdditionalIncludes(); printInputIncludes(); printMainProgram(); # ----------------------------------------------------------------------------------- # Parse error headers and build array of all relevant lines open(ERR, "cat " . join(" ", @INPUTFILES) . "|") or die "Cannot open error header files"; $/="\};"; #We set the section termination string - very important processInput(); close(ERR); # ----------------------------------------------------------------------------------- printTrailer(); select STDOUT; close PROGRAM; compileLinkAndRun(); # 4: Done! exit; # ----------------------------------------------------------------------------------- # Subroutines # ----------------------------------------------------------------------------------- sub processInput { # 3: Read input, process each line, output it. while ( $line = <ERR>) { ($enum) = ($line =~ /\n\s*(?:enum|CF_ENUM\(OSStatus\))\s*{\s*([^}]*)};/); while ($enum ne '') #basic filter for badly formed enums { #Drop leading whitespace $enum =~ s/^\s+//; # print "A:", $enum,"\n"; ($leadingcomment) = ($enum =~ m%^(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)%); if ($leadingcomment ne '') { $enum = substr($enum, length($leadingcomment)); $leadingcomment = substr($leadingcomment, 2); # drop leading "/*" $leadingcomment = substr($leadingcomment, 0, -2); # drop trailing "*/" $leadingcomment = cleanupComment($leadingcomment); } next if ($enum eq ''); #basic filter for badly formed enums # Check for C++ style comments at start of line if ($enum =~ /\s*(\/\/)/) { #Drop everything before the end of line $enum =~ s/[^\n]*[\n]*//; next; } ($identifier) = ($enum =~ /\s*([_A-Za-z][_A-Za-z0-9]*)/); # print "identifier: ", $identifier,"\n" if ($identifier ne ''); #Drop everything before the comma $enum =~ s/[^,]*,//; # Now look for trailing comment. We only consider them # trailing if they come before the end of the line ($trailingcomment) = ($enum =~ /^[ \t]*\/\*((.)*)?\*\//); # ($trailingcomment) = ($enum =~ m%^(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)%); $trailingcomment = cleanupComment($trailingcomment); #Drop everything before the end of line $enum =~ s/[^\n]*[\n]*//; # print "B:", $enum,"\n"; # print "lc:$leadingcomment, id:$identifier, tc:$trailingcomment\n"; # print "===========================================\n"; writecomment($leadingcomment, $identifier, $trailingcomment); } } } sub writecomment { # Leading comment, id, trailing comment # To aid localizers, we will not output a line with no comment # # Output is e.g. # tmp << "/* errAuthorizationSuccess */\n\"" << errAuthorizationSuccess # << "\" = \"The operation completed successfully.\"\n" << endl; my($mylc,$myid,$mytc) = @_; if ($myid =~ /(CSSM_ERRCODE|CSSMERR_|errSec|errCS|errAuth|errSSL)[_A-Za-z][_A-Za-z0-9]*/) { $errormessage = ''; if ($mytc ne '') { $errormessage = $mytc; } elsif ($mylc ne '') { $errormessage = $mylc; } elsif ($GENDEBUGSTRINGS eq "YES") { $errormessage = $myid; } if ($errormessage ne '') { print "\ttmp << \"/* ", $myid, " */\\n\\\"\" << "; print $myid, " << \"\\\" = \\\""; print $errormessage, "\\\";\\n\" << endl;\n"; } } }; sub printAdditionalIncludes { #This uses the "here" construct to dump out lines verbatim print <<"AdditionalIncludes"; #include <iostream> #include <fstream> #include <CoreFoundation/CoreFoundation.h> #include <Foundation/Foundation.h> using namespace std; AdditionalIncludes } sub printInputIncludes { #Now "#include" each of the input files print "\n#include \"$_\"" foreach @INPUTFILES; print "\n"; } sub printMainProgram { #Output the main part of the program using the "here" construct print <<"MAINPROGRAM"; void writeStrings(const char *stringsFileName); void createStringsTemp(); int main (int argc, char * const argv[]) { const char *stringsFileName = NULL; if (argc == 2) stringsFileName = argv[1]; else if (argc == 1) stringsFileName = "SecErrorMessages.strings"; else return -1; cout << "Strings file to create: " << stringsFileName << endl; createStringsTemp(); writeStrings(stringsFileName); } void writeStrings(const char *stringsFileName) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:@"generateErrStrings.tmp"]; NSData *rawstrings = [fh readDataToEndOfFile]; UInt32 encoding = CFStringConvertEncodingToNSStringEncoding (kCFStringEncodingUTF8); NSString *instring = [[NSString alloc] initWithData:rawstrings encoding:(NSStringEncoding)encoding]; if (instring) { NSString *path = [NSString stringWithUTF8String:stringsFileName]; NSFileManager *fm = [NSFileManager defaultManager]; if ([fm fileExistsAtPath:path]) [fm removeItemAtPath:path error:NULL]; BOOL bx = [fm createFileAtPath:path contents:nil attributes:nil]; NSFileHandle *fs = [NSFileHandle fileHandleForWritingAtPath:path]; [fs writeData:[instring dataUsingEncoding:NSUnicodeStringEncoding]]; } [pool release]; } void createStringsTemp() { ofstream tmp("generateErrStrings.tmp") ; MAINPROGRAM } sub cleanupComment { my $comment = shift @_; # print "A:",$comment,"\n"; if ($comment ne '') { $comment =~ s/\s\s+/ /g; # Squeeze multiple spaces to one $comment =~ s/^\s+//; # Drop leading whitespace $comment =~ s/\s+$//; # Drop trailing whitespace $comment =~ s/[\"]/\\\\\\"/g; # Replace double quotes with \" (backslash is sextupled to make it through regex and printf) } # print "B:",$comment,"\n"; $comment; } sub printTrailer { print " tmp.close();\n"; print "}\n"; } sub compileLinkAndRun { $status = system( <<"MAINPROGRAM"); (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) MAINPROGRAM die "$compile exited funny: $?" unless $status == 0; $status = system( <<"LINKERSTEP"); (cd ${TMPDIR} ; /usr/bin/clang++ -o generateErrStrings generateErrStrings.o -framework Foundation ) LINKERSTEP die "$linker exited funny: $?" unless $status == 0; $status = system( <<"RUNSTEP"); (cd ${TMPDIR} ; ./generateErrStrings $TARGETSTR ) RUNSTEP die "$built program exited funny: $?" unless $status == 0; }