├── conversion ├── idmap.oracle ├── rules ├── convert ├── idmap.forgerock ├── README.md └── idmap.sun ├── bbedit ├── LDIF.bblm.zip ├── LDIF │ ├── LDIF.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcuserdata │ │ │ │ └── cjr.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ ├── xcuserdata │ │ │ └── cjr.xcuserdatad │ │ │ │ └── xcschemes │ │ │ │ └── xcschememanagement.plist │ │ ├── xcshareddata │ │ │ └── xcschemes │ │ │ │ └── LDIF.xcscheme │ │ └── project.pbxproj │ ├── Info.plist │ ├── Interfaces │ │ └── Language Modules │ │ │ ├── BBLMToken.h │ │ │ ├── BBLMTextIterator.h │ │ │ ├── BBLMTextUtils.h │ │ │ ├── BBLMTextUtils.mm │ │ │ └── BBLMInterface.h │ └── LDIF │ │ └── LDIF.mm ├── jstack.plist ├── README.md └── LDIF.plist ├── .gitignore ├── README.md ├── watchdn ├── dumpber ├── topfilters ├── decodecsn ├── slowops └── topology2dot /conversion/idmap.oracle: -------------------------------------------------------------------------------- 1 | mmarie = Mathieu Marie 2 | -------------------------------------------------------------------------------- /bbedit/LDIF.bblm.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisridd/opendj-utils/HEAD/bbedit/LDIF.bblm.zip -------------------------------------------------------------------------------- /bbedit/LDIF/LDIF.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | blib/ 2 | .build/ 3 | _build/ 4 | cover_db/ 5 | inc/ 6 | Build 7 | Build.bat 8 | .last_cover_stats 9 | Makefile 10 | Makefile.old 11 | MANIFEST.bak 12 | META.yml 13 | MYMETA.yml 14 | nytprof.out 15 | pm_to_blib 16 | .DS_Store 17 | -------------------------------------------------------------------------------- /bbedit/LDIF/LDIF.xcodeproj/project.xcworkspace/xcuserdata/cjr.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisridd/opendj-utils/HEAD/bbedit/LDIF/LDIF.xcodeproj/project.xcworkspace/xcuserdata/cjr.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /conversion/rules: -------------------------------------------------------------------------------- 1 | create repository opendj 2 | end repository 3 | 4 | match /branches/README 5 | end match 6 | 7 | match /tags/README 8 | end match 9 | 10 | match /README 11 | end match 12 | 13 | match /trunk/ 14 | repository opendj 15 | branch master 16 | end match 17 | 18 | match /branches/([^/]+)/ 19 | repository opendj 20 | branch \1 21 | end match 22 | 23 | match /tags/([^/]+)/ 24 | action ignore 25 | end match 26 | -------------------------------------------------------------------------------- /conversion/convert: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Convert the opendj repository from subversion to git 3 | 4 | tool=svn-all-fast-export 5 | repo=opendj-svn 6 | 7 | rm -fr opendj 8 | 9 | # Ranges of commits need different identity-maps. 10 | # This shows one way to achieve this. 11 | 12 | # From start committers are from sun.com 13 | $tool --rules rules --max-rev 6616 --identity-map idmap.sun $repo 14 | # Around r???? committers are from oracle.com 15 | #$tool --rules rules --max-rev 6616 --identity-map idmap.oracle $repo 16 | # Around r6617 committers are from forgerock.com 17 | $tool --rules rules --resume-from 6617 --identity-map idmap.forgerock $repo 18 | -------------------------------------------------------------------------------- /conversion/idmap.forgerock: -------------------------------------------------------------------------------- 1 | mark = Mark Craig 2 | ludo = Ludovic Poitou 3 | gary.williams = Gary Williams 4 | cjr = Chris Ridd 5 | matthew = Matthew Swift 6 | miroslavfadrhonc = Miroslav Fadrhonc 7 | n4al = Nemanja Lukić 8 | cgp = German Parente 9 | violette = Violette Roche-Montané 10 | csovant = Christophe Sovant 11 | JnRouvignac = Jean-Noël Rouvignac 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | opendj-utils 2 | ============ 3 | 4 | Some small utilities for the [OpenDJ LDAP server](http://opendj.forgerock.org/). 5 | 6 | * slowops - analyzes operation times in access logs 7 | * topfilters - analyze search filters in OpenDJ logs 8 | * watchdn - watch for operations affecting a given DN in access logs 9 | * decodecsn - decodes replication CSNs 10 | * topology2dot - displays a replication topology in Graphviz DOT format 11 | * dumpber - pretty-print BER-encoded files 12 | 13 | The bbedit directory contains some useful things if you use a Mac and BBEdit or 14 | TextWrangler. 15 | 16 | Ludovic Poitou has some tools on [github too](https://github.com/ludomp/opendj-utils). 17 | 18 | Note these are all unofficial and not supported in any way by ForgeRock. If 19 | they work and they're useful: great! If not, patches are welcome... 20 | -------------------------------------------------------------------------------- /bbedit/LDIF/LDIF.xcodeproj/xcuserdata/cjr.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | LDIF.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | Setext copy.xcscheme 13 | 14 | orderHint 15 | 1 16 | 17 | Setext.xcscheme 18 | 19 | orderHint 20 | 0 21 | 22 | 23 | SuppressBuildableAutocreation 24 | 25 | 5F05133905A0957400BD0E76 26 | 27 | primary 28 | 29 | 30 | 67ED5E271C85FBAA0051CA0A 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /conversion/README.md: -------------------------------------------------------------------------------- 1 | Convert from svn to git 2 | ======================= 3 | 4 | Currently the OpenDJ repository is held in subversion. This is a set of scripts 5 | using the svn-all-fast-export tool from KDE that converts it (one-way) to a 6 | nice clean git repository. 7 | 8 | I originally discovered this tool [here](http://blog.smartbear.com/software-quality/bid/170525/Migrating-from-Subversion-to-Git-Lessons-Learned) 9 | after struggling with git-svn. 10 | 11 | That tool is pretty difficult to build if you don't have exactly the right 12 | versions of KDE/Qt/etc, so for simplicity I run this in Ubuntu 12. The 13 | converted repo obviously doesn't require Linux. 14 | 15 | The first thing you want is a local copy of the whole SVN repo. I put this in 16 | opendj-svn. This has all the branches, tags, everything. 17 | 18 | Then just run the `convert` script. It takes a few minutes, and uses the rules 19 | in `rules` to get around various oddities in the svn repo. This is quite amazing 20 | because git-svn takes about 10 hours to do the same conversion! 21 | 22 | The main difficulty in the conversion is handled by running the tool in several 23 | steps. It does this because the committer names used by svn should be converted 24 | into "correct" email addresses for git, and over time the same committer names 25 | have worked for Sun, Oracle, and ForgeRock. I set up 3 `idmap.*` files to cover 26 | these three periods of time. I know the boundaries between all three periods is 27 | not correct. Corrections are welcomed! 28 | 29 | -------------------------------------------------------------------------------- /bbedit/jstack.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | BBEditDocumentType 21 | CodelessLanguageModule 22 | BBLMColorsSyntax 23 | 24 | BBLMIsCaseSensitive 25 | 26 | BBLMPredefinedNameList 27 | 28 | TIMED_WAITING 29 | RUNNABLE 30 | WAITING 31 | 32 | BBLMLanguageCode 33 | JSTK 34 | BBLMLanguageDisplayName 35 | Java Jstack 36 | BBLMSuffixMap 37 | 38 | 39 | BBLMLanguageSuffix 40 | .jstack 41 | BBLMIsSourceKind 42 | 43 | 44 | 45 | BBLMScansFunctions 46 | 47 | 48 | Language Features 49 | 50 | Function Pattern 51 | 54 | ^"(?P.*?)"\s(daemon\s)?prio= 55 | .*? 56 | ^$ 57 | ) 58 | ) 59 | ]]> 60 | 61 | Identifier and Keyword Character Class 62 | A-Za-z0-9_.;"- 63 | 64 | 65 | -------------------------------------------------------------------------------- /bbedit/README.md: -------------------------------------------------------------------------------- 1 | BBEdit Language Modules 2 | ======================= 3 | 4 | I've written two language modules for BBEdit (and TextWrangler) that help you 5 | navigate through LDAP Data Interchange Format (LDIF) files. 6 | 7 | The first version I wrote was a "Codeless Language Module", basically a bunch 8 | of regular expressions. It understands commenting, and tries to populate the 9 | "function" list with the DN of each entry. This makes it pretty convenient to 10 | move around the LDIF file. 11 | 12 | Copy LDIF.plist into ~/Library/Application Support/BBEdit/Language Modules/ (or 13 | ~/Library/Application Support/TextWrangler/Language Modules/) and restart 14 | BBEdit/TextWrangler to use the first version. 15 | 16 | The line-oriented structure of LDIF means it is impossible to do proper 17 | keyword colouring or to handle wrapped lines in LDIF correctly in a codeless 18 | language module, so I wrote a second version in C++. 19 | 20 | Open LDIF.xcodeproj in Xcode (5 or 6) and build it. The target LDIF.bblm will 21 | be somewhere in your ~/Library/Developer/Xcode/DerivedData/ directory. Copy it 22 | into ~/Library/Application Support/BBEdit/Language Modules/ (or 23 | ~/Library/Application Support/TextWrangler/Language Modules/). Remove LDIF.plist 24 | if you've installed it first and restart BBEdit/TextWrangler. 25 | 26 | If you don't want to build it yourself, a pre-built binary is in the 27 | LDIF.bblm.zip file. Unzip it into the correct Language Modules directory in the 28 | previous paragraph. 29 | 30 | This version understands wrapped lines, and does some smarter keyword colouring. 31 | It will decode any base64-encoded DNs before putting them in the function list. 32 | 33 | There's an extra codeless language module for easily navigating Java jstack 34 | output. There's no standard filename extension for these. 35 | 36 | Install the jstack.plist file into the Language Modules directory as described 37 | above. 38 | -------------------------------------------------------------------------------- /conversion/idmap.sun: -------------------------------------------------------------------------------- 1 | neil_a_wilson = Neil A. Wilson 2 | el_kaboing = Mike Keyes 3 | gbellato = Gilles Bellaton 4 | coulbeck = Andy Coulbeck 5 | matthew_swift = Matthew Swift 6 | behret = Brian Ehret 7 | al_xipe = Arnaud Lacour 8 | boli = Bo Li 9 | dugan = Mike Dugan 10 | davidely = David Ely 11 | lutoff = Daniel Lutoff 12 | gary_williams = Gary Williams 13 | tdj_tx = Tom Jones 14 | jvergara = Josu Vergara 15 | treydrake = Trey Drake 16 | pgamba = Philippe GAMBA 17 | sin = Kunal Sinha 18 | david_page = David Scott Page 19 | abobrov = Anton Bobrov 20 | kenneth_suter = Kenneth Suter 21 | jdemendi = Joseph de Menditte 22 | ugaston = Unai Gaston Caminos 23 | mkeyes = Mike Keyes 24 | smaguin = Sylvie Maguin 25 | shankar_mbn = Shankar Gowda 26 | sgouvern = Sylvie Gouverneyre-Chambert 27 | schwing = Jean-Luc Schwing 28 | sberthol = Sebastien Bertholet 29 | rhaggard = Ragan Haggard 30 | ooudghir = Olivier Oudghiri 31 | mrossign = Mathieu Rossignol 32 | maudj = Maud Jamati-Bartlett 33 | madiot = Pierre-Ephrem Madiot 34 | sshoaff = Stephen Shoaff 35 | jpikus = Jeanine Pikus 36 | jcduff = Christopher Duff 37 | ludovicp = Ludovic Poitou 38 | -------------------------------------------------------------------------------- /watchdn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # Find all operations relating to a particular DN 3 | 4 | use strict; 5 | use warnings; 6 | 7 | $::VERSION = 0.1; 8 | 9 | sub version() 10 | { 11 | print STDERR "$0 version $::VERSION\n"; 12 | exit 0; 13 | } 14 | 15 | sub usage() 16 | { 17 | print STDERR "Usage: $0 [-v] [-h] dn\n"; 18 | exit 1; 19 | } 20 | 21 | my %opt = ( 22 | 'h' => 0, # Help 23 | 'v' => 0, # Version 24 | ); 25 | 26 | my $dn = "cn=Directory Manager"; 27 | 28 | while (@ARGV) { 29 | my $arg = shift @ARGV; 30 | if ($arg =~ m{^-h$|^--help$}) { 31 | $opt{h} = 1; 32 | } elsif ($arg =~ m{^-v$|^--version$}) { 33 | $opt{v} = 1; 34 | } else { 35 | $dn = $arg; 36 | last; 37 | } 38 | } 39 | 40 | version if $opt{v}; 41 | usage if $opt{h}; 42 | 43 | my %tuple; 44 | 45 | while(<>) { 46 | chomp; 47 | if (my ($t) = m{ REQ (conn=[-0-9]+ op=[0-9]+ msgID=[0-9]+).*(base|dn|authDN)="$dn"}i) { 48 | print "$_\n"; 49 | $tuple{$t} = 1; 50 | next; 51 | } 52 | if (my ($t) = m{ RES (conn=[-0-9]+ op=[0-9]+ msgID=[0-9]+) }) { 53 | print "$_\n" if exists $tuple{$t}; 54 | delete $tuple{$t}; 55 | next; 56 | } 57 | } 58 | 59 | 1; 60 | 61 | __END__ 62 | 63 | =head1 NAME 64 | 65 | watchdn - analyze operations for a particular DN in OpenDJ log files 66 | 67 | =head1 SYNOPSIS 68 | 69 | watchdn dn 70 | 71 | watchdn -h 72 | 73 | watchdn -v 74 | 75 | =head1 DESCRIPTION 76 | 77 | OpenDJ access log files record the requests and result for LDAP operations on 78 | separate log lines by default. With a busy server, this can make following the 79 | operations on a particular DN somewhat challenging. 80 | 81 | This tool reports all operations and results affecting a given DN. 82 | 83 | =head1 OPTIONS 84 | 85 | =over 86 | 87 | =item B<-h> display help text. 88 | 89 | =item B<-v> report the tool version. 90 | 91 | =item B the LDAP DN that needs to be monitored. 92 | 93 | =back 94 | 95 | =head1 AUTHOR 96 | 97 | Chris Ridd Echris.ridd@forgerock.comE 98 | 99 | =head1 COPYRIGHT 100 | 101 | Copyright (c) 2014 Chris Ridd. All rights reserved. This tool is free software; 102 | you can redistribute it and/or modify it under the same terms as Perl itself. 103 | -------------------------------------------------------------------------------- /bbedit/LDIF.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | BBEditDocumentType 21 | CodelessLanguageModule 22 | BBLMColorsSyntax 23 | 24 | BBLMIsCaseSensitive 25 | 26 | BBLMPredefinedNameList 27 | 28 | dn 29 | control 30 | changetype 31 | add 32 | modify 33 | replace 34 | delete 35 | 36 | BBLMLanguageCode 37 | LDIF 38 | BBLMLanguageDisplayName 39 | LDAP Data Interchange Format 40 | BBLMSuffixMap 41 | 42 | 43 | BBLMLanguageSuffix 44 | .ldif 45 | BBLMIsSourceKind 46 | 47 | 48 | 49 | BBLMScansFunctions 50 | 51 | BBLMCommentLineDefault 52 | # 53 | 54 | Language Features 55 | 56 | Open Line Comments 57 | # 58 | 59 | Function Pattern 60 | 63 | dn:\s+(?P.*?$) 64 | .*? 65 | ^$ 66 | ) 67 | ) 68 | ]]> 69 | 70 | Comment Pattern 71 | 76 | 77 | Skip Pattern 78 | comment) 81 | ) 82 | ]]> 83 | Identifier and Keyword Character Class 84 | A-Za-z0-9_.;- 85 | 86 | 87 | -------------------------------------------------------------------------------- /bbedit/LDIF/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | LDIF 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleSignature 18 | BBLM 19 | CFBundleVersion 20 | 8.0 21 | CSResourcesFileMapped 22 | 23 | NSHumanReadableCopyright 24 | Copyright © 2015-2018 Chris Ridd. All rights reserved. 25 | com.barebones.bblminfo 26 | 27 | 28 | BBLMCanGuessLanguage 29 | 30 | BBLMColorsSyntax 31 | 32 | BBLMCommentLineDefault 33 | # 34 | BBLMDroppedFilePathStyle 35 | POSIX 36 | BBLMEntryPointName 37 | LDIF 38 | BBLMFunctionScannerDoesFoldsToo 39 | 40 | BBLMIsCaseSensitive 41 | 42 | BBLMKeywordList 43 | 44 | changetype 45 | add 46 | delete 47 | replace 48 | modify 49 | increment 50 | version 51 | moddn 52 | modrdn 53 | newrdn 54 | deleteoldrdn 55 | newsuperior 56 | dn 57 | control 58 | true 59 | false 60 | 61 | BBLMLanguageCode 62 | LDIF 63 | BBLMLanguageDisplayName 64 | LDAP Data Interchange Format 65 | BBLMScansFunctions 66 | 67 | BBLMSuffixMap 68 | 69 | 70 | BBLMLanguageSuffix 71 | .ldif 72 | 73 | 74 | BBLMUseHTMLFileSearchRules 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /bbedit/LDIF/Interfaces/Language Modules/BBLMToken.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * BBLMToken.h 5 | * Generic token class for language parsing in BBEdit Language Modules 6 | * 7 | * Created by Seth Dillingham on 7/18/06. 8 | * Copyright 2006 Macrobyte Resources. All rights reserved. 9 | * 10 | */ 11 | 12 | #ifndef BBLMToken_h 13 | #define BBLMToken_h 1 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "BBLMTextIterator.h" 21 | 22 | class BBLMToken 23 | { 24 | public: 25 | 26 | #pragma mark - 27 | #pragma mark Public 28 | #pragma mark - 29 | 30 | #pragma mark De-/Constructors 31 | 32 | // default constructor 33 | BBLMToken() 34 | :m_start( 0 ), 35 | m_length( 0 ) 36 | {} 37 | 38 | // current position 39 | BBLMToken( BBLMTextIterator& current_position ) 40 | :m_start( current_position.Offset() ), 41 | m_length( 0 ) 42 | {} 43 | 44 | // current position, length 45 | BBLMToken( BBLMTextIterator& current_position, SInt32 tokLength ) 46 | :m_start( current_position.Offset() - tokLength ), 47 | m_length( tokLength ) 48 | {} 49 | 50 | // start offset, length 51 | BBLMToken( SInt32 tokStart, SInt32 tokLength ) 52 | :m_start( tokStart ), 53 | m_length( tokLength ) 54 | {} 55 | 56 | BBLMToken( const BBLMToken * originalTok ) 57 | :m_start( originalTok->start() ), 58 | m_length( originalTok->length() ) 59 | {} 60 | 61 | BBLMToken( const BBLMToken& originalTok ) 62 | :m_start( originalTok.start() ), 63 | m_length( originalTok.length() ) 64 | {} 65 | 66 | ~BBLMToken() { } 67 | 68 | #pragma mark - 69 | #pragma mark Member Functions 70 | 71 | SInt32 start() const { 72 | return m_start; 73 | } 74 | 75 | 76 | SInt32 length() const { 77 | return m_length; 78 | } 79 | 80 | SInt32 end() const { 81 | return ( ( m_start + m_length > 0 ) ? ( m_start + m_length ) - 1 : -1 ); 82 | } 83 | 84 | void reset() { 85 | m_start = m_length = 0; 86 | } 87 | 88 | NSRange range() const { 89 | return NSMakeRange( m_start, m_length ); 90 | } 91 | 92 | 93 | 94 | protected: 95 | 96 | #pragma mark - 97 | #pragma mark Protected 98 | #pragma mark - 99 | 100 | SInt32 m_start; 101 | 102 | SInt32 m_length; 103 | 104 | private: 105 | 106 | // BBLMToken( const BBLMToken& ); // disallowed 107 | }; 108 | #endif 109 | -------------------------------------------------------------------------------- /dumpber: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | use strict; 4 | use warnings; 5 | use Convert::ASN1; 6 | 7 | $::VERSION = 0.1; 8 | 9 | sub version() 10 | { 11 | print STDERR "$0 version $::VERSION\n"; 12 | exit 0; 13 | } 14 | 15 | sub usage() 16 | { 17 | print STDERR "Usage: $0 [-base64|-hex] [-h] [-v]\n"; 18 | exit 1; 19 | } 20 | 21 | 22 | my %opt = ( 23 | 'base64' => 0, # Base64-encoded input 24 | 'hex' => 0, # Hex-encoded input 25 | 'h' => 0, 26 | 'v' => 0, 27 | ); 28 | 29 | while (@ARGV) { 30 | my $arg = shift @ARGV; 31 | if ($arg =~ m{^-base64$}) { 32 | $opt{base64} = 1; 33 | require MIME::Base64; 34 | } elsif ($arg =~ m{^-hex$}) { 35 | $opt{hex} = 1; 36 | } elsif ($arg =~ m{^-h$|^--help$}) { 37 | $opt{h} = 1; 38 | } elsif ($arg =~ m{^-v$|^--version$}) { 39 | $opt{v} = 1; 40 | } else { 41 | unshift @ARGV, $arg; 42 | last; 43 | } 44 | } 45 | 46 | version if $opt{v}; 47 | usage if $opt{h}; 48 | 49 | my $str; 50 | { 51 | local $/ = undef; 52 | $str = <>; 53 | } 54 | 55 | if ($opt{base64}) { 56 | $str = MIME::Base64::decode($str); 57 | } elsif ($opt{hex}) { 58 | $str =~ s/[^0-9a-fA-F]//g; 59 | $str =~ s/([0-9a-fA-F][0-9a-fA-F])/pack('C',hex $1)/ge; 60 | } 61 | 62 | Convert::ASN1::asn_dump(*STDOUT, $str); 63 | 64 | exit 0; 65 | 66 | __END__ 67 | 68 | =head1 NAME 69 | 70 | dumpber - pretty-print BER-encoded data 71 | 72 | =head1 SYNOPSIS 73 | 74 | dumpber [-base64|-hex] [-h] [-v] 75 | 76 | =head1 DESCRIPTION 77 | 78 | Attempt to decode and pretty-print the BER-encoded input. The input can be 79 | base64- or hex-encoded, but the default is raw binary. 80 | 81 | =head1 OPTIONS 82 | 83 | =over 84 | 85 | =item B<-base64> base64-decode the data first. 86 | 87 | =item B<-hex> hex-decode the data first. 88 | 89 | =item B<-h> display help text. 90 | 91 | =item B<-v> display version information. 92 | 93 | =back 94 | 95 | =head1 EXAMPLE 96 | 97 | $ dumpber /tmp/test.bin 98 | 0000 30: SEQUENCE { 99 | 0002 13: [UNIVERSAL 23] 100 | 0004 : 31 33 30 36 30 36 30 38 32 30 34 39 5A __ __ __ 130606082049Z 101 | 0011 13: [UNIVERSAL 23] 102 | 0013 : 31 33 30 39 30 34 30 38 32 30 34 39 5A __ __ __ 130904082049Z 103 | 0020 : } 104 | 105 | =head1 AUTHOR 106 | 107 | Chris Ridd Echris.ridd@forgerock.comE 108 | 109 | =head1 COPYRIGHT 110 | 111 | Copyright (c) 2013 Chris Ridd. All rights reserved. This tool is free software; 112 | you can redistribute it and/or modify it under the same terms as Perl itself. 113 | -------------------------------------------------------------------------------- /topfilters: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # Find the most popular search filters 3 | 4 | use strict; 5 | use warnings; 6 | 7 | $::VERSION = 0.2; 8 | 9 | sub version() 10 | { 11 | print STDERR "$0 version $::VERSION\n"; 12 | exit 0; 13 | } 14 | 15 | sub usage() 16 | { 17 | print STDERR "Usage: $0\n"; 18 | exit 1; 19 | } 20 | 21 | my %opt = ( 22 | 'h' => 0, # Help 23 | 'v' => 0, # Version 24 | ); 25 | 26 | while (@ARGV) { 27 | my $arg = shift @ARGV; 28 | if ($arg =~ m{^-h$|^--help$}) { 29 | $opt{h} = 1; 30 | } elsif ($arg =~ m{^-v$|^--version$}) { 31 | $opt{v} = 1; 32 | } else { 33 | push @ARGV, $arg; 34 | last; 35 | } 36 | } 37 | 38 | version if $opt{v}; 39 | usage if $opt{h}; 40 | 41 | my %filter; 42 | 43 | while (<>) { 44 | chomp; 45 | if (my ($scope,$filter) = m{SEARCH REQ.* scope=(\S+) filter="([^\"]+)"}) { 46 | next if $scope =~ m{baseObject}; 47 | $filter = lc($filter); 48 | $filter =~ s{\(([a-z0-9_-]+)=[^\*\)]+\*?\)}{($1=EQUALITY)}gi; 49 | $filter =~ s{\(([a-z0-9_-]+)>=[^\*\)]+\)}{($1=ORDERING)}gi; 50 | $filter =~ s{\(([a-z0-9_-]+)<=[^\*\)]+\)}{($1=ORDERING)}gi; 51 | $filter =~ s{\(([a-z0-9_-]+)=\*\)}{($1=PRESENCE)}gi; 52 | $filter =~ s{\(([a-z0-9_-]+)=[^\*]*\*[^\)]*\)}{($1=SUBSTRING)}gi; 53 | $filter{$filter}++; 54 | } 55 | } 56 | 57 | format STDOUT_TOP = 58 | frequency filter 59 | . 60 | format = 61 | @>>>>>>>>> ^* 62 | $::freq, $::filt 63 | . 64 | 65 | foreach our $filt (sort { $filter{$b} <=> $filter{$a} } keys(%filter)) { 66 | our $freq = $filter{$filt}; 67 | write; 68 | } 69 | 70 | 1; 71 | 72 | __END__ 73 | 74 | =head1 NAME 75 | 76 | topfilters - analyze search filters in OpenDJ logs 77 | 78 | =head1 SYNOPSIS 79 | 80 | topfilters 81 | 82 | topfilters -h 83 | 84 | topfilters -v 85 | 86 | =head1 DESCRIPTION 87 | 88 | OpenDJ access log files include the filters used for all search operations. 89 | Filters for baseObject searches are not indexable, but all others are. 90 | 91 | This tool outputs a list of filter types, ordered by frequency. Note this does 92 | not mean that the server requires an index for every term, so the output is only 93 | a guide to what might need indexing. 94 | 95 | =head1 OPTIONS 96 | 97 | =over 98 | 99 | =item B<-h> display help text. 100 | 101 | =item B<-v> report the tool version. 102 | 103 | =back 104 | 105 | =head1 EXAMPLE 106 | 107 | $ topfilters < access 108 | frequency filter 109 | 3002 (objectclass=EQUALITY) 110 | 430 (objectclass=PRESENCE) 111 | 25 (ds-task-id=EQUALITY) 112 | 7 (telephonenumber=EQUALITY) 113 | 1 (&(cn=PRESENCE)(telephonenumber=EQUALITY)) 114 | 115 | =head1 SEE ALSO 116 | 117 | L 118 | 119 | =head1 AUTHOR 120 | 121 | Chris Ridd Echris.ridd@forgerock.comE 122 | 123 | =head1 COPYRIGHT 124 | 125 | Copyright (c) 2013-2022 Chris Ridd. All rights reserved. This tool is free software; 126 | you can redistribute it and/or modify it under the same terms as Perl itself. 127 | -------------------------------------------------------------------------------- /bbedit/LDIF/LDIF.xcodeproj/xcshareddata/xcschemes/LDIF.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 61 | 62 | 63 | 64 | 65 | 66 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /decodecsn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | use strict; 4 | use warnings; 5 | use bignum qw(hex); 6 | 7 | sub usage() { 8 | print STDERR "Usage: $0 [-z timezone] csn [...]\n"; 9 | exit 1; 10 | } 11 | 12 | usage unless $#ARGV >= 0; 13 | 14 | my $zone; 15 | 16 | if ($ARGV[0] eq "-z") { 17 | shift @ARGV; 18 | $zone = shift @ARGV; 19 | usage unless defined $zone; 20 | } 21 | 22 | if (eval{require DateTime}) { 23 | $zone = DateTime::TimeZone->new(name => defined($zone) ? $zone : 'local'); 24 | } elsif (eval{require POSIX}) { 25 | $zone = undef; 26 | } else { 27 | die "Cannot convert times due to missing modules (DateTime is preferred)\n" 28 | } 29 | 30 | sub ts2timestr($) { 31 | my $ts_dec = shift; 32 | 33 | if (defined $zone) { 34 | my $time = DateTime->from_epoch( epoch => $ts_dec / 1000 ); # UTC 35 | $time->set_time_zone($zone); 36 | return $time->strftime("%a %b %e %Y %H:%M:%S.%3N %Z"); 37 | } 38 | my $ms = sprintf "%03d", $ts_dec % 1000; 39 | return POSIX::strftime("%a %b %e %Y %H:%M:%S.$ms %z", localtime($ts_dec / 1000)); 40 | } 41 | 42 | foreach (@ARGV) { 43 | my ($ts,$id,$no,$id_len); 44 | if (($ts,$id,$no) = m{^0000(............)(....)(........)$}) { 45 | # CSNs pre 6.5.0 46 | my $ts_dec = hex($ts); # Value is 64-bits hence using bignum 47 | my $id_dec = hex($id); 48 | my $no_dec = hex($no); 49 | my $ts_str = ts2timestr $ts_dec; 50 | print "CSNv1 $_\n ts=$ts ($ts_dec) $ts_str\n id=$id ($id_dec)\n no=$no ($no_dec)\n"; 51 | } elsif (($id_len,$ts,$no,$id) = m{^01(..)(............)(........)(.*)}) { 52 | # CSNs from 6.5.0 onwards 53 | if (length($id) != hex($id_len)) { 54 | warn "Not a CSN: $_\n"; 55 | next; 56 | } 57 | my $ts_dec = hex($ts); # Value is 64-bits hence using bignum 58 | my $no_dec = hex($no); 59 | my $ts_str = ts2timestr $ts_dec; 60 | print "CSNv2 $_\n ts=$ts ($ts_dec) $ts_str\n id=$id\n no=$no ($no_dec)\n"; 61 | } else { 62 | warn "Not a CSN: $_\n"; 63 | } 64 | } 65 | 66 | exit 0; 67 | 68 | __END__ 69 | 70 | =head1 NAME 71 | 72 | decodecsn - decode OpenDJ change sequence numbers 73 | 74 | =head1 SYNOPSIS 75 | 76 | decodecsn [-z timezone] csn [...] 77 | 78 | =head1 DESCRIPTION 79 | 80 | OpenDJ uses change sequence numbers to record information about replicated 81 | changes. The CSN format is structured and can sometimes be usefully analyzed. 82 | 83 | This tool decodes CSNs. 84 | 85 | =head1 OPTIONS 86 | 87 | =over 88 | 89 | =item B<-z> use the following timezone name or offset instead of localtime to 90 | display the date and time in the CSN. This uses the perl DateTime module. If 91 | that module isn't installed this flag will be ignored and localtime will be 92 | used. 93 | 94 | =item B a change sequence number encoded in hex. 95 | 96 | =back 97 | 98 | =head1 EXAMPLES 99 | 100 | $ decodecsn 0000014bcd10fe1562cd0083a482 101 | 102 | CSNv1 0000014bcd10fe1562cd0083a482 103 | ts=014bcd10fe15 (1425074617877) Fri Feb 27 2015 22:03:37.877 GMT 104 | id=62cd (25293) 105 | no=0083a482 (8627330) 106 | 107 | $ decodecsn -z America/Los_Angeles 0000014bcd10fe1562cd0083a482 108 | 109 | CSNv1 0000014bcd10fe1562cd0083a482 110 | ts=014bcd10fe15 (1425074617877) Fri Feb 27 2015 14:03:37.877 PST 111 | id=62cd (25293) 112 | no=0083a482 (8627330) 113 | 114 | $ decodecsn -z -0500 0000014bcd10fe1562cd0083a482 115 | 116 | CSNv1 0000014bcd10fe1562cd0083a482 117 | ts=014bcd10fe15 (1425074617877) Fri Feb 27 2015 17:03:37.877 -0500 118 | id=62cd (25293) 119 | no=0083a482 (8627330) 120 | 121 | $ decodecsn 01050166b043d5fe000000c416407 122 | 123 | CSNv2 01050166b043d5fe000000c416407 124 | ts=0166b043d5fe (1540555527678) Fri Oct 26 2018 13:05:27.678 BST 125 | id=16407 126 | no=000000c4 (196) 127 | 128 | =head1 WARNING 129 | 130 | The format of the CSN is undocumented and subject to change. This tool has been 131 | tested with CSNv1 (OpenDJ 2.4 to DS 6.0.0), and CSNv2 (DS 6.5.0 and up). 132 | 133 | =head1 SEE ALSO 134 | 135 | L 136 | 137 | =head1 AUTHOR 138 | 139 | Chris Ridd Echris.ridd@forgerock.comE 140 | 141 | =head1 COPYRIGHT 142 | 143 | Copyright (c) 2012-2018 Chris Ridd. All rights reserved. This tool is free 144 | software; you can redistribute it and/or modify it under the same terms as Perl 145 | itself. 146 | -------------------------------------------------------------------------------- /slowops: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # Find particular operations and display their etime distribution 3 | 4 | use strict; 5 | use warnings; 6 | 7 | $::VERSION = 0.3; 8 | 9 | sub version() 10 | { 11 | print STDERR "$0 version $::VERSION\n"; 12 | exit 0; 13 | } 14 | 15 | sub usage() 16 | { 17 | print STDERR "Usage: $0 [-s] [-j] [-e etime] [op]\n"; 18 | exit 1; 19 | } 20 | 21 | my %opt = ( 22 | 's' => 0, # Summary mode 23 | 'j' => 0, # JSON logs 24 | 'e' => -1, # Etime 25 | 'h' => 0, # Help 26 | 'v' => 0, # Version 27 | ); 28 | 29 | my $op = 'SEARCH'; 30 | 31 | while (@ARGV) { 32 | my $arg = shift @ARGV; 33 | if ($arg =~ m{^-s$|^--summary$}) { 34 | $opt{'s'} = 1; 35 | } elsif ($arg =~ m{^-j$|^--json$}) { 36 | # JSON::XS is significantly faster than JSON::PP 37 | if (eval{require JSON::XS}) { 38 | $opt{j} = JSON::XS->new; 39 | } elsif (eval{require JSON::PP}) { 40 | $opt{j} = JSON::PP->new; 41 | } else { 42 | die "Cannot locate a JSON module\n"; 43 | } 44 | } elsif ($arg =~ m{^-e$|^--etime$}) { 45 | $opt{e} = shift @ARGV; 46 | usage unless defined $opt{e}; 47 | } elsif ($arg =~ m{^-h$|^--help$}) { 48 | $opt{h} = 1; 49 | } elsif ($arg =~ m{^-v$|^--version$}) { 50 | $opt{v} = 1; 51 | } else { 52 | $op = $arg; 53 | last; 54 | } 55 | } 56 | 57 | version if $opt{v}; 58 | usage if $opt{h}; 59 | 60 | my %details; 61 | my %quantize; 62 | my $scale = 0; 63 | 64 | # Calculating mean 65 | my $sum = 0; 66 | my $values = 0; 67 | # Calculating median 68 | my $lowest = undef; 69 | my $highest = undef; 70 | # Calculating mode 71 | my %mode; 72 | 73 | sub updatestats 74 | { 75 | my $etime = shift; 76 | my $details = shift; 77 | 78 | $sum += $etime; 79 | $values++; 80 | $lowest = $etime unless defined($lowest); 81 | $highest = $etime unless defined($highest); 82 | $lowest = $etime if $etime < $lowest; 83 | $highest = $etime if $etime > $highest; 84 | $mode{$etime} = 0 unless exists $mode{$etime}; 85 | $mode{$etime}++; 86 | 87 | my $bucket = 1; 88 | do { 89 | $quantize{$bucket} = [] unless exists $quantize{$bucket}; 90 | if ($etime <= $bucket) { 91 | push @{$quantize{$bucket}}, $details; 92 | if (scalar(@{$quantize{$bucket}}) > $scale) { 93 | $scale = scalar(@{$quantize{$bucket}}); 94 | } 95 | return; 96 | } 97 | $bucket *= 2; 98 | } while (1); 99 | } 100 | 101 | while (<>) { 102 | chomp; 103 | # JSON log format 104 | if ($opt{'j'}) { 105 | my $record = $opt{j}->decode($_); 106 | next unless $record->{request}->{operation} eq $op; 107 | my $etime = $record->{response}->{elapsedTime}; 108 | print "$_\n" if $opt{'e'} != -1 && $etime >= $opt{'e'}; 109 | updatestats($etime, $_) if $opt{'s'}; 110 | next; 111 | } 112 | # Traditional log format 113 | if (my ($tuple) = m{$op REQ.*(conn=-?\d+ op=\d+ msgID=\d+)}) { 114 | $details{$tuple} = $_; 115 | next; 116 | } 117 | # Traditional log format 118 | if (my ($tuple,$remainder) = m{$op RES .*(conn=-?\d+ op=\d+ msgID=\d+)( +.*)}) { 119 | if (exists $details{$tuple}) { 120 | my $details = $details{$tuple}; 121 | delete $details{$tuple}; 122 | my $etime = ""; 123 | $etime = $1 if $remainder =~ m{ etime=(\d+)}; 124 | if ($opt{'e'} != -1 && $etime >= $opt{'e'}) { 125 | print "$details$remainder\n"; 126 | } 127 | updatestats($etime, $details) if $opt{'s'}; 128 | } 129 | next; 130 | } 131 | # Combined log format 132 | if (my ($tuple,$remainder) = m{$op .*(conn=-?\d+ op=\d+ msgID=\d+)( +.*)}) { 133 | my $etime = ""; 134 | $etime = $1 if $remainder =~ m{ etime=(\d+)}; 135 | if ($opt{'e'} != -1 && $etime >= $opt{'e'}) { 136 | print "$_\n"; 137 | } 138 | updatestats($etime, $_) if $opt{'s'}; 139 | next; 140 | } 141 | } 142 | 143 | exit 0 unless $opt{'s'}; 144 | format STDOUT_TOP = 145 | value ------------------------Distribution------------------------ count 146 | . 147 | format = 148 | @>>>>> |@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<< 149 | $::bucket, $::bar, $::count 150 | . 151 | foreach our $bucket (sort {$a <=> $b} (keys %quantize)) { 152 | our $count = scalar(@{$quantize{$bucket}}); 153 | our $bar = "@" x ($count * 60.0 / $scale); 154 | write; 155 | } 156 | 157 | if ($values > 0) { 158 | my $key_max = undef; 159 | my $val_max = 0; 160 | foreach (keys %mode) { 161 | if ($mode{$_} > $val_max) { 162 | $val_max = $mode{$_}; 163 | $key_max = $_; 164 | } 165 | } 166 | printf "Mean: %.02f\n", $sum / $values; 167 | printf "Median: %.02f\n", ($highest - $lowest) / 2; 168 | printf "Mode: %.02f\n", $key_max; 169 | } 170 | 171 | 1; 172 | 173 | __END__ 174 | 175 | =head1 NAME 176 | 177 | slowops - analyze operation performance in OpenDJ log files 178 | 179 | =head1 SYNOPSIS 180 | 181 | slowops [-j] -s op 182 | 183 | slowops [-j] -e min op 184 | 185 | slowops -h 186 | 187 | slowops -v 188 | 189 | =head1 DESCRIPTION 190 | 191 | OpenDJ access log files record the elapsed time ("etime") taken by each LDAP 192 | operation. However if the server is busy finding slow operations is awkward, and 193 | finding any trends in the data is difficult. 194 | 195 | This tool analyzes OpenDJ access logs in a couple of ways. The statistics mode 196 | shows a graph of operation performance, and some basic figures like 197 | mean/mode/median. The etime mode reports all operations that take longer than a 198 | certain time to complete. 199 | 200 | =head1 OPTIONS 201 | 202 | =over 203 | 204 | =item B<-j> parse JSON-formatted logs. 205 | 206 | =item B<-h> display help text. 207 | 208 | =item B<-v> report the tool version. 209 | 210 | =item B<-s> report statistics after reading all the data. The graph output is a 211 | power-of-two frequency distribution, modelled on dtrace(1)'s quantize function. 212 | 213 | =item B<-e> report operations which have etimes larger than C>. 214 | 215 | =item B the LDAP operation being analyzed, which should be one of C, 216 | C, C, C, C, C. 217 | 218 | =back 219 | 220 | =head1 EXAMPLE 221 | 222 | $ slowops -s ADD < access 223 | 224 | value ------------------------Distribution------------------------ count 225 | 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 193 226 | 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 317 227 | 4 | 3 228 | 8 | 3 229 | 16 | 1 230 | Mean: 1.68 231 | Median: 4.50 232 | Mode: 2.00 233 | 234 | =head1 SEE ALSO 235 | 236 | L 237 | 238 | L 239 | 240 | =head1 AUTHOR 241 | 242 | Chris Ridd Echris.ridd@forgerock.comE 243 | 244 | =head1 COPYRIGHT 245 | 246 | Copyright (c) 2012-2017 Chris Ridd. All rights reserved. This tool is free 247 | software; you can redistribute it and/or modify it under the same terms as Perl 248 | itself. 249 | -------------------------------------------------------------------------------- /bbedit/LDIF/Interfaces/Language Modules/BBLMTextIterator.h: -------------------------------------------------------------------------------- 1 | #ifndef BBLMTEXTITERATOR_h 2 | #define BBLMTEXTITERATOR_h 1 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "BBLMInterface.h" 9 | 10 | class BBLMTextIterator 11 | { 12 | private: 13 | 14 | UniChar* fTextPtr; 15 | size_t fTextLen; 16 | UniChar* fGapLoc; 17 | size_t fGapLen; 18 | UniChar* fTextStart; 19 | UniChar* fTextEnd; 20 | 21 | UniChar* AddOffset(const int32_t offset) 22 | { 23 | bool wasPreGap = (fTextPtr < fGapLoc); 24 | UniChar* result = fTextPtr + offset; 25 | 26 | if (offset < 0) 27 | { 28 | if (!wasPreGap && result < (fGapLoc + fGapLen)) 29 | result -= fGapLen; 30 | } 31 | else 32 | { 33 | if (wasPreGap && result >= fGapLoc) 34 | result += fGapLen; 35 | } 36 | 37 | return result; 38 | } 39 | 40 | public: 41 | 42 | BBLMTextIterator(const BBLMParamBlock& params) 43 | { 44 | fTextPtr = params.fText; 45 | fTextLen = params.fTextLength; 46 | fGapLoc = fTextPtr + params.fTextGapLocation; 47 | fGapLen = params.fTextGapLength; 48 | fTextStart = fTextPtr; 49 | fTextEnd = fTextStart + fTextLen; 50 | 51 | if (fTextStart >= fGapLoc) 52 | fTextStart += fGapLen; 53 | 54 | if (fTextEnd >= fGapLoc) 55 | fTextEnd += fGapLen; 56 | 57 | fTextPtr = fTextStart; 58 | } 59 | 60 | BBLMTextIterator(const BBLMParamBlock& params, const int32_t offset) 61 | { 62 | fTextPtr = params.fText; 63 | fTextLen = params.fTextLength; 64 | fGapLoc = fTextPtr + params.fTextGapLocation; 65 | fGapLen = params.fTextGapLength; 66 | fTextStart = fTextPtr; 67 | fTextEnd = fTextStart + fTextLen; 68 | 69 | if (fTextStart >= fGapLoc) 70 | fTextStart += fGapLen; 71 | 72 | if (fTextEnd >= fGapLoc) 73 | fTextEnd += fGapLen; 74 | 75 | fTextPtr = fTextStart; 76 | fTextPtr = AddOffset(offset); 77 | } 78 | 79 | BBLMTextIterator(const BBLMParamBlock& params, const int32_t offset, const int32_t textLength) 80 | { 81 | fTextPtr = params.fText; 82 | fTextLen = textLength; 83 | fGapLoc = fTextPtr + params.fTextGapLocation; 84 | fGapLen = params.fTextGapLength; 85 | fTextStart = fTextPtr; 86 | fTextEnd = fTextStart + (fTextLen + offset); 87 | 88 | if (fTextStart >= fGapLoc) 89 | fTextStart += fGapLen; 90 | 91 | if (fTextEnd >= fGapLoc) 92 | fTextEnd += fGapLen; 93 | 94 | fTextPtr = fTextStart; 95 | fTextPtr = AddOffset(offset); 96 | } 97 | 98 | BBLMTextIterator(const BBLMTextIterator& iter) 99 | : fTextPtr (iter.fTextPtr), fTextLen(iter.fTextLen), 100 | fGapLoc (iter.fGapLoc), fGapLen (iter.fGapLen), 101 | fTextStart(iter.fTextStart), fTextEnd (iter.fTextEnd) 102 | { /*...*/ } 103 | 104 | BBLMTextIterator(const BBLMTextIterator& iter, const int32_t offset) 105 | : fTextPtr (iter.fTextPtr), fTextLen(iter.fTextLen), 106 | fGapLoc (iter.fGapLoc), fGapLen (iter.fGapLen), 107 | fTextStart(iter.fTextStart), fTextEnd (iter.fTextEnd) 108 | { fTextPtr = AddOffset(offset); } 109 | 110 | 111 | UniChar operator*() 112 | { 113 | if ((fTextPtr >= fTextEnd) || (fTextPtr < fTextStart)) 114 | return 0; 115 | 116 | return(*fTextPtr); 117 | } 118 | 119 | #if __LP64__ 120 | inline 121 | UniChar operator[](const CFIndex index) 122 | { 123 | UniChar* result = AddOffset(static_cast(index)); 124 | 125 | if ((result >= fTextEnd) || (result < fTextStart)) 126 | return 0; 127 | 128 | return(*result); 129 | } 130 | 131 | inline 132 | UniChar operator[](const UInt32 index) 133 | { 134 | UniChar* result = AddOffset(index); 135 | 136 | if ((result >= fTextEnd) || (result < fTextStart)) 137 | return 0; 138 | 139 | return(*result); 140 | } 141 | #endif 142 | 143 | inline 144 | UniChar operator[](const int32_t index) 145 | { 146 | UniChar* result = AddOffset(index); 147 | 148 | if ((result >= fTextEnd) || (result < fTextStart)) 149 | return 0; 150 | 151 | return(*result); 152 | } 153 | 154 | void operator ++(int) 155 | { 156 | fTextPtr++; 157 | if (fTextPtr == fGapLoc) 158 | fTextPtr += fGapLen; 159 | } 160 | 161 | BBLMTextIterator& operator +=(const int32_t delta) 162 | { fTextPtr = AddOffset(delta); return *this; } 163 | 164 | BBLMTextIterator operator +(const int32_t delta) 165 | { return BBLMTextIterator(*this, delta); } 166 | 167 | void operator --(int) 168 | { 169 | if (fTextPtr == (fGapLoc + fGapLen)) 170 | fTextPtr -= fGapLen; 171 | 172 | fTextPtr--; 173 | } 174 | 175 | BBLMTextIterator& operator -=(const int32_t delta) 176 | { fTextPtr = AddOffset(-delta); return *this; } 177 | 178 | BBLMTextIterator operator -(const int32_t delta) 179 | { return BBLMTextIterator(*this, -delta); } 180 | 181 | const UniChar* Address(void) const 182 | { return fTextPtr; } 183 | 184 | int32_t Offset(void) const 185 | { 186 | int32_t result = static_cast(fTextPtr - fTextStart); 187 | 188 | if (fTextPtr >= fGapLoc && fTextStart < fGapLoc) 189 | result -= fGapLen; 190 | 191 | return result; 192 | } 193 | 194 | void SetOffset(const int32_t newPos) 195 | { 196 | int32_t delta = (0 - Offset()) + newPos; 197 | 198 | fTextPtr = AddOffset(delta); 199 | } 200 | 201 | size_t CharsLeft(void) const 202 | { 203 | // 204 | // NB: we can't use fTextLen here because that's the length of the 205 | // text in the container. We have to rely on the specified bounds 206 | // of the iterator (fTextEnd and fTextStart) and account for the gap 207 | // when we do the math. 208 | // 209 | int32_t len = static_cast(fTextEnd - fTextStart); 210 | int32_t result = 0; 211 | 212 | if ((fTextEnd >= fGapLoc) && (fTextStart < fGapLoc)) 213 | len -= fGapLen; 214 | 215 | result = len - Offset(); 216 | if (result < 0) 217 | result = 0; 218 | 219 | return result; 220 | } 221 | 222 | bool InBounds() 223 | { return (fTextPtr >= fTextStart && fTextPtr < fTextEnd); } 224 | 225 | UniChar GetNextChar() 226 | { 227 | if (fTextPtr >= fTextEnd) 228 | return 0; 229 | 230 | UniChar result = **this; 231 | 232 | (*this)++; 233 | 234 | return result; 235 | } 236 | 237 | UniChar GetPrevChar() 238 | { 239 | (*this)--; 240 | 241 | if (fTextPtr <= fTextStart) 242 | return '\r'; 243 | 244 | return (*this)[-1]; 245 | } 246 | 247 | template 248 | size_t strlen(const CharXX *str) 249 | { 250 | size_t len = 0; 251 | 252 | str--; 253 | 254 | while (*++str != 0) 255 | len++; 256 | 257 | return(len); 258 | } 259 | 260 | 261 | // DRSWAT: function for sanitizing comparison results down to an int that is precisely -1, 0, or 1. 262 | // Necessary for 64-bit to deal with precision loss compiler errors. 263 | template 264 | static inline int MakeComparisonResult(_signedIntT inDelta) 265 | { 266 | return (static_cast(inDelta > 0) - static_cast(inDelta < 0)); 267 | } 268 | 269 | template 270 | int strcmp(const CharXX *str, size_t n) 271 | { 272 | BBLMTextIterator p = *this; 273 | unsigned long c1, c2; 274 | 275 | str--; 276 | n++; 277 | 278 | while (--n && fTextPtr < fTextEnd) 279 | { 280 | c1 = p.GetNextChar(); 281 | c2 = *++str; 282 | 283 | if (c1 != c2) 284 | { 285 | long delta = c1 - c2; 286 | return MakeComparisonResult(delta); 287 | } 288 | } 289 | 290 | return(0); 291 | } 292 | 293 | template 294 | int strcmp(const CharXX *str) 295 | { 296 | return strcmp(str, strlen(str)); 297 | } 298 | 299 | template 300 | int stricmp(const CharXX *str, size_t n) 301 | { 302 | BBLMTextIterator p = *this; 303 | unsigned long c1, c2; 304 | 305 | str--; 306 | n++; 307 | 308 | while (--n && fTextPtr < fTextEnd) 309 | { 310 | c1 = p.GetNextChar(); 311 | c2 = *++str; 312 | 313 | if ((c1 & ~0x7FU) == 0) 314 | c1 = tolower(static_cast(c1)); 315 | 316 | if ((c2 & ~0x7FU) == 0) 317 | c2 = tolower(static_cast(c2)); 318 | 319 | if (c1 != c2) 320 | { 321 | long delta = c1 - c2; 322 | return MakeComparisonResult(delta); 323 | } 324 | } 325 | 326 | return(0); 327 | } 328 | 329 | template 330 | int stricmp(const CharXX *str) 331 | { 332 | return stricmp(str, strlen(str)); 333 | } 334 | }; 335 | 336 | #endif // BBLMTEXTITERATOR_h 337 | -------------------------------------------------------------------------------- /bbedit/LDIF/Interfaces/Language Modules/BBLMTextUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BBLMTextUtils.h 3 | * 4 | * Created by Seth Dillingham on 2/14/2008. 5 | * Copyright 2008 Macrobyte Resources. 6 | * 7 | * Generic, UniChar-based, utility functions for language modules. 8 | * To use it, a language module has to subclass BBLMTextUtils, 9 | * such as with YAMLTextUtils or ScalaTextUtils. 10 | * 11 | * Most text-processing methods use a BBLMTextIterator, which is shared 12 | * with another object (such as a Tokenizer). So, for example, calling 13 | * skipInlineWhitespace() will cause the "host's" text iterator to be 14 | * incremented so that it points to the first non-inline-whitespace 15 | * character that is found at or after the iterator's current position. 16 | * 17 | * HOWEVER, any method whose name ends with "ByIndex" does NOT increment 18 | * the TextIterator. Instead, they accept a reference to an offset from the 19 | * BBLMTextIterator's current position, and update that offset (the index) 20 | * before returning. For an example, see ::skipLineByIndex 21 | * 22 | * 23 | * NOTE that this class was originally written for the YAML module, and 24 | * and may still have some non-generic stuff that needs to be either 25 | * refactored or moved to a subclass. 26 | */ 27 | 28 | #ifndef BBLMTextUtils_h 29 | #define BBLMTextUtils_h 30 | 31 | #include "BBLMInterface.h" 32 | #include "BBLMTextIterator.h" 33 | 34 | #define isByte(c) ( ( (c) & ~0xFF ) == 0 ) 35 | #define isBracket(c) ( ( (c) == '(' ) || ( (c) == '{' ) || ( (c) == '[' ) || ( (c) == ']' ) || ( (c) == '}' ) || ( (c) == ')' ) ) 36 | #define isHexChar(c) ( isByte(c) && \ 37 | ( ( ( (c) >= '0' ) && ( (c) <= '9' ) ) || \ 38 | ( ( (c) >= 'a' ) && ( (c) <= 'f' ) ) || \ 39 | ( ( (c) >= 'A' ) && ( (c) <= 'F' ) ) ) ) 40 | 41 | class BBLMTextUtils 42 | { 43 | 44 | #pragma mark - 45 | #pragma mark public 46 | public: 47 | BBLMTextUtils( BBLMParamBlock & params, 48 | const BBLMCallbackBlock & bblm_callbacks, 49 | BBLMTextIterator & p ) 50 | :m_params( params ), 51 | m_bblm_callbacks( bblm_callbacks ), 52 | m_p( p ), 53 | m_inlineWhiteCharSet( NULL ), 54 | m_breakCharSet( NULL ), 55 | m_EOLCharSet( NULL ) 56 | { 57 | m_inlineWhiteCharSet = CFCharacterSetCreateMutable( kCFAllocatorDefault ); 58 | 59 | m_breakCharSet = CFCharacterSetCreateMutable( kCFAllocatorDefault ); 60 | 61 | m_EOLCharSet = CFCharacterSetCreateMutable( kCFAllocatorDefault ); 62 | 63 | this->initEOLChars(); 64 | this->initWhitespace(); 65 | } 66 | 67 | virtual ~BBLMTextUtils() 68 | { 69 | if ( NULL != m_inlineWhiteCharSet ) 70 | { 71 | CFRelease( m_inlineWhiteCharSet ); 72 | 73 | m_inlineWhiteCharSet = NULL; 74 | } 75 | 76 | if ( NULL != m_breakCharSet ) 77 | { 78 | CFRelease( m_breakCharSet ); 79 | 80 | m_breakCharSet = NULL; 81 | } 82 | 83 | if ( NULL != m_EOLCharSet ) 84 | { 85 | CFRelease( m_EOLCharSet ); 86 | 87 | m_EOLCharSet = NULL; 88 | } 89 | } 90 | 91 | 92 | bool isBreakChar( UniChar ); 93 | 94 | bool isInlineWhiteChar( UniChar ); 95 | 96 | bool isEOLChar( UniChar ); 97 | 98 | inline UniChar LowerChar( UniChar c ) { 99 | return ( ( c >= 'A' ) && ( c <= 'Z' ) ) ? c + ( 'a' - 'A' ) : c; 100 | } 101 | 102 | 103 | 104 | #pragma mark - 105 | #pragma mark Skippers 106 | 107 | bool skipWhitespace(); 108 | 109 | 110 | bool skipInlineWhitespace(); 111 | 112 | bool skipInlineWhitespace( SInt32 & ); 113 | 114 | bool skipInlineWhitespace( BBLMTextIterator & p ); 115 | 116 | bool skipInlineWhitespace( SInt32 &, BBLMTextIterator & ); 117 | 118 | 119 | bool skipInlineWhitespaceByIndex( SInt32 & index ) { 120 | return this->skipInlineWhitespaceByIndex( m_p, index ); 121 | } 122 | 123 | bool skipInlineWhitespaceByIndex( BBLMTextIterator &, SInt32 & index ); 124 | 125 | 126 | void skipPreviousInlineWhitespace(); 127 | 128 | void skipPreviousInlineWhitespaceByIndex( SInt32 & index ) { 129 | return this->skipPreviousInlineWhitespaceByIndex( m_p, index ); 130 | } 131 | 132 | void skipPreviousInlineWhitespaceByIndex( BBLMTextIterator &, SInt32 & ); 133 | 134 | 135 | bool skipLine() { 136 | return this->skipLine( m_p ); 137 | } 138 | 139 | bool skipLine( BBLMTextIterator & ); 140 | 141 | inline bool skipLineByIndex( SInt32 & index ) { 142 | return this->skipLineByIndex( m_p, index ); 143 | } 144 | 145 | bool skipLineByIndex( BBLMTextIterator &, SInt32 & ); 146 | 147 | bool skipLineWithMaxIndexByIndex( SInt32 &, SInt32 ); 148 | 149 | inline bool skipToEOL() { 150 | return this->skipToEOL( m_p ); 151 | } 152 | 153 | bool skipToEOL( BBLMTextIterator & p ); 154 | 155 | SInt32 findLineEndBeforeIndex( SInt32 ); 156 | 157 | SInt32 findLineEndAfterIndex( SInt32 ); 158 | 159 | virtual bool skipWord() = 0; 160 | 161 | virtual bool skipWordByIndex( SInt32 & ) = 0; 162 | 163 | bool matchChars( const char * ); 164 | 165 | bool imatchChars( const char * ); 166 | 167 | virtual bool matchWord( const char * pat ) = 0; 168 | 169 | virtual bool imatchWord( const char * pat ) = 0; 170 | 171 | bool skipToCharByIndex( UniChar, SInt32 &, bool ); 172 | 173 | bool skipToCharSameLineByIndex( UniChar, SInt32 & ); 174 | 175 | inline bool skipGroupByIndexCountingLines( SInt32 & index, const UniChar breakChar, SInt32 & ctLines ) { 176 | return this->skipGroupByIndexCountingLines( m_p, index, breakChar, ctLines ); 177 | } 178 | 179 | bool skipGroupByIndexCountingLines( BBLMTextIterator &, SInt32 &, const UniChar, SInt32 & ); 180 | 181 | bool rightTrimByIndex( SInt32, SInt32 ); 182 | 183 | virtual bool skipDelimitedStringByIndex( SInt32 & ) = 0; 184 | 185 | bool skipDelimitedStringByIndex( SInt32 & /* index */, bool /* flAllowEOL */, 186 | bool /* flAllowEscape */, bool /* flAllowEscapedEOL */ ); 187 | 188 | SInt32 copyCollapsedRangeToBuffer( SInt32 &, SInt32 &, UniChar *, NSUInteger ); 189 | 190 | CFStringRef createCFStringFromOffsets( SInt32 &, SInt32 &, SInt32 ); 191 | 192 | CFStringRef createCFStringFromOffsetsWithPrefix( SInt32 &, SInt32 &, SInt32, CFStringRef ); 193 | 194 | UInt32 countLinesInRange( UInt32 rangeStart, UInt32 rangeEnd, UInt32 maxLinesToFind); 195 | 196 | 197 | typedef enum BBLMTUNumberType { 198 | kBBLMTUUnknown, 199 | kBBLMTULong, 200 | kBBLMTUOctal, 201 | kBBLMTUBinary, 202 | kBBLMTUHex, 203 | kBBLMTUFloat, 204 | kBBLMTUScientific, 205 | kBBLMTUImaginary 206 | } BBLMTUNumberType; 207 | 208 | inline bool skipBinary() { 209 | BBLMTUNumberType nt = kBBLMTUUnknown; 210 | 211 | return this->skipBinary( nt ); 212 | } 213 | 214 | bool skipBinary( BBLMTUNumberType & ); 215 | 216 | 217 | inline bool skipHex() { 218 | BBLMTUNumberType nt = kBBLMTUUnknown; 219 | 220 | return this->skipHex( nt ); 221 | } 222 | 223 | bool skipHex( BBLMTUNumberType & ); 224 | 225 | 226 | inline bool skipFloat() { 227 | BBLMTUNumberType nt = kBBLMTUUnknown; 228 | 229 | return this->skipFloat( nt ); 230 | } 231 | 232 | bool skipFloat( BBLMTUNumberType & ); 233 | 234 | 235 | inline bool skipOctal() { 236 | BBLMTUNumberType nt = kBBLMTUUnknown; 237 | 238 | return this->skipOctal( nt ); 239 | } 240 | 241 | bool skipOctal( BBLMTUNumberType & ); 242 | 243 | 244 | inline bool skipNumber() { 245 | BBLMTUNumberType nt = kBBLMTUUnknown; 246 | 247 | return this->skipNumber( nt ); 248 | } 249 | 250 | bool skipNumber( BBLMTUNumberType & ); 251 | 252 | 253 | #pragma mark - 254 | #pragma mark protected 255 | protected: 256 | 257 | BBLMParamBlock & m_params; 258 | 259 | const BBLMCallbackBlock & m_bblm_callbacks; 260 | 261 | BBLMTextIterator & m_p; 262 | 263 | #pragma mark - 264 | #pragma mark Character Tests 265 | 266 | void addBreakChar( UniChar c ); 267 | 268 | void addBreakChars( CFStringRef ); 269 | 270 | void clearBreakChar( UniChar ); 271 | 272 | void clearBreakChars( CFStringRef ); 273 | 274 | virtual void initBreaks() = 0; 275 | 276 | private: 277 | void addCharsToSet( CFStringRef, CFMutableCharacterSetRef ); 278 | void addCharToSet( UniChar, CFMutableCharacterSetRef ); 279 | 280 | void removeCharsFromSet( CFStringRef, CFMutableCharacterSetRef ); 281 | void removeCharFromSet( UniChar, CFMutableCharacterSetRef ); 282 | 283 | 284 | void initWhitespace(); 285 | void initEOLChars(); 286 | 287 | #pragma mark - 288 | #pragma mark private 289 | private: 290 | 291 | CFMutableCharacterSetRef m_inlineWhiteCharSet; 292 | 293 | CFMutableCharacterSetRef m_breakCharSet; 294 | 295 | CFMutableCharacterSetRef m_EOLCharSet; 296 | }; 297 | 298 | #endif // BBLMTextUtils_h 299 | -------------------------------------------------------------------------------- /bbedit/LDIF/LDIF.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 67ED5E321C85FD690051CA0A /* LDIF.mm in Sources */ = {isa = PBXBuildFile; fileRef = 67ED5E311C85FD690051CA0A /* LDIF.mm */; }; 11 | 67ED5E381C86E4360051CA0A /* BBLMTextUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 67ED5E371C86E4360051CA0A /* BBLMTextUtils.mm */; }; 12 | 67ED5E3B1C86E4C10051CA0A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67ED5E3A1C86E4C10051CA0A /* Foundation.framework */; }; 13 | 67ED5E3E1C86E5290051CA0A /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67ED5E3D1C86E5290051CA0A /* Security.framework */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXFileReference section */ 17 | 5F05134505A0957400BD0E76 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 18 | 5F05134605A0957400BD0E76 /* LDIF.bblm */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LDIF.bblm; sourceTree = BUILT_PRODUCTS_DIR; }; 19 | 67ED5E311C85FD690051CA0A /* LDIF.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = LDIF.mm; path = LDIF/LDIF.mm; sourceTree = ""; }; 20 | 67ED5E331C86E41B0051CA0A /* BBLMToken.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BBLMToken.h; path = "Interfaces/Language Modules/BBLMToken.h"; sourceTree = ""; }; 21 | 67ED5E341C86E42E0051CA0A /* BBLMTextUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BBLMTextUtils.h; path = "Interfaces/Language Modules/BBLMTextUtils.h"; sourceTree = ""; }; 22 | 67ED5E351C86E42E0051CA0A /* BBLMTextIterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BBLMTextIterator.h; path = "Interfaces/Language Modules/BBLMTextIterator.h"; sourceTree = ""; }; 23 | 67ED5E361C86E42E0051CA0A /* BBLMInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BBLMInterface.h; path = "Interfaces/Language Modules/BBLMInterface.h"; sourceTree = ""; }; 24 | 67ED5E371C86E4360051CA0A /* BBLMTextUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BBLMTextUtils.mm; path = "Interfaces/Language Modules/BBLMTextUtils.mm"; sourceTree = ""; }; 25 | 67ED5E3A1C86E4C10051CA0A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 26 | 67ED5E3D1C86E5290051CA0A /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | 67ED5E391C86E4B20051CA0A /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | 67ED5E3E1C86E5290051CA0A /* Security.framework in Frameworks */, 35 | 67ED5E3B1C86E4C10051CA0A /* Foundation.framework in Frameworks */, 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 089C166AFE841209C02AAC07 /* Hello World */ = { 43 | isa = PBXGroup; 44 | children = ( 45 | 08FB77ADFE841716C02AAC07 /* Source */, 46 | 2EADE40E056EA22B004A4EFC /* SDK Headers */, 47 | 5F05134505A0957400BD0E76 /* Info.plist */, 48 | 67ED5E3C1C86E4CE0051CA0A /* Frameworks */, 49 | 5F05134605A0957400BD0E76 /* LDIF.bblm */, 50 | ); 51 | name = "Hello World"; 52 | sourceTree = ""; 53 | }; 54 | 08FB77ADFE841716C02AAC07 /* Source */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 67ED5E311C85FD690051CA0A /* LDIF.mm */, 58 | 67ED5E371C86E4360051CA0A /* BBLMTextUtils.mm */, 59 | ); 60 | name = Source; 61 | sourceTree = ""; 62 | }; 63 | 2EADE40E056EA22B004A4EFC /* SDK Headers */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 67ED5E341C86E42E0051CA0A /* BBLMTextUtils.h */, 67 | 67ED5E351C86E42E0051CA0A /* BBLMTextIterator.h */, 68 | 67ED5E361C86E42E0051CA0A /* BBLMInterface.h */, 69 | 67ED5E331C86E41B0051CA0A /* BBLMToken.h */, 70 | ); 71 | name = "SDK Headers"; 72 | sourceTree = ""; 73 | }; 74 | 67ED5E3C1C86E4CE0051CA0A /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 67ED5E3D1C86E5290051CA0A /* Security.framework */, 78 | 67ED5E3A1C86E4C10051CA0A /* Foundation.framework */, 79 | ); 80 | name = Frameworks; 81 | sourceTree = ""; 82 | }; 83 | /* End PBXGroup section */ 84 | 85 | /* Begin PBXNativeTarget section */ 86 | 5F05133905A0957400BD0E76 /* LDIF */ = { 87 | isa = PBXNativeTarget; 88 | buildConfigurationList = 2E4F91CD0863164B009D6578 /* Build configuration list for PBXNativeTarget "LDIF" */; 89 | buildPhases = ( 90 | 5F05133F05A0957400BD0E76 /* Sources */, 91 | 67ED5E391C86E4B20051CA0A /* Frameworks */, 92 | ); 93 | buildRules = ( 94 | ); 95 | dependencies = ( 96 | ); 97 | name = LDIF; 98 | productInstallPath = "$(HOME)/Library/Bundles"; 99 | productName = "Hello World"; 100 | productReference = 5F05134605A0957400BD0E76 /* LDIF.bblm */; 101 | productType = "com.apple.product-type.bundle"; 102 | }; 103 | /* End PBXNativeTarget section */ 104 | 105 | /* Begin PBXProject section */ 106 | 089C1669FE841209C02AAC07 /* Project object */ = { 107 | isa = PBXProject; 108 | attributes = { 109 | LastUpgradeCheck = 0710; 110 | }; 111 | buildConfigurationList = 2E4F91D10863164B009D6578 /* Build configuration list for PBXProject "LDIF" */; 112 | compatibilityVersion = "Xcode 3.2"; 113 | developmentRegion = English; 114 | hasScannedForEncodings = 1; 115 | knownRegions = ( 116 | en, 117 | ); 118 | mainGroup = 089C166AFE841209C02AAC07 /* Hello World */; 119 | productRefGroup = 089C166AFE841209C02AAC07 /* Hello World */; 120 | projectDirPath = ""; 121 | projectRoot = ""; 122 | targets = ( 123 | 5F05133905A0957400BD0E76 /* LDIF */, 124 | ); 125 | }; 126 | /* End PBXProject section */ 127 | 128 | /* Begin PBXSourcesBuildPhase section */ 129 | 5F05133F05A0957400BD0E76 /* Sources */ = { 130 | isa = PBXSourcesBuildPhase; 131 | buildActionMask = 2147483647; 132 | files = ( 133 | 67ED5E381C86E4360051CA0A /* BBLMTextUtils.mm in Sources */, 134 | 67ED5E321C85FD690051CA0A /* LDIF.mm in Sources */, 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | /* End PBXSourcesBuildPhase section */ 139 | 140 | /* Begin XCBuildConfiguration section */ 141 | 2E4F91CE0863164B009D6578 /* Debug */ = { 142 | isa = XCBuildConfiguration; 143 | buildSettings = { 144 | COMBINE_HIDPI_IMAGES = YES; 145 | COPY_PHASE_STRIP = NO; 146 | INFOPLIST_FILE = Info.plist; 147 | INSTALL_MODE_FLAG = "ug+w,o-w,a+rX"; 148 | ONLY_ACTIVE_ARCH = NO; 149 | OTHER_CFLAGS = ""; 150 | PRODUCT_NAME = LDIF; 151 | WRAPPER_EXTENSION = bblm; 152 | }; 153 | name = Debug; 154 | }; 155 | 2E4F91CF0863164B009D6578 /* Release */ = { 156 | isa = XCBuildConfiguration; 157 | buildSettings = { 158 | COMBINE_HIDPI_IMAGES = YES; 159 | COPY_PHASE_STRIP = YES; 160 | INFOPLIST_FILE = Info.plist; 161 | INSTALL_MODE_FLAG = "ug+w,o-w,a+rX"; 162 | ONLY_ACTIVE_ARCH = NO; 163 | OTHER_CFLAGS = ""; 164 | PRODUCT_NAME = LDIF; 165 | WRAPPER_EXTENSION = bblm; 166 | }; 167 | name = Release; 168 | }; 169 | 2E4F91D20863164B009D6578 /* Debug */ = { 170 | isa = XCBuildConfiguration; 171 | buildSettings = { 172 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 173 | ENABLE_TESTABILITY = YES; 174 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 175 | ONLY_ACTIVE_ARCH = YES; 176 | }; 177 | name = Debug; 178 | }; 179 | 2E4F91D30863164B009D6578 /* Release */ = { 180 | isa = XCBuildConfiguration; 181 | buildSettings = { 182 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 183 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 184 | }; 185 | name = Release; 186 | }; 187 | /* End XCBuildConfiguration section */ 188 | 189 | /* Begin XCConfigurationList section */ 190 | 2E4F91CD0863164B009D6578 /* Build configuration list for PBXNativeTarget "LDIF" */ = { 191 | isa = XCConfigurationList; 192 | buildConfigurations = ( 193 | 2E4F91CE0863164B009D6578 /* Debug */, 194 | 2E4F91CF0863164B009D6578 /* Release */, 195 | ); 196 | defaultConfigurationIsVisible = 0; 197 | defaultConfigurationName = Debug; 198 | }; 199 | 2E4F91D10863164B009D6578 /* Build configuration list for PBXProject "LDIF" */ = { 200 | isa = XCConfigurationList; 201 | buildConfigurations = ( 202 | 2E4F91D20863164B009D6578 /* Debug */, 203 | 2E4F91D30863164B009D6578 /* Release */, 204 | ); 205 | defaultConfigurationIsVisible = 0; 206 | defaultConfigurationName = Debug; 207 | }; 208 | /* End XCConfigurationList section */ 209 | }; 210 | rootObject = 089C1669FE841209C02AAC07 /* Project object */; 211 | } 212 | -------------------------------------------------------------------------------- /topology2dot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # 3 | # Extract OpenDJ replication topology information from a dump of the 4 | # subtree in LDIF format, and output a Graphviz DOT format 5 | # representation. 6 | # 7 | # Limitations: 8 | # From 2.6.0 it is possible to see more information about each DS. However it is 9 | # not possible to determine which DSes are part of the same OpenDJ server, so 10 | # the output shows more DSes than you would initially expect. Ignoring certain 11 | # suffixes like and can be helpful. 12 | # 13 | # The output is not particularly beautiful, as I usually import it into 14 | # OmniGraffle which only interprets a subset of DOT styles. 15 | 16 | use strict; 17 | use warnings; 18 | use Net::LDAP::LDIF; 19 | 20 | # Values for the DOT fillcolor, line and fontcolor attributes for RSes and DSes 21 | my $rsfill = qq{"#4ea3df"}; 22 | my $rsline = qq{"#011993"}; 23 | my $rstext = qq{"#ffffff"}; 24 | 25 | my $dsfill = qq{"#caf3d9"}; 26 | my $dsline = qq{"#9ebfab"}; 27 | my $dstext = qq{"#000000"}; 28 | 29 | package RS; 30 | 31 | sub new { 32 | my ($self,$hostname,$port,$rsid) = @_; 33 | my $type = ref($self) || $self; 34 | my $obj = bless {}, $type; 35 | $obj->{name} = "$hostname:$port"; 36 | $obj->{rsid} = $rsid; 37 | $obj->{domains} = []; 38 | return $obj; 39 | } 40 | 41 | sub get_name { 42 | my $self = shift; 43 | return $self->{name}; 44 | } 45 | 46 | sub get_id { 47 | my $self = shift; 48 | return $self->{rsid}; 49 | } 50 | 51 | sub add_domain { 52 | my ($self,$domain) = @_; 53 | push @{$self->{domains}}, $domain; 54 | } 55 | 56 | sub get_domains { 57 | my ($self,$rsid) = @_; 58 | return @{$self->{domains}}; 59 | } 60 | 61 | 1; 62 | 63 | package DS; 64 | 65 | sub new { 66 | my ($self,$hostname,$dsid,$domain) = @_; 67 | my $type = ref($self) || $self; 68 | my $obj = bless {}, $type; 69 | $obj->{name} = $hostname; 70 | $obj->{dsid} = $dsid; 71 | $obj->{domain} = $domain; 72 | return $obj; 73 | } 74 | 75 | sub get_name { 76 | my $self = shift; 77 | return $self->{name}; 78 | } 79 | 80 | sub get_id { 81 | my $self = shift; 82 | return $self->{dsid}; 83 | } 84 | 85 | sub get_domain { 86 | my $self = shift; 87 | return $self->{domain}; 88 | } 89 | 90 | sub set_rs { 91 | my ($self,$rs) = @_; 92 | $self->{rs} = $rs; 93 | } 94 | 95 | sub get_rs { 96 | my $self = shift; 97 | return $self->{rs}; 98 | } 99 | 100 | 1; 101 | 102 | package main; 103 | 104 | sub html { 105 | my $str = shift; 106 | $str =~ s/&/&/g; 107 | $str =~ s//>/g; 109 | return $str; 110 | } 111 | 112 | my %ignore; 113 | 114 | foreach (@ARGV) { 115 | $ignore{$_} = 1; 116 | } 117 | @ARGV = (); 118 | 119 | my $ldif = Net::LDAP::LDIF->new(shift, "r", onerror => 'undef'); 120 | 121 | my %rs; 122 | my %rsbyid; 123 | my %ds; 124 | my %domain; 125 | 126 | while (not $ldif->eof()) { 127 | my $entry = $ldif->read_entry(); 128 | unless ($ldif->error()) { 129 | my $dn = $entry->dn(); 130 | my $serverid = $entry->get_value('server-id'); 131 | my $domainname = $entry->get_value('domain-name'); 132 | next if defined($domainname) && exists $ignore{$domainname}; 133 | 134 | my $connectedto = $entry->get_value('connected-to'); 135 | # 2.6.0: cn=Replication server RS(...) hostname:port 136 | if ($dn =~ m{^cn=Replication server RS\((\d+)\) ([^:]+):(\d+),}i) { 137 | # This server is the RS 138 | my $server = $rs{"$2:$3"}; 139 | $server = new RS($2, $3, $1) unless defined($server); 140 | $rs{"$2:$3"} = $server; 141 | $rsbyid{$1} = $server; 142 | $server->add_domain($domainname); 143 | # 2.5.x: 144 | } elsif ($dn =~ m{^cn=Replication Server\s+([^, ]+)\s+([^, ]+)\s+([^, ]+),}i) { 145 | # This server is an RS 146 | # port hostname rsid 147 | my $server = $rs{"$2:$1"}; 148 | $server = new RS($2, $1, $3) unless defined($server); 149 | $rs{"$2:$1"} = $server; 150 | $rsbyid{$3} = $server; 151 | $server->add_domain($domainname); 152 | # 2.6.0: cn=Connected replication server RS(...) hostname:port 153 | } elsif ($dn =~ m{^cn=Connected replication server RS\((\d+)\) ([^:]+):(\d+),}i) { 154 | # Another RS (not this one) 155 | my $server = $rs{"$2:$3"}; 156 | $server = new RS($2, $3, $1) unless defined($server); 157 | $rs{"$2:$3"} = $server; 158 | $rsbyid{$1} = $server; 159 | $server->add_domain($domainname); 160 | # 2.5.x 161 | } elsif ($dn =~ m{^cn=Connected Replication Server\s+([^:]+):(\d+)\s+([^,]+),}i) { 162 | # This entry is another RS 163 | # hostname port rsid 164 | my $server = $rs{"$1:$2"}; 165 | $server = new RS($1, $2, $3) unless defined($server); 166 | $rs{"$1:$2"} = $server; 167 | $rsbyid{$3} = $server; 168 | $server->add_domain($domainname); 169 | # 2.6.0: cn=Directory server DS(...) hostname:port 170 | } elsif ($dn =~ m{^cn=Directory Server DS\((\d+)\) ([^:]+):(\d+),}i) { 171 | # 2.6.0 DS connected to that server 172 | my $server = new DS("$2:$3", $1, $domainname); 173 | $ds{"$2:$3"} = $server; 174 | # 2.5.x 175 | } elsif ($dn =~ m{^cn=Connected Replica ([^, ]+)\s+(\d+),}i) { 176 | # This entry is a DS connected to this server 177 | # hostname dsid 178 | # no other server info 179 | my $server = new DS($1, $2, $domainname); 180 | $ds{"$1 $2"} = $server; 181 | if ($connectedto =~ m{^Replication Server (\d+) (\d+)}) { 182 | $server->set_rs($rsbyid{$2}); 183 | } 184 | # 2.6.0: cn=Connected directory server DS(...) hostname:port 185 | } elsif ($dn =~ m{^cn=Connected directory server DS\((\d+)\) ([^:]+):(\d+),}i) { 186 | my $server = new DS("$2:$3", $1, $domainname); 187 | $ds{"$2:$3"} = $server; 188 | if ($connectedto =~ m{Connected replication server RS\((\d+)\) }) { 189 | $server->set_rs($1); 190 | } elsif ($connectedto =~ m{Replication Server (\d+) (\d+)}) { 191 | $server->set_rs($2); 192 | } 193 | # 2.5.x 194 | } elsif ($dn =~ m{^cn=Undirect Replica\s+([^,]+),}i) { 195 | # This entry is a DS connected to another RS 196 | # dsid 197 | my $server = new DS("unknown", $1, $domainname); 198 | $ds{"unknown $1"} = $server; 199 | if ($connectedto =~ m{Connected Replication Server\s+([^, ]+)\s(\d+),}) { 200 | $server->set_rs($rsbyid{$2}); 201 | } 202 | } 203 | } 204 | } 205 | 206 | # Post-process the DSes so they point to RS objects 207 | # (DSes could be written before the RSes in the monitor output) 208 | foreach (keys(%ds)) { 209 | my $server = $ds{$_}; 210 | my $rsid = $server->get_rs(); 211 | my $rs = undef; 212 | unless (ref($rsid)) { 213 | $rs = $rsbyid{$rsid}; 214 | $server->set_rs($rs) if defined $rs; 215 | } 216 | } 217 | 218 | print "// OpenDJ Replication topology\n"; 219 | print "digraph replication {\n"; 220 | 221 | my @rslinks; 222 | foreach (sort(keys(%rs))) { 223 | my $server = $rs{$_}; 224 | my $rsid = $server->get_id(); 225 | print " rs$rsid [shape=box,style=filled,fillcolor=$rsfill,fontcolor=$rstext]\n"; 226 | print " rs$rsid [label=<", html($server->get_name()), " (", html($server->get_id()), ")"; 227 | foreach my $domain (sort $server->get_domains()) { 228 | print "
", html($domain); 229 | } 230 | print ">]\n"; 231 | print "\n"; 232 | push @rslinks, "rs$rsid"; 233 | } 234 | 235 | # Fully meshed RSes 236 | while (scalar(@rslinks) > 1) { 237 | my $t = shift @rslinks; 238 | foreach (@rslinks) { 239 | print " $t -> $_ [color=$rsline]\n"; 240 | } 241 | } 242 | 243 | foreach (sort(keys(%ds))) { 244 | my $server = $ds{$_}; 245 | my $dsid = $server->get_id(); 246 | print "\n"; 247 | print " ds$dsid [shape=box,style=filled,fillcolor=$dsfill,fontcolor=$dstext]\n"; 248 | print " ds$dsid [label=<", html($server->get_name()), " (", html($server->get_id()), ")
", html($server->get_domain()), ">]\n"; 249 | my $rsid = $server->get_rs()->get_id(); 250 | print " ds$dsid -> rs$rsid [color=$dsline]\n"; 251 | } 252 | 253 | print "}\n"; 254 | 255 | exit 0; 256 | 257 | __END__ 258 | 259 | =head1 NAME 260 | 261 | topology2dot - display OpenDJ replication topology in GraphViz DOT format 262 | 263 | =head1 SYNOPSIS 264 | 265 | topology2dot [ignored-suffix [...]] < monitor.ldif 266 | 267 | =head1 DESCRIPTION 268 | 269 | The topology of a network of replicated OpenDJ servers can be determined by 270 | analysis of a complete dump of the Ecn=monitorE subtree. 271 | 272 | This tool extracts as much useful information as possible and outputs a 273 | graph in GraphViz DOT format. The replication servers are drawn in blue boxes, 274 | and the directory servers are drawn in green boxes. 275 | 276 | The first line of each label contains the hostname and administration port 277 | number of the server (if available) and the server-id in parentheses. The 278 | remaining lines list the suffixes being replicated. 279 | 280 | =head1 NOTE 281 | 282 | The format of the entries in the Ecn=monitorE subtree is subject to 283 | change. This tool has been tested with OpenDJ 2.5 and 2.6.0. 284 | 285 | Full details of the connected directory servers are not currently available. 286 | One directory server would typically have 3 or more separate server-ids, each 287 | replicating a separate suffix. 288 | 289 | GraphViz's circo tool is recommended for rendering the output. 290 | 291 | =head1 SEE ALSO 292 | 293 | L 294 | 295 | =head1 AUTHOR 296 | 297 | Chris Ridd Echris.ridd@forgerock.comE 298 | 299 | =head1 COPYRIGHT 300 | 301 | Copyright (c) 2013-2017 Chris Ridd. All rights reserved. This tool is free 302 | software; you can redistribute it and/or modify it under the same terms as Perl 303 | itself. 304 | -------------------------------------------------------------------------------- /bbedit/LDIF/LDIF/LDIF.mm: -------------------------------------------------------------------------------- 1 | // 2 | // LDIF.mm 3 | // LDIF 4 | // 5 | // Created by Chris on 06/07/2014. 6 | // Copyright (c) 2014 Chris Ridd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #include 12 | 13 | #include "BBLMInterface.h" 14 | #include "BBLMTextIterator.h" 15 | 16 | // Custom colour runs 17 | #define kLDIFBBLMType @"com.barebones.bblm.html.attribute-name" /* Not documented */ 18 | #define kLDIFBBLMValue @"com.barebones.bblm.html.attribute-value" /* Not documented */ 19 | #define kLDIFBBLMDash @"com.example.ldif.dash" 20 | #define kLDIFBBLMSeparator @"com.example.ldif.separator" 21 | #define kLDIFBBLMName @"com.barebones.bblm.identifier" /* Not documented */ 22 | 23 | extern "C" OSErr LDIF(BBLMParamBlock ¶ms, 24 | const BBLMCallbackBlock &bblm_callbacks); 25 | 26 | Boolean IsNewline(UniChar c) 27 | { 28 | switch (c) { 29 | case '\r': 30 | case '\n': 31 | case 0x0085: 32 | case 0x2028: 33 | return true; 34 | } 35 | return false; 36 | } 37 | 38 | void EatNewline(BBLMParamBlock ¶ms, UInt32 *ppos) 39 | { 40 | BBLMTextIterator text(params); 41 | UInt32 pos = *ppos; 42 | if (text[pos] == '\r' && text[pos + 1] == '\n') { 43 | *ppos = pos + 2; 44 | return; 45 | } 46 | if (text[pos] == '\r') { 47 | *ppos = pos + 1; 48 | return; 49 | } 50 | if (text[pos] == '\n') { 51 | *ppos = pos + 1; 52 | return; 53 | } 54 | // Unicode-y things 55 | if (text[pos] == 0x0085) { 56 | *ppos = pos + 1; 57 | return; 58 | } 59 | if (text[pos] == 0x2028) { 60 | *ppos = pos + 1; 61 | return; 62 | } 63 | return; 64 | } 65 | 66 | /* 67 | * Read a string value up to the end of the line. Append any continuation lines. 68 | * Leaves *ppos at the start of the next line. 69 | */ 70 | CFStringRef ReadWrappedLine(BBLMParamBlock ¶ms, bool base64, UInt32 *ppos) 71 | { 72 | CFMutableDataRef data = NULL; 73 | CFMutableStringRef str = NULL; 74 | if (base64) 75 | data = CFDataCreateMutable(NULL, 0); 76 | else 77 | str = CFStringCreateMutable(NULL, 0); 78 | BBLMTextIterator text(params); 79 | UInt32 pos = *ppos; 80 | while (pos < params.fTextLength) { 81 | if (IsNewline(text[pos]) == false) { 82 | if (base64) { 83 | UInt8 byte = (UInt8)text[pos]; 84 | CFDataAppendBytes(data, &byte, 1); 85 | } else { 86 | UniChar ch = (UniChar)text[pos]; 87 | CFStringAppendCharacters(str, &ch, 1); 88 | } 89 | pos++; 90 | continue; 91 | } 92 | EatNewline(params, &pos); 93 | if (text[pos] != ' ') 94 | break; 95 | pos++; 96 | } 97 | if (base64) { 98 | CFDataRef output = NULL; 99 | SecTransformRef transform = SecDecodeTransformCreate(kSecBase64Encoding, NULL); 100 | if (SecTransformSetAttribute(transform, kSecTransformInputAttributeName, data, NULL)) 101 | output = (CFDataRef)SecTransformExecute(transform, NULL); 102 | str = (CFMutableStringRef)CFStringCreateFromExternalRepresentation(NULL, output, kCFStringEncodingUTF8); 103 | if (output) CFRelease(output); 104 | if (data) CFRelease(data); 105 | if (transform) CFRelease(transform); 106 | } 107 | *ppos = pos; 108 | return str; 109 | } 110 | 111 | 112 | /* 113 | * Ignore the rest of this wrapped line. 114 | * Leaves *ppos at the start of the next line. 115 | */ 116 | void SkipWrappedLine(BBLMParamBlock ¶ms, UInt32 *ppos) 117 | { 118 | BBLMTextIterator text(params); 119 | UInt32 pos = *ppos; 120 | while (pos < params.fTextLength) { 121 | 122 | if (IsNewline(text[pos]) == false) { 123 | pos++; 124 | continue; 125 | } 126 | EatNewline(params, &pos); 127 | if (text[pos] != ' ') 128 | break; 129 | } 130 | *ppos = pos; 131 | } 132 | 133 | /* 134 | * Ignore the rest of this plain line. 135 | * Leaves *ppos at the start of the next line. 136 | */ 137 | void SkipLine(BBLMParamBlock ¶ms, UInt32 *ppos) 138 | { 139 | BBLMTextIterator text(params); 140 | UInt32 pos = *ppos; 141 | while (pos < params.fTextLength) { 142 | 143 | if (IsNewline(text[pos]) == false) { 144 | pos++; 145 | continue; 146 | } 147 | EatNewline(params, &pos); 148 | break; 149 | } 150 | *ppos = pos; 151 | } 152 | 153 | void ScanForFunctions(BBLMParamBlock ¶ms, 154 | const BBLMCallbackBlock &bblm_callbacks) 155 | { 156 | UInt32 pos = 0; 157 | UInt32 startPos = pos; 158 | UInt32 endPos; 159 | UInt32 foldStart = 0; 160 | CFStringRef str = NULL; 161 | 162 | BBLMTextIterator text(params); 163 | bblmResetTokenBuffer(&bblm_callbacks, params.fFcnParams.fTokenBuffer); 164 | bblmResetProcList(&bblm_callbacks, params.fFcnParams.fFcnList); 165 | while (pos < params.fTextLength) { 166 | if (tolower(text[pos+0]) == 'd' && 167 | tolower(text[pos+1]) == 'n' && 168 | text[pos+2] == ':') { 169 | bool base64 = false; 170 | if (text[pos+3] == ':') 171 | base64 = true; 172 | startPos = pos; 173 | pos += 3; 174 | if (base64) 175 | pos++; 176 | 177 | while (text[pos] == ' ') { 178 | pos++; 179 | } 180 | str = ReadWrappedLine(params, base64, &pos); 181 | foldStart = pos; 182 | continue; 183 | } 184 | if (IsNewline(text[pos]) == true && str != NULL) { 185 | // i.e. a blank line 186 | endPos = pos - 1; 187 | EatNewline(params, &pos); 188 | 189 | UInt32 namePos; 190 | (void)bblmAddCFStringTokenToBuffer(&bblm_callbacks, params.fFcnParams.fTokenBuffer, str, &namePos); 191 | 192 | BBLMProcInfo info; 193 | memset(&info, 0, sizeof(info)); 194 | info.fFunctionStart = startPos; 195 | info.fFunctionEnd = endPos; 196 | info.fSelStart = startPos; 197 | info.fSelEnd = pos; 198 | info.fFirstChar = startPos; 199 | info.fIndentLevel = 0; 200 | info.fKind = kBBLMFunctionMark; 201 | info.fFlags = 0; 202 | info.fNameStart = namePos; 203 | info.fNameLength = (UInt32)CFStringGetLength(str); 204 | UInt32 index = 0; 205 | (void)bblmAddFunctionToList(&bblm_callbacks, params.fFcnParams.fFcnList, info, &index); 206 | (void)bblmAddFoldRange(&bblm_callbacks, foldStart, endPos - foldStart, kBBLMFunctionAutoFold); 207 | pos = endPos + 1; 208 | CFRelease(str); 209 | str = NULL; 210 | } 211 | SkipWrappedLine(params, &pos); 212 | } 213 | } 214 | 215 | void Split(BBLMParamBlock ¶ms, 216 | UInt32 pos, 217 | UInt32 *start, 218 | UInt32 *separator, 219 | UInt32 *value) 220 | { 221 | BBLMTextIterator text(params); 222 | *start = pos; 223 | while (pos < params.fTextLength && text[pos] != ':') { 224 | pos++; 225 | } 226 | *separator = pos; 227 | while (pos < params.fTextLength && (text[pos] == ':' || text[pos] == '<')) { 228 | pos++; 229 | } 230 | *value = pos; 231 | } 232 | 233 | // Case-insensitive match of a keyword 234 | bool Match(BBLMParamBlock ¶ms, 235 | const char *str, 236 | UInt32 pos) 237 | { 238 | BBLMTextIterator text(params); 239 | for (const char *p = str; *p; p++, pos++) { 240 | if (tolower(text[pos]) != (UniChar)(*p)) { 241 | return false; 242 | } 243 | } 244 | return true; 245 | } 246 | 247 | // keywords - code 248 | // separators - ldifseparator 249 | // values and dn - ldifvalue 250 | // - - ldifdash 251 | // types - ldiftype 252 | // comment - comment 253 | void ScanForRuns(BBLMParamBlock ¶ms, 254 | const BBLMCallbackBlock &bblm_callbacks) 255 | { 256 | BBLMTextIterator text(params); 257 | UInt32 pos = params.fCalcRunParams.fStartOffset; 258 | while (pos < params.fTextLength) { 259 | UInt32 start, separator, value; 260 | start = pos; 261 | if (IsNewline(text[pos]) == true) { 262 | EatNewline(params, &pos); 263 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kBBLMCodeRunKind, start, pos - start) == false) { 264 | return; 265 | } 266 | continue; 267 | } 268 | if (text[pos] == '#') { 269 | SkipLine(params, &pos); 270 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kBBLMLineCommentRunKind, start, pos - start) == false) { 271 | return; 272 | } 273 | continue; 274 | } 275 | // - 276 | if (text[pos] == '-' && IsNewline(text[pos + 1]) == true) { 277 | SkipLine(params, &pos); 278 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMDash, start, pos - start) == false) { 279 | return; 280 | } 281 | continue; 282 | } 283 | Split(params, pos, &start, &separator, &value); 284 | // dn: ... 285 | // newrdn: ... 286 | // newsuperior: ... 287 | if (Match(params, "dn", start) == true || 288 | Match(params, "newrdn", start) == true || 289 | Match(params, "newsuperior", start) == true) { 290 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kBBLMCodeRunKind, start, separator - start) == false) { 291 | return; 292 | } 293 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMSeparator, separator, value - separator) == false) { 294 | return; 295 | } 296 | pos = value; 297 | SkipWrappedLine(params, &pos); 298 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMName, value, pos - value) == false) { 299 | return; 300 | } 301 | continue; 302 | } 303 | 304 | // version: . 305 | if (Match(params, "version", start) == true) { 306 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kBBLMCodeRunKind, start, separator - start) == false) { 307 | return; 308 | } 309 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMSeparator, separator, value - separator) == false) { 310 | return; 311 | } 312 | pos = value; 313 | SkipWrappedLine(params, &pos); 314 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMValue, value, pos - value) == false) { 315 | return; 316 | } 317 | continue; 318 | } 319 | 320 | // changetype: add/del/etc 321 | // deleteoldrdn: true/false 322 | if (Match(params, "changetype", start) == true || 323 | Match(params, "deleteoldrdn", start) == true) { 324 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kBBLMCodeRunKind, start, separator - start) == false) { 325 | return; 326 | } 327 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMSeparator, separator, value - separator) == false) { 328 | return; 329 | } 330 | pos = value; 331 | SkipLine(params, &pos); 332 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kBBLMCodeRunKind, value, pos - value) == false) { 333 | return; 334 | } 335 | continue; 336 | } 337 | 338 | // add/delete/replace/increment: attribute 339 | if (Match(params, "add", start) == true || 340 | Match(params, "delete", start) == true || 341 | Match(params, "replace", start) == true || 342 | Match(params, "increment", start) == true) { 343 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kBBLMCodeRunKind, start, separator - start) == false) { 344 | return; 345 | } 346 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMSeparator, separator, value - separator) == false) { 347 | return; 348 | } 349 | pos = value; 350 | SkipLine(params, &pos); 351 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMType, value, pos - value) == false) { 352 | return; 353 | } 354 | continue; 355 | } 356 | 357 | // attribute: value 358 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMType, start, separator - start) == false) { 359 | return; 360 | } 361 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMSeparator, separator, value - separator) == false) { 362 | return; 363 | } 364 | pos = value; 365 | SkipWrappedLine(params, &pos); 366 | if (bblmAddRun(&bblm_callbacks, params.fLanguage, kLDIFBBLMValue, value, pos - value) == false) { 367 | return; 368 | } 369 | } 370 | } 371 | 372 | void AdjustRuns(BBLMParamBlock ¶ms, 373 | const BBLMCallbackBlock &bblm_callbacks) 374 | { 375 | UInt32 pos = params.fAdjustRangeParams.fStartIndex; 376 | BBLMTextIterator text(params); 377 | 378 | while (pos > 0) { 379 | if (IsNewline(text[pos-1]) == true) { 380 | break; 381 | } 382 | pos--; 383 | } 384 | params.fAdjustRangeParams.fStartIndex = pos; 385 | pos = params.fAdjustRangeParams.fEndIndex; 386 | while (pos < params.fTextLength) { 387 | if (IsNewline(text[pos]) == true) { 388 | break; 389 | } 390 | pos++; 391 | } 392 | params.fAdjustRangeParams.fEndIndex = pos; 393 | } 394 | 395 | OSErr LDIF(BBLMParamBlock ¶ms, 396 | const BBLMCallbackBlock &bblm_callbacks) 397 | { 398 | OSErr result = noErr; 399 | 400 | if (params.fSignature != kBBLMParamBlockSignature || 401 | params.fVersion < kBBLMParamBlockVersion) { 402 | fprintf(stderr, "Wrong sig or vers\n"); 403 | return paramErr; 404 | } 405 | 406 | switch (params.fMessage) 407 | { 408 | case kBBLMScanForFunctionsMessage: 409 | ScanForFunctions(params, bblm_callbacks); 410 | result = noErr; 411 | break; 412 | 413 | case kBBLMAdjustRangeMessage: 414 | AdjustRuns(params, bblm_callbacks); 415 | result = noErr; 416 | break; 417 | 418 | case kBBLMCalculateRunsMessage: 419 | ScanForRuns(params, bblm_callbacks); 420 | result = noErr; 421 | break; 422 | // kBBLMSetCategoriesMessage 423 | // kBBLMGuessLanguageMessage 424 | // kBBLMRunKindForWordMessage 425 | } 426 | return result; 427 | } 428 | -------------------------------------------------------------------------------- /bbedit/LDIF/Interfaces/Language Modules/BBLMTextUtils.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * BBLMTextUtils.cpp 3 | * 4 | * Created by Seth Dillingham on 2/14/2008. 5 | * Copyright 2008 Macrobyte Resources. 6 | * 7 | * Generic, UniChar-based, utility functions for language modules. 8 | * To use it, a language module has to subclass BBLMTextUtils, 9 | * such as with YAMLTextUtils or ScalaTextUtils. 10 | * 11 | * Most text-processing methods use a BBLMTextIterator, which is shared 12 | * with another object (such as a Tokenizer). So, for example, calling 13 | * skipInlineWhitespace() will cause the "host's" text iterator to be 14 | * incremented so that it points to the first non-inline-whitespace 15 | * character that is found at or after the iterator's current position. 16 | * 17 | * HOWEVER, any method whose name ends with "ByIndex" does NOT increment 18 | * the TextIterator. Instead, they accept a reference to an offset from the 19 | * BBLMTextIterator's current position, and update that offset (the index) 20 | * before returning. For an example, see ::skipLineByIndex 21 | * 22 | * 23 | * NOTE that this class was originally written for the YAML module, and 24 | * and may still have some non-generic stuff that needs to be either 25 | * refactored or moved to a subclass. 26 | */ 27 | 28 | #import 29 | #include 30 | 31 | #include "BBLMTextUtils.h" 32 | 33 | #pragma mark Character Tests and Init 34 | #pragma mark - 35 | 36 | // private 37 | void 38 | BBLMTextUtils::addCharsToSet( CFStringRef chars, CFMutableCharacterSetRef charSet ) { 39 | CFCharacterSetAddCharactersInString( charSet, chars ); 40 | } 41 | 42 | void 43 | BBLMTextUtils::addCharToSet( UniChar c, CFMutableCharacterSetRef charSet ) { 44 | CFStringRef s = CFStringCreateWithCharacters( kCFAllocatorDefault, &c, 1 ); 45 | 46 | this->addCharsToSet( s, charSet ); 47 | 48 | CFRelease( s ); 49 | } 50 | 51 | void 52 | BBLMTextUtils::removeCharsFromSet( CFStringRef chars, CFMutableCharacterSetRef charSet ) { 53 | CFCharacterSetRemoveCharactersInString( charSet, chars ); 54 | } 55 | 56 | void 57 | BBLMTextUtils::removeCharFromSet( UniChar c, CFMutableCharacterSetRef charSet ) { 58 | CFStringRef s = CFStringCreateWithCharacters( kCFAllocatorDefault, &c, 1 ); 59 | 60 | this->removeCharsFromSet( s, charSet ); 61 | 62 | CFRelease( s ); 63 | } 64 | 65 | #pragma mark - 66 | 67 | // protected 68 | void 69 | BBLMTextUtils::addBreakChar( UniChar c ) { 70 | this->addCharToSet( c, m_breakCharSet ); 71 | } 72 | 73 | void BBLMTextUtils::addBreakChars( CFStringRef s ) { 74 | this->addCharsToSet( s, m_breakCharSet ); 75 | } 76 | 77 | void 78 | BBLMTextUtils::clearBreakChar( UniChar c ) { 79 | this->removeCharFromSet( c, m_breakCharSet ); 80 | } 81 | 82 | void 83 | BBLMTextUtils::clearBreakChars( CFStringRef s ) { 84 | this->removeCharsFromSet( s, m_breakCharSet ); 85 | } 86 | 87 | bool 88 | BBLMTextUtils::isBreakChar( UniChar c ) { 89 | return CFCharacterSetIsCharacterMember( m_breakCharSet, c ); 90 | } 91 | 92 | #pragma mark - 93 | 94 | bool 95 | BBLMTextUtils::isInlineWhiteChar( UniChar c ) { 96 | return CFCharacterSetIsCharacterMember( m_inlineWhiteCharSet, c ); 97 | } 98 | 99 | void 100 | BBLMTextUtils::initEOLChars() { 101 | this->addCharsToSet(CFSTR( "\r\n" ), m_EOLCharSet); 102 | this->addCharToSet(0x0085, m_EOLCharSet); // NEXT LINE (NEL) 103 | this->addCharToSet(0, m_EOLCharSet); // null, for EOF cases 104 | } 105 | 106 | void 107 | BBLMTextUtils::initWhitespace() { 108 | this->addCharsToSet(CFSTR( " \t\f\v" ), m_inlineWhiteCharSet); 109 | } 110 | 111 | #pragma mark - 112 | 113 | bool 114 | BBLMTextUtils::isEOLChar( UniChar c ) { 115 | return CFCharacterSetIsCharacterMember( m_EOLCharSet, c ); 116 | } 117 | 118 | 119 | #pragma mark - 120 | #pragma mark Skippers 121 | #pragma mark - 122 | 123 | bool 124 | BBLMTextUtils::skipWhitespace() { 125 | while ( m_p.InBounds() ) 126 | { 127 | if ( isspace( *m_p ) ) 128 | m_p++; 129 | else 130 | return true; 131 | } 132 | 133 | return false; 134 | } 135 | 136 | bool 137 | BBLMTextUtils::skipInlineWhitespace() { 138 | SInt32 ct = 0; 139 | 140 | return this->skipInlineWhitespace( ct, m_p ); 141 | } 142 | 143 | bool 144 | BBLMTextUtils::skipInlineWhitespace( SInt32 & ctChars ) { 145 | return this->skipInlineWhitespace( ctChars, m_p ); 146 | } 147 | 148 | bool 149 | BBLMTextUtils::skipInlineWhitespace( BBLMTextIterator & p ) { 150 | SInt32 ct = 0; 151 | 152 | return this->skipInlineWhitespace( ct, p ); 153 | } 154 | 155 | 156 | bool 157 | BBLMTextUtils::skipInlineWhitespace( SInt32 & ctChars, BBLMTextIterator & p ) { 158 | ctChars = 0; 159 | 160 | while ( p.InBounds() ) 161 | { 162 | UniChar c = p[ 0 ]; 163 | 164 | if ( this->isInlineWhiteChar( c ) ) 165 | { 166 | ++ctChars; 167 | p++; 168 | } 169 | else if ( ( c == '\\' ) && this->isInlineWhiteChar( p[ 1 ] ) ) 170 | { 171 | ctChars += 2; 172 | p += 2; 173 | } 174 | else 175 | return true; 176 | } 177 | 178 | return false; 179 | } 180 | 181 | bool 182 | BBLMTextUtils::skipInlineWhitespaceByIndex( BBLMTextIterator & p, SInt32 & index ) { 183 | SInt32 ix = index, 184 | start = p.Offset(), 185 | limit = m_params.fTextLength; 186 | UniChar c; 187 | 188 | while ( start + ix < limit ) 189 | { 190 | c = p[ ix ]; 191 | 192 | if ( this->isInlineWhiteChar( c ) ) 193 | ++ix; 194 | else if ( '\\' == c ) 195 | { 196 | if ( this->isInlineWhiteChar( p[ ix + 1 ] ) ) 197 | ix += 2; 198 | else 199 | { 200 | index = ix; 201 | return ( start + ix < limit ); 202 | } 203 | } 204 | else 205 | { 206 | index = ix; 207 | return true; 208 | } 209 | } 210 | 211 | index = ix; 212 | 213 | return false; 214 | } 215 | 216 | void 217 | BBLMTextUtils::skipPreviousInlineWhitespace() { 218 | SInt32 ix = m_p.Offset(); 219 | UniChar c = *m_p; 220 | 221 | while ( m_p.Offset() >= 0 ) 222 | { 223 | if ( this->isInlineWhiteChar( c ) ) 224 | m_p--; 225 | else if ( '\\' == c ) 226 | { 227 | if ( ( m_p.Offset() < ix ) && this->isInlineWhiteChar( m_p[ 1 ] ) ) 228 | m_p--; 229 | else 230 | break; 231 | } 232 | else 233 | break; 234 | } 235 | } 236 | 237 | void 238 | BBLMTextUtils::skipPreviousInlineWhitespaceByIndex( BBLMTextIterator & p, SInt32 & index ) { 239 | SInt32 ix = index - 1; 240 | UniChar c; 241 | 242 | while ( ix >= 0 ) 243 | { 244 | c = p[ ix ]; 245 | 246 | if ( this->isInlineWhiteChar( c ) ) 247 | --ix; 248 | else if ( '\\' == c ) 249 | { 250 | if ( ( ix < index ) && this->isInlineWhiteChar( p[ ix + 1 ] ) ) 251 | { 252 | --ix; 253 | continue; 254 | } 255 | else 256 | { 257 | index = ix; 258 | return; 259 | } 260 | } 261 | else 262 | { 263 | index = ix; 264 | return; 265 | } 266 | } 267 | 268 | if ( ix < index ) 269 | index = ix + 1; 270 | else 271 | index = ix; 272 | 273 | return; 274 | } 275 | 276 | bool 277 | BBLMTextUtils::skipLine( BBLMTextIterator & p ) { 278 | if ( ! this->skipToEOL( p ) ) 279 | return false; 280 | 281 | p++; 282 | 283 | return p.InBounds(); 284 | } 285 | 286 | bool 287 | BBLMTextUtils::skipToEOL( BBLMTextIterator & p ) { 288 | while ( ! this->isEOLChar( *p ) ) 289 | p++; 290 | 291 | return p.InBounds(); 292 | } 293 | 294 | bool 295 | BBLMTextUtils::skipLineByIndex( BBLMTextIterator & p, SInt32 & index ) { 296 | SInt32 ix = index, 297 | limit = m_params.fTextLength, 298 | start = p.Offset(); 299 | 300 | while ( start + ix < limit ) 301 | { 302 | if ( this->isEOLChar( p[ ix ] ) ) 303 | break; 304 | 305 | ++ix; 306 | } 307 | 308 | if ( start + ix < limit ) // skip the \r 309 | ++ix; 310 | 311 | index = ix; 312 | 313 | return ( start + ix < limit ); 314 | } 315 | 316 | bool 317 | BBLMTextUtils::skipLineWithMaxIndexByIndex( SInt32 & index, SInt32 maxIndex ) { 318 | SInt32 ix = index, 319 | limit = m_params.fTextLength; 320 | 321 | if ( limit > maxIndex ) 322 | limit = maxIndex; 323 | 324 | while ( ix < limit ) 325 | { 326 | if ( BBLMCharacterIsLineBreak(m_p[ ix ]) ) 327 | { 328 | index = ix; 329 | return true; 330 | } 331 | else 332 | { 333 | ++ix; 334 | } 335 | } 336 | 337 | index = ix; 338 | 339 | return false; 340 | } 341 | 342 | SInt32 343 | BBLMTextUtils::findLineEndBeforeIndex( SInt32 index ) { 344 | BBLMTextIterator iter( m_p ); 345 | SInt32 ix = index; 346 | 347 | iter.SetOffset( 0 ); 348 | 349 | while ( ix >= 0 ) 350 | { 351 | if ( BBLMCharacterIsLineBreak(iter[ ix ]) ) 352 | break; 353 | 354 | --ix; 355 | } 356 | 357 | return ix; 358 | } 359 | 360 | SInt32 361 | BBLMTextUtils::findLineEndAfterIndex( SInt32 index ) { 362 | BBLMTextIterator iter( m_p ); 363 | SInt32 ix = index, 364 | limit = m_params.fTextLength; 365 | 366 | iter.SetOffset( 0 ); 367 | 368 | while ( ix < limit ) 369 | { 370 | if ( BBLMCharacterIsLineBreak(iter[ ix ]) ) 371 | break; 372 | 373 | ++ix; 374 | } 375 | 376 | return ix; 377 | } 378 | 379 | 380 | bool 381 | BBLMTextUtils::skipToCharByIndex( UniChar seeking, SInt32 & index, bool flAllowEscapes ) { 382 | SInt32 ix = index, 383 | limit = m_params.fTextLength, 384 | pos = m_p.Offset(); 385 | UniChar c; 386 | 387 | while ( pos + ix < limit ) 388 | { 389 | c = m_p[ ix ]; 390 | 391 | if ( c == seeking ) 392 | { 393 | index = ix; 394 | return true; 395 | } 396 | else if ( flAllowEscapes && ( '\\' == c ) && ( pos + ix + 1 < limit ) ) 397 | ++ix; 398 | 399 | ++ix; 400 | } 401 | 402 | return false; 403 | } 404 | 405 | bool 406 | BBLMTextUtils::skipToCharSameLineByIndex( UniChar seeking, SInt32 & index ) { 407 | SInt32 ix = index, 408 | limit = m_params.fTextLength, 409 | pos = m_p.Offset(); 410 | UniChar c; 411 | 412 | while ( pos + ix < limit ) 413 | { 414 | c = m_p[ ix ]; 415 | 416 | if ( c == seeking ) 417 | { 418 | index = ix; 419 | return true; 420 | } 421 | else if ( ( '\\' == c ) && ( pos + ix + 1 < limit ) && ( ! BBLMCharacterIsLineBreak(m_p[ ix + 1 ]) ) ) 422 | ++ix; 423 | else if ( BBLMCharacterIsLineBreak(c) ) 424 | return false; 425 | 426 | ++ix; 427 | } 428 | 429 | return false; 430 | } 431 | 432 | bool 433 | BBLMTextUtils::skipGroupByIndexCountingLines( BBLMTextIterator & p, SInt32 & index, const UniChar breakChar, SInt32 & ctLines ) { 434 | UniChar startChar = p[ index ], 435 | closeChar = startChar, 436 | c; 437 | SInt32 ix = index + 1, 438 | max = (SInt32) m_params.fTextLength - p.Offset(); 439 | 440 | switch ( startChar ) 441 | { 442 | case '{': 443 | closeChar = '}'; 444 | break; 445 | 446 | case '(': 447 | closeChar = ')'; 448 | break; 449 | 450 | case '[': 451 | closeChar = ']'; 452 | break; 453 | 454 | default: 455 | break; 456 | } 457 | 458 | for ( ; ix < max; ++ix ) 459 | { 460 | c = p[ ix ]; 461 | 462 | if ( ( c == closeChar ) || ( c == breakChar ) ) 463 | break; 464 | 465 | switch ( c ) 466 | { 467 | case '\r': 468 | case '\n': 469 | ++ctLines; 470 | 471 | break; 472 | 473 | case '\\': 474 | ++ix; 475 | 476 | break; 477 | 478 | case '{': 479 | case '(': 480 | case '[': 481 | // not passing the original breakChar here because we're going to be nested in a subgroup 482 | if ( ! this->skipGroupByIndexCountingLines( p, ix, 0, ctLines ) ) 483 | return false; 484 | 485 | break; 486 | 487 | default: 488 | break; 489 | } 490 | } 491 | 492 | index = ix; 493 | 494 | return ( (bool)p[ ix ] ); 495 | } 496 | 497 | bool 498 | BBLMTextUtils::skipOctal( BBLMTUNumberType & numberType ) { 499 | if ( '0' != *m_p ) 500 | return true; 501 | 502 | SInt32 startIx = m_p.Offset(); 503 | 504 | while ( isdigit( *m_p ) ) 505 | { 506 | if ( ( '9' == *m_p ) || ( '8' == *m_p ) ) 507 | { 508 | m_p--; 509 | 510 | return skipFloat( numberType ); 511 | } 512 | 513 | m_p++; 514 | } 515 | 516 | if ( ( '.' == *m_p ) || ( 'e' == *m_p ) || ( 'E' == *m_p ) ) 517 | { 518 | m_p--; 519 | 520 | return this->skipFloat( numberType ); 521 | } 522 | 523 | if ( m_p.Offset() - startIx > 1 ) 524 | numberType = kBBLMTUOctal; 525 | else 526 | numberType = kBBLMTULong; 527 | 528 | return ( 0 != *m_p ); 529 | } 530 | 531 | bool 532 | BBLMTextUtils::skipHex( BBLMTUNumberType & numberType ) { 533 | SInt32 startIx = m_p.Offset(); 534 | 535 | numberType = kBBLMTUHex; 536 | 537 | if ( ( *m_p == '0' ) && ( ( m_p[1] == 'x' ) || ( m_p[1] == 'X' ) ) ) 538 | { 539 | m_p += 2; 540 | 541 | for ( UniChar c = *m_p; isHexChar( c ); m_p++, c = *m_p ) 542 | { ; } 543 | 544 | if ( m_p.Offset() == startIx + 2 ) // no hex chars were found after the 0x 545 | m_p -= 2; 546 | } 547 | 548 | return ( 0 != *m_p ); 549 | } 550 | 551 | bool 552 | BBLMTextUtils::skipBinary( BBLMTUNumberType & numberType ) { 553 | SInt32 startIx = m_p.Offset(); 554 | 555 | numberType = kBBLMTUBinary; 556 | 557 | if ( ( *m_p == '0' ) && ( ( m_p[1] == 'b' ) || ( m_p[1] == 'B' ) ) ) 558 | { 559 | m_p += 2; 560 | 561 | for ( UniChar c = *m_p; ( '0' == c ) || ( '1' == c ); m_p++, c = *m_p ) 562 | { ; } 563 | 564 | if ( m_p.Offset() == startIx + 2 ) // no binary chars were found after the 0b 565 | m_p -= 2; 566 | } 567 | 568 | return ( 0 != *m_p ); 569 | } 570 | 571 | bool 572 | BBLMTextUtils::skipFloat( BBLMTUNumberType & numberType ) { 573 | numberType = kBBLMTULong; 574 | 575 | while ( isdigit( *m_p ) ) 576 | m_p++; 577 | 578 | if ( '.' == *m_p ) 579 | { 580 | m_p++; 581 | 582 | numberType = kBBLMTUFloat; 583 | } 584 | 585 | while ( isdigit( *m_p ) ) 586 | m_p++; 587 | 588 | if ( ( 'e' == *m_p ) || ( 'E' == *m_p ) ) 589 | { 590 | m_p++; 591 | 592 | if ( ( '-' == *m_p ) || ( '+' == *m_p ) ) 593 | m_p++; 594 | 595 | if ( ! isdigit( *m_p ) ) 596 | { 597 | m_p--; 598 | 599 | if ( ( '-' == *m_p ) || ( '+' == *m_p ) ) 600 | m_p--; 601 | 602 | return true; 603 | } 604 | 605 | numberType = kBBLMTUScientific; 606 | } 607 | 608 | while ( isdigit( *m_p ) ) 609 | m_p++; 610 | 611 | return ( 0 != *m_p ); 612 | } 613 | 614 | bool 615 | BBLMTextUtils::skipNumber( BBLMTUNumberType & numberType ) { 616 | if ( '0' == *m_p ) 617 | { 618 | if ( ( 'x' == m_p[1] ) || ( 'X' == m_p[1] ) ) 619 | { 620 | this->skipHex( numberType ); 621 | } 622 | else if ( ( 'b' == m_p[1] ) || ( 'B' == m_p[1] ) ) 623 | { 624 | this->skipBinary( numberType ); 625 | } 626 | else if ( ( 'e' == m_p[1] ) || ( 'E' == m_p[1] ) ) 627 | { 628 | this->skipFloat( numberType ); 629 | } 630 | else 631 | { 632 | this->skipOctal( numberType ); 633 | } 634 | } 635 | else // 1.2345e+3 636 | { 637 | this->skipFloat( numberType ); 638 | } 639 | 640 | return m_p.InBounds(); 641 | } 642 | 643 | 644 | #pragma mark - 645 | #pragma mark Tests 646 | 647 | bool 648 | BBLMTextUtils::matchChars( const char * pat ) { 649 | SInt32 i, 650 | pos = m_p.Offset(); 651 | 652 | for ( i = 0; *pat; ++i, pat++ ) 653 | { 654 | if ( pos + i >= (SInt32) m_params.fTextLength ) 655 | return false; 656 | 657 | if (BBLMCharacterIsLineBreak(m_p[i]) && BBLMCharacterIsLineBreak(*pat)) 658 | continue; 659 | 660 | if ( m_p[ i ] != *pat ) 661 | return false; 662 | } 663 | 664 | return true; 665 | } 666 | 667 | bool 668 | BBLMTextUtils::imatchChars( const char * pat ) { 669 | SInt32 i = 0, 670 | pos = m_p.Offset(); 671 | 672 | for ( i = 0; *pat; ++i, pat++ ) 673 | { 674 | if ( pos + i >= (SInt32) m_params.fTextLength ) 675 | return false; 676 | 677 | if (BBLMCharacterIsLineBreak(m_p[i]) && BBLMCharacterIsLineBreak(*pat)) 678 | continue; 679 | 680 | if ( LowerChar( m_p[ i ] ) != LowerChar( *pat ) ) 681 | return false; 682 | } 683 | 684 | return true; 685 | } 686 | 687 | 688 | 689 | #pragma mark - 690 | 691 | 692 | /* 693 | Starting at position rightSide, move to the left until a non-whitespace char is found 694 | */ 695 | bool 696 | BBLMTextUtils::rightTrimByIndex( SInt32 leftIndex, SInt32 rightSide ) { 697 | SInt32 ix, lastReturn; 698 | bool flContinue = true; 699 | UniChar c; 700 | 701 | ix = lastReturn = rightSide; 702 | 703 | for ( ; flContinue && ( ix > leftIndex ); --ix ) 704 | { 705 | c = m_p[ ix ]; 706 | 707 | if ( this->isInlineWhiteChar( c ) ) 708 | ; 709 | else if ( this->isEOLChar( c ) ) 710 | lastReturn = ix - 1; 711 | else 712 | flContinue = false; 713 | } 714 | 715 | return lastReturn; 716 | } 717 | 718 | bool 719 | BBLMTextUtils::skipDelimitedStringByIndex( SInt32 & index, bool flAllowEOL, 720 | bool flAllowEscapes, bool flAllowEscapedEOL ) { 721 | SInt32 ix = index + 1, 722 | limit = m_params.fTextLength - m_p.Offset(); 723 | UniChar c, 724 | delim; 725 | 726 | if ( ix < limit ) 727 | delim = m_p[ index ]; 728 | else 729 | return false; 730 | 731 | while ( ix < limit ) 732 | { 733 | c = m_p[ ix ]; 734 | 735 | if ( delim == c ) 736 | break; 737 | else if ( ! flAllowEOL && this->isEOLChar( c ) ) 738 | break; 739 | else if ( flAllowEscapes && ( '\\' == c ) ) 740 | { 741 | if ( ! flAllowEscapedEOL && this->isEOLChar( m_p[ ix + 1 ] ) ) 742 | break; 743 | 744 | ++ix; 745 | } 746 | 747 | ++ix; 748 | } 749 | 750 | index = ix + ( ( ix < limit ) ? 1 : 0 ); 751 | 752 | return index < limit; 753 | } 754 | 755 | UInt32 756 | BBLMTextUtils::countLinesInRange( UInt32 rangeStart, UInt32 rangeStop, UInt32 maxLinesToFind ) 757 | { 758 | if (rangeStop <= rangeStart) 759 | return 0; 760 | 761 | UInt32 i = 0, length = rangeStop - rangeStart, 762 | ctLines = 0; 763 | BBLMTextIterator p( m_params, rangeStart ); 764 | 765 | while ( p.InBounds() && ( i < length ) ) 766 | { 767 | if ( BBLMCharacterIsLineBreak(*p) ) 768 | { 769 | ctLines++; 770 | } 771 | 772 | if ( ctLines >= maxLinesToFind ) 773 | { 774 | break; 775 | } 776 | 777 | p++; 778 | ++i; 779 | } 780 | 781 | return ctLines; 782 | } 783 | 784 | #pragma mark - 785 | 786 | inline NSUInteger PIN(NSUInteger a, NSUInteger b) { 787 | return (a < b) ? a : b; 788 | } 789 | 790 | SInt32 791 | BBLMTextUtils::copyCollapsedRangeToBuffer( SInt32 & rangeStart, SInt32 & rangeEnd, UniChar * buffer, NSUInteger maxLength ) { 792 | BBLMTextIterator p( m_p ); 793 | SInt32 j = 0; 794 | const NSUInteger strLength = PIN(static_cast(rangeEnd - rangeStart + 1), maxLength); 795 | bool flFirstChar = true, 796 | flNeedSpace = false; 797 | 798 | if ( rangeEnd < rangeStart ) 799 | return 0; 800 | 801 | p.SetOffset( rangeStart ); 802 | 803 | for ( NSUInteger i = 0; i < strLength; i++, p++ ) 804 | { 805 | const UniChar c = *p; 806 | 807 | if ( isspace( c ) ) 808 | { 809 | if ( flFirstChar ) 810 | { 811 | rangeStart++; 812 | continue; 813 | } 814 | 815 | flFirstChar = false; 816 | flNeedSpace = true; 817 | continue; 818 | } 819 | else if ( flNeedSpace ) 820 | { 821 | buffer[ j++ ] = ' '; 822 | flNeedSpace = false; 823 | } 824 | 825 | flFirstChar = false; 826 | buffer[ j++ ] = c; 827 | } 828 | 829 | p--; 830 | for ( SInt32 i = rangeEnd; i > rangeStart; i--, p-- ) 831 | { 832 | if ( isspace( *p ) ) 833 | --rangeEnd; 834 | else 835 | break; 836 | } 837 | 838 | return j; 839 | } 840 | 841 | CFStringRef 842 | BBLMTextUtils::createCFStringFromOffsets( SInt32 & start, SInt32 & stop, SInt32 maxLength ) { 843 | enum { kMaxStaticLength = 256 }; 844 | CFStringRef str = NULL; 845 | SInt32 strLength = PIN((stop - start) + 1, maxLength); 846 | UniChar chars[kMaxStaticLength]; 847 | UniChar *buffer = nil; 848 | UniChar *dst = chars; 849 | 850 | if (stop < start) { 851 | goto errorExitBadParameters; 852 | } 853 | 854 | if (strLength > kMaxStaticLength) 855 | { 856 | if (nil == (buffer = new UniChar[strLength])) { 857 | goto errorExitAllocationFailed; 858 | } 859 | dst = buffer; 860 | } 861 | 862 | strLength = this->copyCollapsedRangeToBuffer( start, stop, dst, maxLength ); 863 | 864 | // make the string 865 | str = CFStringCreateWithCharacters( kCFAllocatorDefault, dst, strLength ); 866 | 867 | if (NULL == str) { 868 | goto errorExitCFStringCreateFailed; 869 | } 870 | 871 | errorExitAllocationFailed: 872 | errorExitBadParameters: 873 | errorExitCFStringCreateFailed: 874 | 875 | delete[] buffer; 876 | 877 | return str; 878 | } 879 | 880 | CFStringRef 881 | BBLMTextUtils::createCFStringFromOffsetsWithPrefix( SInt32 & start, SInt32 & stop, SInt32 maxLength, CFStringRef prefix ) { 882 | CFStringRef str = nil; 883 | 884 | str = this->createCFStringFromOffsets(start, stop, maxLength); 885 | if ((nil != str) && 886 | (nil != prefix) && 887 | (0 != CFStringGetLength(prefix))) 888 | { 889 | CFMutableStringRef result = nil; 890 | 891 | result = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, prefix); 892 | if (nil != result) 893 | CFStringAppend(result, str); 894 | 895 | CFRelease(str); 896 | 897 | str = result; 898 | } 899 | 900 | return str; 901 | } 902 | -------------------------------------------------------------------------------- /bbedit/LDIF/Interfaces/Language Modules/BBLMInterface.h: -------------------------------------------------------------------------------- 1 | // -*- mode: objective-c++; coding: utf-8; -*- 2 | 3 | #ifndef BBLMINTERFACE_h 4 | #define BBLMINTERFACE_h 1 5 | 6 | enum 7 | { 8 | kBBLMCurrentCallbackVersion = 0x00000007 9 | }; 10 | 11 | typedef enum 12 | { 13 | kBBLMFunctionMark, 14 | kBBLMTypedef, 15 | kBBLMPragmaMark, 16 | kBBLMInclude, 17 | kBBLMSysInclude, 18 | 19 | // 20 | // the following collection of function types is for comments with specific 21 | // "callout" formations. If you have a formation that doesn't fit one of these, 22 | // use kBBLMNoteCallout. 23 | // 24 | kBBLMFixMeCallout, // example: /* FIXME: this won't work in Y2K */ 25 | kBBLMToDoCallout, // example: /* TODO: add support for blargh */ 26 | kBBLMReviewCallout, // example: /* REVIEW for correctness */ 27 | kBBLMQuestionCallout, // example: /* ???:correia:20080717 what should the final value really be here? */ 28 | kBBLMWarningCallout, // example: /* !!!:siegel: this looks wrong to me */ 29 | kBBLMNoteCallout, // example: /* NOTE: You should always check for NULL here */ 30 | 31 | kBBLMURLInclude, 32 | kBBLMSiteRelativeInclude, 33 | 34 | kBBLMFunctionEnumDeclaration, 35 | kBBLMFunctionStructDeclaration, 36 | kBBLMFunctionUnionDeclaration, 37 | kBBLMFunctionClassDeclaration, 38 | kBBLMFunctionProtocolDeclaration, 39 | kBBLMFunctionClassInterface, 40 | kBBLMFunctionClassImplementation, 41 | kBBLMFunctionInstanceVariable, 42 | kBBLMFunctionMethod, 43 | kBBLMFunctionPropertyDeclaration, 44 | kBBLMFunctionPropertySynthesis, 45 | kBBLMFunctionSGMLNamedID, 46 | kBBLMFunctionHTMLNamedAnchor, 47 | kBBLMFunctionHTMLHeading1, 48 | kBBLMFunctionHTMLHeading2, 49 | kBBLMFunctionHTMLHeading3, 50 | kBBLMFunctionHTMLHeading4, 51 | kBBLMFunctionHTMLHeading5, 52 | kBBLMFunctionHTMLHeading6, 53 | 54 | kBBLMLastUsedFunctionKind, // do not change or use this value; it should always 55 | // occur after the last factory defined value 56 | kBBLMLastCoreFunctionKind = 31, 57 | kBBLMReservedFunctionKind, // do not generate any function entries with this kind! 58 | kBBLMFirstUserFunctionKind, 59 | 60 | // moved from HTMLFunctionScanner because they were used in a switch statement, and LLVM 4 noticed they weren't "real" BBLMFunctionKinds 61 | kHTMLFunctionJavascriptRun = 255, 62 | kHTMLFunctionPHPRun = 254, 63 | kHTMLFunctionCSSRun = 253, 64 | kHTMLFunctionJSPRun = 252, 65 | kHTMLFunctionASPRun = 251, 66 | kHTMLFunctionRubyRun = 250 67 | 68 | } BBLMFunctionKinds; 69 | 70 | #define kBBLMFunctionTypeGeneral @"com.barebones.bblm.function-type.function" 71 | #define kBBLMFunctionTypeTypedef @"com.barebones.bblm.function-type.typedef" 72 | #define kBBLMFunctionTypeNamedMark @"com.barebones.bblm.function-type.named-mark" 73 | #define kBBLMFunctionTypeInclude @"com.barebones.bblm.function-type.include" 74 | #define kBBLMFunctionTypeSystemInclude @"com.barebones.bblm.function-type.include.system" 75 | #define kBBLMFunctionTypeFIXMECallout @"com.barebones.bblm.function-type.callout.fixme" 76 | #define kBBLMFunctionTypeTODOCallout @"com.barebones.bblm.function-type.callout.to-do" 77 | #define kBBLMFunctionTypeREVIEWCallout @"com.barebones.bblm.function-type.callout.review" 78 | #define kBBLMFunctionTypeQuestionCallout @"com.barebones.bblm.function-type.callout.question" 79 | #define kBBLMFunctionTypeWarningCallout @"com.barebones.bblm.function-type.callout.warning" 80 | #define kBBLMFunctionTypeNoteCallout @"com.barebones.bblm.function-type.callout.note" 81 | #define kBBLMFunctionTypeClassDeclaration @"com.barebones.bblm.function-type.class-declaration" 82 | #define kBBLMFunctionTypeProtocolDeclaration @"com.barebones.bblm.function-type.protocol-declaration" 83 | #define kBBLMFunctionTypeClassInterface @"com.barebones.bblm.function-type.class-interface" 84 | #define kBBLMFunctionTypeClassImplementation @"com.barebones.bblm.function-type.class-implementation" 85 | #define kBBLMFunctionTypeInstanceVariable @"com.barebones.bblm.function-type.ivar" 86 | #define kBBLMFunctionTypeMethod @"com.barebones.bblm.function-type.method" 87 | #define kBBLMFunctionTypePropertyDeclaration @"com.barebones.bblm.function-type.property.declaration" 88 | #define kBBLMFunctionTypePropertySynthesis @"com.barebones.bblm.function-type.property.synthesis" 89 | #define kBBLMFunctionTypeEnumeration @"com.barebones.bblm.function-type.enumeration" 90 | #define kBBLMFunctionTypeStructureDeclaration @"com.barebones.bblm.function-type.structure" 91 | #define kBBLMFunctionTypeUnionDeclaration @"com.barebones.bblm.function-type.union" 92 | #define kBBLMFunctionTypeSGMLNamedID @"com.barebones.bblm.function-type.sgml-named-id" 93 | #define kBBLMFunctionTypeHTMLNamedAnchor @"com.barebones.bblm.function-type.html-named-anchor" 94 | #define kBBLMFunctionTypeHTMLHeading1 @"com.barebones.bblm.function-type.html-named-heading-1" 95 | #define kBBLMFunctionTypeHTMLHeading2 @"com.barebones.bblm.function-type.html-named-heading-2" 96 | #define kBBLMFunctionTypeHTMLHeading3 @"com.barebones.bblm.function-type.html-named-heading-3" 97 | #define kBBLMFunctionTypeHTMLHeading4 @"com.barebones.bblm.function-type.html-named-heading-4" 98 | #define kBBLMFunctionTypeHTMLHeading5 @"com.barebones.bblm.function-type.html-named-heading-5" 99 | #define kBBLMFunctionTypeHTMLHeading6 @"com.barebones.bblm.function-type.html-named-heading-6" 100 | 101 | typedef enum 102 | { 103 | kBBLMIsPrototype = 0x00000001, 104 | kBBLMIsForwardDecl = 0x00000002 105 | } BBLMFunctionFlags; 106 | 107 | typedef enum 108 | { 109 | // 110 | // Beginning with BBEdit 9.2, these three bits are always set; 111 | // you should always generate entries for prototypes, includes, 112 | // and callouts when appropriate. 113 | // 114 | 115 | kBBLMShowPrototypes = 0x00000001, 116 | kBBLMShowIncludes = 0x00000002, 117 | kBBLMShowCommentCallouts= 0x00000004 118 | } BBLMFcnOptionFlags; 119 | 120 | // 121 | // You are encouraged to generate runs with these run kinds wherever 122 | // possible. Do *not* add your own run kinds that begin with 123 | // "com.barebones"; instead, use your own reverse-domain-name space 124 | // (starting with your bundle ID) for your custom run kinds. The 125 | // language module documentation contains complete information. 126 | // 127 | 128 | #define kBBLMCodeRunKind @"com.barebones.bblm.code" 129 | #define kBBLMPreprocessorRunKind @"com.barebones.bblm.preprocessor" 130 | #define kBBLMPostPreprocessorRunKind @"com.barebones.bblm.postpreprocessor" 131 | #define kBBLMCommentRunKind @"com.barebones.bblm.comment" // use for languages that don't distinguish between... 132 | #define kBBLMLineCommentRunKind @"com.barebones.bblm.line-comment" // ...a "rest of line" comment, e.g. "//" in C or "#" in Ruby 133 | #define kBBLMBlockCommentRunKind @"com.barebones.bblm.block-comment" // ...a begin/end delimited comment, e.g. /*...*/ in C 134 | #define kBBLMStringRunKind @"com.barebones.bblm.string" // use for languages that don't distinguish between... 135 | #define kBBLMSingleQuotedStringRunKind @"com.barebones.bblm.single-string" // ...a single-quoted string, e.g. 'a' in C 136 | #define kBBLMDoubleQuotedStringRunKind @"com.barebones.bblm.double-string" // ...a double-quoted string, e.g. "hello world" in C 137 | #define kBBLMHereDocStringRunKind @"com.barebones.bblm.heredoc-string" // ...a "here doc" string as used in many scripting languages 138 | #define kBBLMNumberRunKind @"com.barebones.bblm.number" 139 | #define kBBLMFileIncludeRunKind @"com.barebones.bblm.file-include" 140 | #define kBBLMVariableRunKind @"com.barebones.bblm.variable" 141 | #define kBBLMKeywordRunKind @"com.barebones.bblm.keyword" 142 | #define kBBLMPredefinedSymbolRunKind @"com.barebones.bblm.predefined-symbol" 143 | #define kBBLMIndexedSymbolRunKind @"com.barebones.bblm.indexed-symbol" 144 | #define kBBLMSGMLCDATARunKind @"com.barebones.bblm.sgml-cdata" // SGML unparsed character data (i.e. inside of a block) 145 | #define kBBLMSGMLPCDATARunKind @"com.barebones.bblm.sgml-pcdata" // SGML parsed character data (i.e. things not in tags) 146 | #define kBBLMSGMLEntityRunKind @"com.barebones.bblm.sgml-entity" // an SGML/HTML/XML entity (named or numeric) 147 | #define kBBLMSGMLDeclarationRunKind @"com.barebones.bblm.sgml-decl" // (not including comments) 148 | #define kBBLMSGMLTagRunKind @"com.barebones.bblm.sgml-tag" // singleton SGML/HTML tags, e.g.
149 | #define kBBLMSGMLOpenTagRunKind @"com.barebones.bblm.sgml-open-tag" // openers such as
    150 | #define kBBLMSGMLCloseTagRunKind @"com.barebones.bblm.sgml-close-tag" // closers for openers (e.g.
) 151 | #define kBBLMXMLPIRunKind @"com.barebones.bblm.xml-pi" // processing instructions: 152 | #define kBBLMXMLEmptyTagRunKind @"com.barebones.bblm.xml-empty" // empty XML tags e.g.
153 | 154 | #define kBBLMEmbeddedLanguageStartRunKind @"com.barebones.bblm.embedded-start" // zero-length runs, marking start or end 155 | #define kBBLMEmbeddedLanguageEndRunKind @"com.barebones.bblm.embedded-end" // of languages, such as javascript in html 156 | 157 | enum 158 | { 159 | // 160 | // these are values for the result from 161 | // kBBLMGuessLanguageMessage. Note that you should not return a 162 | // value greater than kBBLMGuessDefiniteYes, or else guessing 163 | // will not work correctly. 164 | // 165 | 166 | kBBLMGuessDefiniteNo, 167 | kBBLMGuessMaybe = 127, 168 | kBBLMGuessDefiniteYes = 255 169 | }; 170 | 171 | enum 172 | { 173 | kBBLMManualFold = 0, 174 | kBBLMGenericAutoFold = 1, 175 | kBBLMModuleAutoFold = 2, 176 | kBBLMClassAutoFold = 3, 177 | kBBLMFunctionAutoFold = 4, 178 | kBBLMDataAutoFold = 5, 179 | kBBLMBlockCommentAutoFold = 6, 180 | kBBLMHereDocAutoFold = 7, 181 | 182 | kBBLMLastFoldKind = 31 183 | }; 184 | 185 | typedef UInt32 BBLMFoldKind; 186 | 187 | typedef SInt8 BBLMCategoryTable[256]; 188 | typedef SInt8 BBLMUnicodeCategoryTable[65536]; 189 | 190 | typedef enum 191 | { 192 | kBBLMNullMessage, // reserved 193 | 194 | kBBLMInitMessage, // the language module should perform any global initializations 195 | kBBLMDisposeMessage, // the language module is being shut down forever; time to clean up 196 | 197 | kBBLMScanForFunctionsMessage, // generate a list of functions in the supplied text. 198 | // relevant parameters are in fFcnParams of the BBLMParamBlock 199 | 200 | kBBLMAdjustRangeMessage, // adjust indices of first and last language run to be rescanned 201 | 202 | kBBLMCalculateRunsMessage, // generate a list of language runs in the supplied text 203 | // relevant parameters are in fCalcRunParams of the BBLMParamBlock 204 | 205 | kBBLMAdjustEndMessage, // adjust offset to last character in text that needs to be redrawn 206 | 207 | kBBLMSetCategoriesMessage, // configure character categories 208 | 209 | kBBLMEscapeStringMessage, // escape a string 210 | 211 | kBBLMGuessLanguageMessage, // figure out whether the provided text is in our language 212 | 213 | kBBLMWordLeftStringMessage, // return a PCRE pattern C-string ptr for a word-left search 214 | 215 | kBBLMWordRightStringMessage, // return a PCRE pattern C-string ptr for a word-right search 216 | 217 | kBBLMScanForFoldRangesMessage, // generate a list of text ranges that may be of interest 218 | // for folding purposes 219 | 220 | kBBLMCanSpellCheckRunMessage, // return whether the given run of text can be spell checked 221 | 222 | kBBLMScanSubrangeForFunctionsMessage, // like kBBLMScanForFunctionsMessage, but takes a range of text 223 | // to scan rather than requiring examination of the whole file 224 | 225 | kBBLMCreateSupportedLanguagesDictionary,// if the module has a skeleton bblm info that indicates that it 226 | // should be asked for the supported languages dictionary, it 227 | // will get called with this message. Note that this call is 228 | // a "create"; your module should either return a copy or retain 229 | // the value that it's about to return, because the application will 230 | // release it when it's done. 231 | 232 | kBBLMFindEndOfEmbeddedLanguageMessage, // ask a "parent" language's module to find the end of an embedded 233 | // language (essentially, the start of the "closing" delimiter) 234 | // so that we'll know how to set the fTextLength when we send the 235 | // kBBLMCalculateRunsMessage to the embedded language's module 236 | 237 | kBBLMAdjustRangeForTextCompletion, // if kBBLMSupportsTextCompletion is set, this message may 238 | // be sent to ask the module to adjust the proposed character 239 | // range prior to being sent a kBBLMCreateTextCompletionArray 240 | // message 241 | 242 | kBBLMFilterRunForTextCompletion, // if kBBLMSupportsTextCompletion is set, this message may 243 | // be sent to ask the module if tokens in the given run of 244 | // text may be considered for completion (based on the run 245 | // kind and any other contextual information that the module 246 | // cares to use). 247 | 248 | kBBLMSetCategoriesForTextCompletionMessage, 249 | // if kBBLMSupportsTextCompletion is set, this message may 250 | // be sent to return a custom category table to be used for 251 | // computing tokens during text completion. If you receive 252 | // this message, you may either handle it as though you had 253 | // received a kBBLMSetCategoriesMessage, or you may return 254 | // a custom category table. 255 | // 256 | 257 | kBBLMCreateTextCompletionArray, // if kBBLMSupportsTextCompletion is set, this message may 258 | // be sent to ask the module to return an array of possible 259 | // completions 260 | 261 | kBBLMCreateURLByResolvingIncludeFileMessage, 262 | // if kBBLMCanResolveIncludeFiles is set, this message will be 263 | // sent to ask the module to return a URL to the included 264 | // file on disk (or elsewhere) 265 | 266 | kBBLMRunKindForWordMessage, // If the module has BBLMSupportsWordLookup = YES, this 267 | // message will be sent to ask the module for the run kind 268 | // corresponding to a particular word. (This replaces 269 | // kBBLMMatchKeywordWithCFStringMessage and 270 | // kBBLMMatchPredefinedNameMessage from the old API.) 271 | 272 | kBBLMAutoPairMessage, // When called, the editor is considering auto-pairing 273 | // a typed character. 274 | 275 | kBBLMLastMessage 276 | } BBLMMessage; 277 | 278 | #if (! __LP64__) 279 | #pragma pack(push, 2) 280 | #endif 281 | 282 | // BBLMProcInfo - generated and used by the function scanner 283 | 284 | typedef struct BBLMProcInfo 285 | { 286 | UInt32 fFunctionStart; // char offset in file of first character of function 287 | UInt32 fFunctionEnd; // char offset of last character of function 288 | 289 | UInt32 fSelStart; // first character to select when choosing function 290 | UInt32 fSelEnd; // last character to select when choosing function 291 | 292 | UInt32 fFirstChar; // first character to make visible when choosing function 293 | 294 | UInt32 fIndentLevel; // indentation level of token 295 | UInt32 fKind; // token kind (see BBLMFunctionKinds for core kinds) 296 | UInt32 fFlags; // token flags (see BBLMFunctionFlags) 297 | UInt32 fNameStart; // char offset in token buffer of token name 298 | SInt32 fNameLength; // length of token name 299 | } BBLMProcInfo; 300 | 301 | // 302 | // BBLMRunRec - generated and used by the syntax coloring machinery 303 | // 304 | 305 | #if __LP64__ 306 | 307 | typedef struct 308 | { 309 | OSType language; 310 | NSString *runKind /* this value is neither retained nor released */; 311 | SInt32 startPos; 312 | SInt32 length; 313 | UInt16 depth; 314 | } BBLMRunRec; 315 | 316 | #else 317 | 318 | #pragma pack(push, 1) 319 | typedef struct 320 | { 321 | OSType language; 322 | NSString *runKind /* this value is neither retained nor released */; 323 | SInt32 startPos; 324 | unsigned int length : 24; 325 | unsigned int depth : 8; 326 | } BBLMRunRec; 327 | #pragma pack(pop) 328 | 329 | #endif 330 | 331 | // 332 | // Dictionary keys for the array returned by kBBLMCreateSymbolCompletionArray message 333 | // 334 | 335 | #define kBBLMCompletionSymbolType CFSTR("SymbolType") // CFStringRef; describes the kind of symbol 336 | // Recommended values for kBBLMCompletionSymbolType. If you define your own, they should 337 | // begin with your language module's bundle identifier to eliminate the possibility of 338 | // conflicts. (Using your bundle ID will also make future UI enhancements possible.) 339 | // Use kBBLMSymbolTypeGenericIdentifier if all else fails. 340 | #define kBBLMSymbolTypeCallout CFSTR("com.barebones.bblm.callout") 341 | #define kBBLMSymbolTypeClass CFSTR("com.barebones.bblm.class") 342 | #define kBBLMSymbolTypeCSSPropertyName CFSTR("com.barebones.bblm.cssPropertyName") 343 | #define kBBLMSymbolTypeEnumerationName CFSTR("com.barebones.bblm.enumName") 344 | #define kBBLMSymbolTypeEnumerationValue CFSTR("com.barebones.bblm.enumValue") 345 | #define kBBLMSymbolTypeExternVariable CFSTR("com.barebones.bblm.externVar") 346 | #define kBBLMSymbolTypeFunction CFSTR("com.barebones.bblm.function") 347 | #define kBBLMSymbolTypeFunctionPrototype CFSTR("com.barebones.bblm.prototype") 348 | #define kBBLMSymbolTypeGenericIdentifier CFSTR("com.barebones.bblm.identifier") 349 | #define kBBLMSymbolTypeGlobalVariable CFSTR("com.barebones.bblm.globalVar") 350 | #define kBBLMSymbolTypeIncludeFile CFSTR("com.barebones.bblm.includeFile") 351 | #define kBBLMSymbolTypeIVar CFSTR("com.barebones.bblm.ivar") 352 | #define kBBLMSymbolTypeLanguageKeyword CFSTR("com.barebones.bblm.keyword") 353 | #define kBBLMSymbolTypeLocalVariable CFSTR("com.barebones.bblm.localVar") 354 | #define kBBLMSymbolTypeMacro CFSTR("com.barebones.bblm.#define") 355 | #define kBBLMSymbolTypeMember CFSTR("com.barebones.bblm.member") 356 | #define kBBLMSymbolTypeMethod CFSTR("com.barebones.bblm.method") 357 | #define kBBLMSymbolTypeNamespace CFSTR("com.barebones.bblm.namespace") 358 | #define kBBLMSymbolTypePredefinedSymbol CFSTR("com.barebones.bblm.predefined") 359 | #define kBBLMSymbolTypeSGMLAttributeName CFSTR("com.barebones.bblm.attributeName") 360 | #define kBBLMSymbolTypeSGMLAttributeValue CFSTR("com.barebones.bblm.attributeValue") 361 | #define kBBLMSymbolTypeSGMLElement CFSTR("com.barebones.bblm.element") 362 | #define kBBLMSymbolTypeStaticType CFSTR("com.barebones.bblm.typedef") 363 | #define kBBLMSymbolTypeStruct CFSTR("com.barebones.bblm.struct") 364 | #define kBBLMSymbolTypeUnion CFSTR("com.barebones.bblm.union") 365 | #define kBBLMSymbolTypeTextTemplate CFSTR("com.barebones.bblm.text-template") 366 | 367 | #define kBBLMSymbolCompletionDisplayString CFSTR("DisplayString") // CFStringRef; used in the presentation UI 368 | 369 | #define kBBLMSymbolCompletionText CFSTR("CompletionText") // CFStringRef; inserted upon acceptance 370 | // (and may contain placeholders) 371 | 372 | #define kBBLMSymbolCompletionSortName CFSTR("SortName") // CFStringRef; Optional: if present, is used for sorting the 373 | // list in the GUI. If absent, the display string 374 | // is used for sorting. 375 | 376 | #define kBBLMSymbolCompletionSymbolOffset CFSTR("SymbolOffset") // CFNumberRef; Optional: integer (zero-based) specifying 377 | // the character offset in the text of where this symbol 378 | // was found. 379 | 380 | #define kBBLMSymbolCompletionAutoIndent CFSTR("AutoIndentCompletionText") 381 | // CFBooleanRef; Optional: if present and YES, indicates 382 | // that the completion text is multi-line and should be 383 | // auto-indented to match the surrounding document content. 384 | 385 | // Flag values for bblmCreateSymbolArrayParams.fOutAdditionalLookupFlags 386 | enum 387 | { 388 | kBBLMSymbolLookupNoFlags = 0x00000000, 389 | 390 | kBBLMSymbolLookupPredefinedNames = 0x00000001, 391 | kBBLMSymbolLookupCurrentFileCtags = 0x00000002, 392 | kBBLMSymbolLookupNearbyCtags = 0x00000004, 393 | kBBLMSymbolLookupClippings = 0x00000008, 394 | kBBLMSymbolLookupWordsInFrontWindow = 0x00000010, 395 | kBBLMSymbolLookupWordsInSystemDict = 0x00000020, 396 | kBBLMSymbolLookupTagMaker = 0x00000040, 397 | kBBLMSymbolLookupTextReplacements = 0x00000080, 398 | 399 | kBBLMSymbolLookupEverywherePossible = 0xFFFFFFFF 400 | }; 401 | 402 | // 403 | // Union members for the BBLMParamBlock structure 404 | // 405 | 406 | typedef struct 407 | { 408 | void *fTokenBuffer; // token buffer for callbacks 409 | void *fFcnList; // function list for callbacks 410 | 411 | UInt32 fOptionFlags; // option flags (see BBLMFcnOptionFlags) 412 | } bblmFcnParams; 413 | 414 | typedef struct 415 | { 416 | UInt32 fScanStart; // where to start scanning (relative to fText) 417 | UInt32 fScanEnd; // where to stop scanning (relative to fText) 418 | 419 | void *fTokenBuffer; // token buffer for callbacks 420 | void *fFcnList; // function list for callbacks 421 | 422 | UInt32 fOptionFlags; // option flags (see BBLMFcnOptionFlags) 423 | } bblmScanSubrangeForFcnParams; 424 | 425 | typedef struct 426 | { 427 | CFDictionaryRef fDictionary; // fully qualified "com.barebones.bblminfo" dictionary 428 | // instance to be returned to the application. 429 | // Your module should either return a copy or retain 430 | // the value that it's about to return, because the 431 | // application will release it when it's done. 432 | } bblmCreateLanguageDictParams; 433 | 434 | typedef struct 435 | { 436 | SInt32 fStartIndex; 437 | SInt32 fEndIndex; 438 | SInt32 fOrigStartIndex; 439 | BBLMRunRec fOrigStartRun; 440 | } 441 | bblmAdjustRangeParams; 442 | 443 | typedef struct 444 | { 445 | SInt32 fStartOffset; 446 | DescType fLanguage; 447 | } bblmCalcRunParams; 448 | 449 | typedef struct 450 | { 451 | SInt32 fStartOffset; // where it should start looking 452 | SInt32 fLanguageStartOffset; // where the embedded language starts 453 | SInt32 fLanguageEndOffset; // where the language ends 454 | DescType fLanguage; // what language we're checking 455 | DescType fParentLanguage; // the parent language whose module we're talking to 456 | } bblmFindLanguageEndParams; 457 | 458 | typedef struct 459 | { 460 | SInt32 fEndOffset; 461 | } 462 | bblmAdjustEndParams; 463 | 464 | typedef struct 465 | { 466 | BBLMCategoryTable fCategoryTable; 467 | } bblmCategoryParams; 468 | 469 | typedef struct 470 | { 471 | SInt8 *fToken; 472 | SInt16 fTokenLength; 473 | Boolean fKeywordMatched; 474 | } bblmKeywordParams; 475 | 476 | typedef struct 477 | { 478 | NSString *fToken; // guaranteed to be non-NIL and non-empty 479 | NSString *fRunKind; // return NIL if there is no match 480 | } bblmWordLookupParams; 481 | 482 | typedef struct 483 | { 484 | UInt8 *fOutputString; 485 | UInt8 fOutputStringSize; 486 | } bblmEscCharParams; 487 | 488 | typedef struct 489 | { 490 | SInt16 fGuessResult; 491 | } bblmGuessLanguageParams; 492 | 493 | typedef const UInt8 *BBLMPatternPtr; 494 | 495 | typedef struct 496 | { 497 | DescType fLanguage; 498 | Boolean fDeleting; 499 | BBLMPatternPtr fPatternString; 500 | } bblmWordLeftRightStringParams; 501 | 502 | typedef struct 503 | { 504 | UInt32 fRunLanguage; 505 | NSString *fRunKind; 506 | UInt32 fRunStart; 507 | UInt32 fRunLength; 508 | Boolean fRunCanBeSpellChecked; 509 | } bblmCanSpellCheckRunParams; 510 | 511 | typedef struct 512 | { 513 | CFURLRef fInDocumentURL; 514 | CFStringRef fInPartialSymbol; 515 | CFRange fInSelectionRange; 516 | CFRange fInProposedCompletionRange; 517 | BBLMRunRec fInCompletionRangeStartRun; 518 | CFRange fOutAdjustedCompletionRange; 519 | } bblmAdjustCompletionRangeParams; 520 | 521 | typedef struct 522 | { 523 | CFURLRef fInDocumentURL; 524 | CFStringRef fInPartialSymbol; 525 | CFRange fInCompletionRange; 526 | BBLMRunRec fInCompletionRangeStartRun; 527 | CFArrayRef fOutSymbolCompletionArray; 528 | SInt32 fOutPreferredCompletionIndex; 529 | UInt32 fOutAdditionalLookupFlags; 530 | } bblmCreateCompletionArrayParams; 531 | 532 | typedef struct 533 | { 534 | BBLMRunRec fInRunInfo; 535 | bool fOutCanCompleteTokensInRun; 536 | } bblmFilterCompletionRunParams; 537 | 538 | typedef struct 539 | { 540 | CFURLRef fInDocumentURL; // may be NULL 541 | CFStringRef fInIncludeFileString; 542 | CFURLRef fOutIncludedItemURL; 543 | } bblmResolveIncludeParams; 544 | 545 | typedef struct 546 | { 547 | UniChar fInTypedCharacter; // the character that the user typed 548 | CFRange fInSelectionRange; // the current selection range 549 | BBLMRunRec fInRunInfo; // if its runKind is not NIL, this is the run in which 550 | // typing is occurring 551 | 552 | UniChar fOutPairingCharacter; // when called, its value is the proposed pairing character; 553 | // change it to zero to suppress auto-pairing, or change its 554 | // value to something else to pair with an alternative character 555 | } bblmAutoPairParams; 556 | 557 | #define kBBLMParamBlockSignature 'R*ch' // parameter block signature 558 | #define kBBLMParamBlockVersion 9 // current parameter block version 559 | 560 | class CTextStorage; 561 | 562 | typedef struct 563 | { 564 | OSType fSignature; // must always be kBBLMParamBlockSignature 565 | UInt32 fVersion; // parameter block version 566 | UInt32 fLength; // must always be >= sizeof(BBLMParamBlock) 567 | 568 | UInt8 fMessage; // input message (see BBLMMessage) 569 | UInt32 fLanguage; // language code 570 | 571 | UniChar *fText; // pointer to text to be scanned 572 | UInt32 fTextLength; // length of text to be scanned 573 | UInt32 fTextGapLocation; // location of "gap" in text (zero if text is contiguous) 574 | UInt32 fTextGapLength; // length of text gap (zero if text is contiguous) 575 | 576 | union 577 | { 578 | bblmFcnParams fFcnParams; 579 | bblmAdjustRangeParams fAdjustRangeParams; 580 | bblmCalcRunParams fCalcRunParams; 581 | bblmAdjustEndParams fAdjustEndParams; 582 | bblmCategoryParams fCategoryParams; 583 | bblmKeywordParams fMatchKeywordParams; 584 | bblmEscCharParams fEscapeCharParams; 585 | bblmGuessLanguageParams fGuessLanguageParams; 586 | bblmWordLeftRightStringParams fWordLeftRightStringParams; 587 | bblmCanSpellCheckRunParams fCanSpellCheckRunParams; 588 | bblmWordLookupParams fWordLookupParams; 589 | bblmScanSubrangeForFcnParams fScanSubrangeForFcnParams; 590 | bblmCreateLanguageDictParams fCreateLanguageDictParams; 591 | bblmFindLanguageEndParams fFindLanguageEndParams; 592 | bblmAdjustCompletionRangeParams fAdjustCompletionRangeParams; 593 | bblmFilterCompletionRunParams fFilterCompletionRunParams; 594 | bblmCreateCompletionArrayParams fCreateCompletionArrayParams; 595 | bblmResolveIncludeParams fResolveIncludeParams; 596 | bblmAutoPairParams fAutoPairParams; 597 | }; 598 | 599 | UInt32 reserved[63]; // reserved for future expansion 600 | CTextStorage *fPrivateTextStorage; // used internally 601 | } BBLMParamBlock; 602 | 603 | typedef struct 604 | { 605 | OSType fSignature; // must always be BBEdit's application signature ("R*ch") 606 | UInt32 fVersion; // reflects current callback version (see kBBLMCurrentCallbackVersion) 607 | 608 | // version 1 callbacks 609 | 610 | // 611 | // these callbacks are used when messages == kBBLMScanForFunctionsMessage, 612 | // and are NIL at all other times. 613 | // 614 | 615 | // 616 | // use these callbacks to make the token buffer and proc list empty. 617 | // BBEdit will do this for you when your plug-in is called; these 618 | // are just for convenience. 619 | // 620 | 621 | OSErr (*fResetTokenBuffer)(void *tokenBuffer); // Available in callback version 1 and later 622 | OSErr (*fResetProcList)(void *procList); // Available in callback version 1 and later 623 | 624 | // 625 | // these callbacks are used in tandem to add a function to the list. When 626 | // you've found a function or other item to be added to the function popup, 627 | // call fAddTokenToBuffer() to add its name to the token buffer. If successful, the 628 | // "offset" parameter will contain a value which should then be passed 629 | // as the second argument to fAddFunctionToList(). If you need to change a 630 | // entry after adding it to the list, call fUpdateFunctionEntry(), using the 631 | // index obtained from a previous fAddFunctionToList() call. 632 | // 633 | 634 | OSErr (*fAddTokenToBuffer)(void *tokenBuffer, // -> token buffer instance passed in fFcnParams 635 | const UniChar *token, // -> points to identifier text (Unicode characters) 636 | const UInt32 length, // -> length of identifier text (in characters) 637 | UInt32 *offset); // <- offset at which token was inserted 638 | // 639 | // Available in callback version 1 and later 640 | 641 | OSErr (*fAddFunctionToList)(void *procList, // -> function list instance passed in fFcnParams 642 | BBLMProcInfo &info, // -> function info record 643 | UInt32 *index); // <- zero-based index of this function's entry 644 | // 645 | // Available in callback version 1 and later 646 | 647 | OSErr (*fGetFunctionEntry)(void *procList, // -> function list instance passed in fFcnParams 648 | UInt32 index, // -> zero-based index of function entry to fetch 649 | BBLMProcInfo &new_info); // <- function info record from list 650 | // 651 | // Available in callback version 1 and later 652 | 653 | OSErr (*fUpdateFunctionEntry)(void *procList, // -> function list instance passed in fFcnParams 654 | UInt32 index, // -> zero-based index of function entry to change 655 | BBLMProcInfo &new_info); // -> function info record containing new information 656 | // 657 | // Available in callback version 1 and later 658 | 659 | // 660 | // these callbacks are used when fMessage is kBBLMAdjustRangeMessage, 661 | // kBBLMCalculateRunsMessage, or kBBLMAdjustEndMessage and are NIL at 662 | // all other times. 663 | // 664 | 665 | SInt32 (*fRunCount)(void); // current number of runs. zero if no runs yet 666 | // defined. negative if run list is undefined 667 | // (usually a result of an allocation failure). 668 | 669 | Boolean (*fGetRun)( SInt32 index, // get a run 670 | DescType& language, // language code 671 | NSString* &kind, // run kind 672 | SInt32& charPos, // character position of run start 673 | SInt32& length); // number of characters in run 674 | 675 | SInt32 (*fFindRun)( SInt32 offset ); // find run containing char offset 676 | // returns -1 if not found 677 | 678 | Boolean (*fAddRun)( // add a new run. returns false if no more runs needed 679 | DescType language, // language code 680 | NSString* kind, // run kind 681 | SInt32 startPos, // character position of run start 682 | SInt32 length, // number of characters in run 683 | bool dontMerge); // when updating a run list, don't return false 684 | // to stop scanning even if this new run 685 | // matches an old one 686 | 687 | void (*fFlushRuns)(void); // flush any runs added with fAddRun that may 688 | // currently be buffered. 689 | 690 | // version 3 callbacks 691 | 692 | // this callback is used when messages == kBBLMScanForFunctionsMessage 693 | 694 | OSErr (*fAddCFStringTokenToBuffer)(void *tokenBuffer, // -> token buffer instance passed in fFcnParams 695 | CFStringRef string, // -> string used for identifier text 696 | UInt32 *offset); // <- offset at which token was inserted 697 | // 698 | // Available in callback version 3 and later 699 | 700 | // version 4 callbacks 701 | 702 | // this callback may be used when messages == kBBLMSetCategoriesMessage 703 | 704 | void (*fSetUnicodeCategoryTable) 705 | (BBLMUnicodeCategoryTable &categoryTable); // -> if called, callee makes internal copy of this 706 | // table and ignores table in fCategoryParams 707 | // 708 | // Available in callback version 4 and later 709 | 710 | 711 | // version 5 callbacks 712 | 713 | // this callback may be used when messages == kBBLMScanForFoldRanges 714 | // or messages == kBBLMScanForFunctionsMessage 715 | 716 | OSErr (*fAddFoldRange)(SInt32 startPos, // character position of first char to be folded 717 | SInt32 length, // number of characters to be folded 718 | BBLMFoldKind foldKind); // type of fold (defaults to kBBLMGenericAutoFold) 719 | 720 | // version 6 callbacks 721 | 722 | // this callback may be used when messages == kBBLMCalculateRunsMessage 723 | OSErr (*fFindEmbeddedLanguageRunsInRange)(const DescType language, 724 | BBLMParamBlock &myParams, 725 | const SInt32 startOffset, 726 | const SInt32 rangeLength, 727 | bool &continueScanning); 728 | 729 | OSErr (*fFindEmbeddedLanguageFunctionsInRange)(const DescType language, 730 | BBLMParamBlock &myParams, 731 | const SInt32 startOffset, 732 | const SInt32 rangeLength); 733 | 734 | } BBLMCallbackBlock; 735 | 736 | #if (! __LP64__) 737 | #pragma pack(pop) 738 | #endif 739 | 740 | #pragma mark - 741 | 742 | #ifdef __cplusplus 743 | 744 | // these inlines should be used in preference to directly accessing the members of the BBLMCallbackBlock 745 | 746 | #pragma mark Function Scanner Callbacks 747 | 748 | inline OSErr bblmResetTokenBuffer(const BBLMCallbackBlock *callbacks, void *tokenBuffer) 749 | { return callbacks->fResetTokenBuffer(tokenBuffer); } 750 | 751 | inline OSErr bblmResetProcList(const BBLMCallbackBlock *callbacks, void *procList) 752 | { return callbacks->fResetProcList(procList); } 753 | 754 | inline OSErr bblmAddTokenToBuffer(const BBLMCallbackBlock *callbacks, 755 | void *tokenBuffer, 756 | const UniChar *token, 757 | const UInt32 length, 758 | UInt32 *offset) 759 | { 760 | return callbacks->fAddTokenToBuffer(tokenBuffer, token, length, offset); 761 | } 762 | 763 | inline OSErr bblmAddCFStringTokenToBuffer(const BBLMCallbackBlock *callbacks, 764 | void *tokenBuffer, 765 | CFStringRef string, 766 | UInt32 *offset) 767 | { 768 | if (kBBLMCurrentCallbackVersion >= 3) 769 | return callbacks->fAddCFStringTokenToBuffer(tokenBuffer, string, offset); 770 | 771 | return paramErr; 772 | } 773 | 774 | inline OSErr bblmAddFunctionToList(const BBLMCallbackBlock *callbacks, 775 | void *procList, 776 | BBLMProcInfo &info, 777 | UInt32 *index) 778 | { 779 | return callbacks->fAddFunctionToList(procList, info, index); 780 | } 781 | 782 | inline OSErr bblmGetFunctionEntry(const BBLMCallbackBlock *callbacks, 783 | void *procList, 784 | UInt32 index, 785 | BBLMProcInfo &info) 786 | { 787 | return callbacks->fGetFunctionEntry(procList, index, info); 788 | } 789 | 790 | inline OSErr bblmUpdateFunctionEntry(const BBLMCallbackBlock *callbacks, 791 | void *procList, 792 | UInt32 index, 793 | BBLMProcInfo &new_info) 794 | { 795 | return callbacks->fUpdateFunctionEntry(procList, index, new_info); 796 | } 797 | 798 | inline // this is one-call alternative to using bblmAddCFStringTokenToBuffer() followed by bblmAddFunctionToList(). 799 | OSStatus bblmAddFunctionToList(const BBLMCallbackBlock *callbacks, 800 | void *tokenBuffer, 801 | void *procList, 802 | CFStringRef name, 803 | BBLMProcInfo &info, 804 | UInt32 *index) 805 | { 806 | OSStatus err = noErr; 807 | 808 | // basic parameter validation 809 | if (nil == callbacks || nil == tokenBuffer || nil == procList || nil == name) { 810 | err = paramErr; 811 | goto EXIT; 812 | } 813 | 814 | err = bblmAddCFStringTokenToBuffer(callbacks, 815 | tokenBuffer, 816 | name, 817 | &info.fNameStart); 818 | if (err != noErr) { 819 | goto EXIT; 820 | } 821 | 822 | err = bblmAddFunctionToList(callbacks, 823 | procList, 824 | info, 825 | index); 826 | if (err != noErr) { 827 | goto EXIT; 828 | } 829 | 830 | EXIT: 831 | return err; 832 | } 833 | 834 | inline // this is just the above, with an NSString parameter so that there's no need for a cast. 835 | OSStatus bblmAddFunctionToList(const BBLMCallbackBlock *callbacks, 836 | void *tokenBuffer, 837 | void *procList, 838 | NSString *name, 839 | BBLMProcInfo &info, 840 | UInt32 *index) 841 | { 842 | return bblmAddFunctionToList(callbacks, 843 | tokenBuffer, 844 | procList, 845 | reinterpret_cast(name), 846 | info, 847 | index); 848 | } 849 | 850 | #pragma mark - 851 | #pragma mark Syntax Coloring Callbacks 852 | 853 | inline SInt32 bblmRunCount(const BBLMCallbackBlock *callbacks) 854 | { 855 | return callbacks->fRunCount(); 856 | } 857 | 858 | inline bool bblmGetRun(const BBLMCallbackBlock *callbacks, 859 | SInt32 index, 860 | DescType& language, 861 | NSString* &kind, 862 | SInt32& charPos, 863 | SInt32& length) 864 | { 865 | return callbacks->fGetRun(index, language, kind, charPos, length); 866 | } 867 | 868 | inline bool bblmGetRun(const BBLMCallbackBlock *callbacks, 869 | SInt32 index, 870 | BBLMRunRec &runInfo) 871 | { 872 | SInt32 runLength = 0; 873 | 874 | if (bblmGetRun(callbacks, index, runInfo.language, runInfo.runKind, runInfo.startPos, runLength)) 875 | { 876 | runInfo.depth = 0; 877 | runInfo.length = runLength; 878 | 879 | return true; 880 | } 881 | 882 | return false; 883 | } 884 | 885 | inline SInt32 bblmFindRun(const BBLMCallbackBlock *callbacks, 886 | SInt32 offset) 887 | { 888 | return callbacks->fFindRun(offset); 889 | } 890 | 891 | inline bool bblmAddRun(const BBLMCallbackBlock *callbacks, 892 | DescType language, 893 | NSString *kind, 894 | SInt32 charPos, 895 | SInt32 length, 896 | bool dontMerge = false) 897 | { 898 | return callbacks->fAddRun(language, kind, charPos, length, dontMerge); 899 | } 900 | 901 | inline void bblmFlushRuns(const BBLMCallbackBlock *callbacks) 902 | { 903 | callbacks->fFlushRuns(); 904 | } 905 | 906 | inline OSErr bblmSetUnicodeCategoryTable(const BBLMCallbackBlock *callbacks, 907 | BBLMUnicodeCategoryTable &categoryTable) 908 | { 909 | OSErr result = noErr; 910 | 911 | if (kBBLMCurrentCallbackVersion >= 4) 912 | callbacks->fSetUnicodeCategoryTable(categoryTable); 913 | else 914 | result = paramErr; 915 | 916 | return result; 917 | } 918 | 919 | inline OSErr bblmAddFoldRange(const BBLMCallbackBlock *callbacks, 920 | SInt32 startPos, 921 | SInt32 length, 922 | BBLMFoldKind foldKind = kBBLMGenericAutoFold) 923 | { 924 | return callbacks->fAddFoldRange(startPos, length, foldKind); 925 | } 926 | 927 | inline OSErr bblmFindEmbeddedLanguageRunsInRange(const BBLMCallbackBlock *callbacks, 928 | const DescType language, 929 | BBLMParamBlock &myParams, 930 | const SInt32 startOffset, 931 | const SInt32 rangeLength, 932 | bool &continueScanning) 933 | { 934 | return callbacks->fFindEmbeddedLanguageRunsInRange(language, 935 | myParams, 936 | startOffset, 937 | rangeLength, 938 | continueScanning); 939 | } 940 | 941 | inline OSErr bblmFindEmbeddedLanguageFunctionsInRange(const BBLMCallbackBlock *callbacks, 942 | const DescType language, 943 | BBLMParamBlock &myParams, 944 | const SInt32 startOffset, 945 | const SInt32 rangeLength) 946 | { 947 | return callbacks->fFindEmbeddedLanguageFunctionsInRange(language, 948 | myParams, 949 | startOffset, 950 | rangeLength); 951 | } 952 | 953 | // 954 | // Use BBLMCharacterIsLineBreak() instead of explicitly testing against \r or \n. 955 | // This will ensure compatibility with past and future versions of BBEdit and 956 | // TextWrangler. 957 | // 958 | 959 | inline 960 | bool BBLMCharacterIsLineBreak(const UniChar ch) __attribute__((const, always_inline)); 961 | 962 | inline 963 | bool BBLMCharacterIsLineBreak(const UniChar ch) 964 | { 965 | return ('\r' == ch) || ('\n' == ch); 966 | } 967 | 968 | // 969 | // Use BBLMCharacterIsBlankOrTab() instead of explicitly testing character values. 970 | // This will ensure compatibility with past and future versions of BBEdit and 971 | // TextWrangler. 972 | // 973 | 974 | inline 975 | bool BBLMCharacterIsBlankOrTab(const UniChar ch) __attribute__((const, always_inline)); 976 | 977 | inline 978 | bool BBLMCharacterIsBlankOrTab(const UniChar ch) 979 | { 980 | return (' ' == ch) || ('\t' == ch); 981 | } 982 | 983 | #else 984 | 985 | #error "Sorry, there is no callback macro support for C." 986 | 987 | #endif 988 | 989 | #endif // BBLMINTERFACE_h 990 | --------------------------------------------------------------------------------