This is Controller.m in view mode; [Download] [Up]
{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;\f1\fmodern Ohlfs;} \paperw13740 \paperh13740 \margl40 \margr40 {\colortbl;\red255\green0\blue0;\red0\green0\blue0;\red255\green109\blue0;\red0\green41\blue0;\red61\green0\blue0;\red0\green255\blue0;} \pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs28\gray300\fc1\cf1 // Controller.m \i0\gray0\fc2\cf2 \ \ \b #import \b0 \gray550\fc3\cf3 "Controller.h" \gray0\fc2\cf2 \ \b #import \b0 \gray550\fc3\cf3 "regex.h" \gray0\fc2\cf2 \ \b #import \b0 <strings.h>\ \b #import \b0 <stdlib.h>\ \ \fs36 @implementation Controller \fs28 \ \ \fs36 - init\ \fs28 \{\ [ \b super \b0 init];\ itemList = [[List alloc] init];\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - free\ \fs28 \{\ [itemList free];\ \b return \b0 [ \b super \b0 free];\ \}\ \ \fs36 - appDidInit:sender\ \fs28 \{\ \b static \b0 NXDefaultsVector RTFSyntaxDefaults = \{\ \{ \gray550\fc3\cf3 "configuration" \gray0\fc2\cf2 , \gray550\fc3\cf3 "" \gray0\fc2\cf2 \},\ \{ \gray93\fc4\cf4 NULL \gray0\fc2\cf2 \}\ \};\ \ \i\gray300\fc1\cf1 // make this object the services delegate \i0\gray0\fc2\cf2 \ [[NXApp appListener] setServicesDelegate: \b self \b0 ];\ \ \i\gray300\fc1\cf1 // setup text objects \i0\gray0\fc2\cf2 \ [regularExpression setMonoFont: \gray93\fc4\cf4 YES \gray0\fc2\cf2 ];\ [matchedSampleText setMonoFont: \gray93\fc4\cf4 YES \gray0\fc2\cf2 ];\ [unmatchedSampleText setMonoFont: \gray93\fc4\cf4 YES \gray0\fc2\cf2 ];\ \ \i\gray300\fc1\cf1 // register defaults \i0\gray0\fc2\cf2 \ NXRegisterDefaults( \gray550\fc3\cf3 "RTFSyntax" \gray0\fc2\cf2 , RTFSyntaxDefaults);\ \ \i\gray300\fc1\cf1 // read defaults \i0\gray0\fc2\cf2 \ [defaultConfiguration setStringValue:NXGetDefaultValue( \gray550\fc3\cf3 "RTFSyntax" \gray0\fc2\cf2 , \gray550\fc3\cf3 "configuration" \gray0\fc2\cf2 )];\ \ \i\gray300\fc1\cf1 // open the default configuration (or a new one if there is no default) \i0\gray0\fc2\cf2 \ \b if \b0 (!fileOpened && ![ \b self \b0 openDefaultConfiguration])\ [ \b self \b0 newConfiguration: \b self \b0 ];\ \ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - (int)app:sender openFile:(const char *)path type:(const char *)type\ \fs28 \{\ \b if \b0 (strcmp(type, \gray550\fc3\cf3 "syntax" \gray0\fc2\cf2 ))\ \b return \b0 \gray93\fc4\cf4 NO \gray0\fc2\cf2 ;\ \ [ \b self \b0 openNamedConfiguration:path];\ [configurationWindow makeKeyAndOrderFront: \b self \b0 ];\ fileOpened = \gray93\fc4\cf4 YES \gray0\fc2\cf2 ;\ \ \b return \b0 \gray93\fc4\cf4 YES \gray0\fc2\cf2 ;\ \}\ \ \fs36 - (BOOL)appAcceptsAnotherFile:(Application *)sender\ \fs28 \{\ \b return \b0 \gray93\fc4\cf4 YES \gray0\fc2\cf2 ;\ \}\ \ \fs36 - previousItem:sender\ \fs28 \{\ \b int \b0 i = [itemNumber intValue];\ \b if \b0 (i <= \gray71\fc5\cf5 1 \gray0\fc2\cf2 )\ \b return \b0 \b self \b0 ;\ [ \b self \b0 saveCurrentItem];\ [ \b self \b0 displayItem:i \gray71\fc5\cf5 -1 \gray0\fc2\cf2 ];\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - nextItem:sender\ \fs28 \{\ \b int \b0 i = [itemNumber intValue];\ \ \b if \b0 (i >= [numberOfItems intValue])\ \b return \b0 \b self \b0 ;\ [ \b self \b0 saveCurrentItem];\ [ \b self \b0 displayItem:i+ \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - deleteItem:sender\ \fs28 \{\ \b int \b0 i = [itemNumber intValue];\ \ \b if \b0 ([numberOfItems intValue] == \gray71\fc5\cf5 1 \gray0\fc2\cf2 )\ \b return \b0 \b self \b0 ; \i\gray300\fc1\cf1 // can't delete last item (need at least one) \i0\gray0\fc2\cf2 \ [[itemList removeObjectAt:i \gray71\fc5\cf5 -1 \gray0\fc2\cf2 ] free];\ [numberOfItems setIntValue:[numberOfItems intValue]- \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ \b if \b0 (i == \gray71\fc5\cf5 1 \gray0\fc2\cf2 )\ [ \b self \b0 displayItem: \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ \b else \b0 \ [ \b self \b0 displayItem:i \gray71\fc5\cf5 -1 \gray0\fc2\cf2 ];\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - insertItem:sender\ \fs28 \{\ \b int \b0 i = [itemNumber intValue];\ \ [ \b self \b0 saveCurrentItem];\ [itemList insertObject:[[SyntaxItem alloc] init] at:i \gray71\fc5\cf5 -1 \gray0\fc2\cf2 ];\ [numberOfItems setIntValue:[numberOfItems intValue]+ \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ [ \b self \b0 displayItem:i];\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - (void)saveCurrentItem\ \fs28 \{\ SyntaxItem *item = [itemList objectAt:[itemNumber intValue]- \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ \ [item setRegularExpression:regularExpression];\ [item setMatchedSampleText:matchedSampleText];\ \}\ \ \fs36 - (void)displayItem:(int)i\ \fs28 \{\ SyntaxItem *item = [itemList objectAt:i \gray71\fc5\cf5 -1 \gray0\fc2\cf2 ];\ \ [itemNumber setIntValue:i];\ \ \b if \b0 ([item getRegularExpression]) \{\ [regularExpression readText:[[item getRegularExpression] stream]];\ [regularExpression setFont:[[item getRegularExpression] font]];\ [regularExpression display];\ \} \b else \b0 \ [regularExpression setText: \gray550\fc3\cf3 "" \gray0\fc2\cf2 ];\ \ \b if \b0 ([item getMatchedSampleText]) \{\ [matchedSampleText readText:[[item getMatchedSampleText] stream]];\ [matchedSampleText setFont:[[item getMatchedSampleText] font]];\ [matchedSampleText setTextColor:[[item getMatchedSampleText] textColor]];\ [matchedSampleText display];\ [matchedColorWell setColor:[matchedSampleText textColor]];\ \} \b else \b0 \ [matchedSampleText setText: \gray550\fc3\cf3 "" \gray0\fc2\cf2 ];\ \}\ \ \fs36 - (BOOL)openDefaultConfiguration\ \fs28 \{\ \b if \b0 (![defaultConfiguration stringValue] || !strlen([defaultConfiguration stringValue])) \{\ \pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc2\cf2 [configurationWindow setTitle:""];\ \pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc2\cf2 \b return \b0 \gray93\fc4\cf4 NO \gray0\fc2\cf2 ;\ \}\ \i\gray300\fc1\cf1 // load default configuration if not already loaded \i0\gray0\fc2\cf2 \ \b if \b0 (strcmp([defaultConfiguration stringValue], [configurationWindow title]))\ [ \b self \b0 openNamedConfiguration:[defaultConfiguration stringValue]];\ \b return \b0 \gray93\fc4\cf4 YES \gray0\fc2\cf2 ;\ \}\ \ \fs36 - openConfiguration:sender\ \fs28 \{\ \b const \b0 \b char \b0 * \b const \b0 types[ \gray71\fc5\cf5 2 \gray0\fc2\cf2 ] = \{ \gray550\fc3\cf3 "syntax" \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 \};\ \b id \b0 openPanel = [OpenPanel new];\ \ [openPanel allowMultipleFiles: \gray93\fc4\cf4 NO \gray0\fc2\cf2 ];\ \b if \b0 (![openPanel runModalForTypes:types])\ \b return \b0 \b self \b0 ;\ [ \b self \b0 openNamedConfiguration:[openPanel filename]];\ [configurationWindow makeKeyAndOrderFront: \b self \b0 ];\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - (void)openNamedConfiguration:(const char *)name\ \fs28 \{\ NXTypedStream *stream;\ Text *text;\ NXColor color;\ \ [configurationWindow setTitle:name];\ \b if \b0 (!(stream = NXOpenTypedStreamForFile(name, \gray93\fc4\cf4 NX_READONLY \gray0\fc2\cf2 ))) \{\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , \gray550\fc3\cf3 "Can't open %s." \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 , name);\ [ \b self \b0 newConfiguration: \b self \b0 ];\ \b return \b0 ;\ \}\ \ text = NXReadObject(stream);\ [unmatchedSampleText readText:[text stream]];\ [unmatchedSampleText setFont:[text font]];\ color = NXReadColor(stream);\ [unmatchedSampleText setTextColor:color];\ [unmatchedSampleText display];\ [unmatchedColorWell setColor:color];\ [text free];\ \ [itemList free];\ itemList = NXReadObject(stream);\ NXCloseTypedStream(stream);\ [numberOfItems setIntValue:[itemList count]];\ \b if \b0 ([itemList count] < \gray71\fc5\cf5 1 \gray0\fc2\cf2 ) \{\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , \gray550\fc3\cf3 "No items in configuration file" \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 );\ \b return \b0 ;\ \}\ [ \b self \b0 displayItem: \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ \b return \b0 ;\ \}\ \ \fs36 - newConfiguration:sender\ \fs28 \{\ [configurationWindow makeKeyAndOrderFront: \b self \b0 ];\ [itemList freeObjects];\ [itemList addObject:[[SyntaxItem alloc] init]];\ [numberOfItems setIntValue: \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ [configurationWindow setTitle: \gray550\fc3\cf3 "Untitled" \gray0\fc2\cf2 ];\ [ \b self \b0 displayItem: \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ [unmatchedSampleText setText: \gray550\fc3\cf3 "unmatched text" \gray0\fc2\cf2 ];\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - saveConfiguration:sender\ \fs28 \{\ NXTypedStream *stream;\ \ [configurationWindow makeKeyAndOrderFront: \b self \b0 ];\ \b if \b0 (!strcmp([configurationWindow title], \gray550\fc3\cf3 "Untitled" \gray0\fc2\cf2 ))\ [ \b self \b0 getConfigurationName];\ \b if \b0 (!(stream = NXOpenTypedStreamForFile([configurationWindow title], \gray93\fc4\cf4 NX_WRITEONLY \gray0\fc2\cf2 ))) \{\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , \gray550\fc3\cf3 "Can't open %s." \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 , [configurationWindow title]);\ \b return \b0 \b self \b0 ;\ \}\ [ \b self \b0 saveCurrentItem];\ NXWriteRootObject(stream, unmatchedSampleText);\ NXWriteColor(stream, [unmatchedSampleText textColor]);\ NXWriteObject(stream, itemList);\ NXCloseTypedStream(stream);\ \ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - revertToSavedConfiguration:sender\ \fs28 \{\ [configurationWindow makeKeyAndOrderFront: \b self \b0 ];\ [ \b self \b0 openNamedConfiguration:[configurationWindow title]];\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - (BOOL)getConfigurationName\ \fs28 \{\ \b id \b0 savePanel = [SavePanel new];\ \ [savePanel setRequiredFileType: \gray550\fc3\cf3 "syntax" \gray0\fc2\cf2 ];\ \b if \b0 (![savePanel runModalForDirectory: \gray93\fc4\cf4 NULL \gray0\fc2\cf2 file: \gray93\fc4\cf4 NULL \gray0\fc2\cf2 ])\ \b return \b0 \gray93\fc4\cf4 NO \gray0\fc2\cf2 ;\ [configurationWindow setTitle:[savePanel filename]];\ \b return \b0 \gray93\fc4\cf4 YES \gray0\fc2\cf2 ;\ \}\ \ \fs36 - saveAsConfiguration:sender\ \fs28 \{\ \b if \b0 ([ \b self \b0 getConfigurationName])\ [ \b self \b0 saveConfiguration: \b self \b0 ];\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - setDefaultConfigurationName:sender\ \fs28 \{\ \b const \b0 \b char \b0 * \b const \b0 types[ \gray71\fc5\cf5 2 \gray0\fc2\cf2 ] = \{ \gray550\fc3\cf3 "syntax" \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 \};\ \b id \b0 openPanel = [OpenPanel new];\ \ [openPanel allowMultipleFiles: \gray93\fc4\cf4 NO \gray0\fc2\cf2 ];\ \b if \b0 (![openPanel runModalForTypes:types])\ \b return \b0 \gray93\fc4\cf4 NO \gray0\fc2\cf2 ;\ [defaultConfiguration setStringValue:[openPanel filename]];\ NXWriteDefault( \gray550\fc3\cf3 "RTFSyntax" \gray0\fc2\cf2 , \gray550\fc3\cf3 "configuration" \gray0\fc2\cf2 , [openPanel filename]);\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - setMatchedColor:sender\ \fs28 \{\ [matchedSampleText setTextColor:[matchedColorWell color]];\ [matchedSampleText display];\ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - setUnmatchedColor:sender;\ \fs28 \{\ [unmatchedSampleText setTextColor:[unmatchedColorWell color]];\ [unmatchedSampleText display];\ \b return \b0 \b self \b0 ;\ \}\ \ \b #define \b0 \gray93\fc4\cf4 MAIL_ADDRESS \gray0\fc2\cf2 \gray550\fc3\cf3 "rgc@cs.umd.edu" \gray0\fc2\cf2 \ \b #define \b0 \gray93\fc4\cf4 SUBJECT \gray0\fc2\cf2 \gray550\fc3\cf3 "Suggestion/Bug for RTFSyntax" \gray0\fc2\cf2 \ \ \fs36 - suggestion:sender\ \fs28 \{\ \b id \b0 s = [NXApp appSpeaker];\ \ NXPortFromName( \gray550\fc3\cf3 "Mail" \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 ); \i\gray300\fc1\cf1 // make sure app is launched \i0\gray0\fc2\cf2 \ [[NXApp appSpeaker] setSendPort:NXPortFromName( \gray550\fc3\cf3 "MailSendDemo" \gray0\fc2\cf2 , \gray93\fc4\cf4 NULL \gray0\fc2\cf2 )];\ [s performRemoteMethod: \gray550\fc3\cf3 "setTo:" \gray0\fc2\cf2 with: \gray93\fc4\cf4 MAIL_ADDRESS \gray0\fc2\cf2 length:strlen( \gray93\fc4\cf4 MAIL_ADDRESS \gray0\fc2\cf2 )+ \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ [s performRemoteMethod: \gray550\fc3\cf3 "setSubject:" \gray0\fc2\cf2 with: \gray93\fc4\cf4 SUBJECT \gray0\fc2\cf2 length:strlen( \gray93\fc4\cf4 SUBJECT \gray0\fc2\cf2 )+ \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ \b return \b0 \b self \b0 ;\ \}\ \ \b #define \b0 \gray93\fc4\cf4 ERRBUF \gray0\fc2\cf2 \gray71\fc5\cf5 100 \gray0\fc2\cf2 \ \ \fs36 - colorSyntax:pasteboard userData:(const char *)userData error:(char **)message\ \fs28 \{\ NXAtom typelist[ \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ \b const \b0 NXAtom *types;\ NXStream *stream;\ Text *text;\ \ \b for \b0 (types = [pasteboard types]; *types; types++)\ \b if \b0 (*types == NXRTFPboardType)\ \b break \b0 ;\ \b if \b0 (*types == \gray71\fc5\cf5 0 \gray0\fc2\cf2 ) \{\ *message = \gray550\fc3\cf3 "Could not find RTF pasteboard type" \gray0\fc2\cf2 ;\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , *message, \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 );\ \b return \b0 \gray93\fc4\cf4 nil \gray0\fc2\cf2 ;\ \}\ \ \pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\fc2\cf2 if \b0 (![ \b self \b0 openDefaultConfiguration]) \{\ *message = \gray551\fc3\cf3 "No default configuration." \gray0\fc2\cf2 ;\ NXRunAlertPanel( \gray551\fc3\cf3 "Error" \gray0\fc2\cf2 , *message, \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 );\ \b return \b0 \gray93\fc4\cf4 nil \gray0\fc2\cf2 ;\ \}\ \pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc2\cf2 \ \i\gray300\fc1\cf1 // read the rich text from the pasteboard stream and save it in an internal text object \i0\gray0\fc2\cf2 \ \b if \b0 (!(stream = [pasteboard readTypeToStream:NXRTFPboardType])) \{\ *message = \gray550\fc3\cf3 "Could not read RTF data" \gray0\fc2\cf2 ;\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , *message, \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 );\ \b return \b0 \gray93\fc4\cf4 nil \gray0\fc2\cf2 ;\ \}\ text = [[Text alloc] init];\ [text setMonoFont: \gray93\fc4\cf4 NO \gray0\fc2\cf2 ];\ NXSeek(stream, 0L, \gray93\fc4\cf4 NX_FROMSTART \gray0\fc2\cf2 );\ [text readRichText:stream];\ NXCloseMemory(stream, \gray93\fc4\cf4 NX_FREEBUFFER \gray0\fc2\cf2 ); \ \ [ \b self \b0 colorRTF:text];\ \ \i\gray300\fc1\cf1 // copy results to the pasteboard \i0\gray0\fc2\cf2 \ typelist[ \gray71\fc5\cf5 0 \gray0\fc2\cf2 ] = NXRTFPboardType;\ [pasteboard declareTypes:typelist num: \gray71\fc5\cf5 1 \gray0\fc2\cf2 owner: \b self \b0 ];\ \b if \b0 (!(stream = NXOpenMemory( \gray93\fc4\cf4 NULL \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray93\fc4\cf4 NX_READWRITE \gray0\fc2\cf2 ))) \{\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , \gray550\fc3\cf3 "Can't open memory file." \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 );\ [text free];\ \b return \b0 \b self \b0 ;\ \}\ [text writeRichText:stream];\ \b if \b0 (![pasteboard writeType:NXRTFPboardType fromStream:stream])\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , \gray550\fc3\cf3 "Can't write results to pasteboard." \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 );\ NXCloseMemory(stream, \gray93\fc4\cf4 NX_FREEBUFFER \gray0\fc2\cf2 );\ [text free];\ \ \b return \b0 \b self \b0 ;\ \}\ \ \fs36 - (void)colorRTF:(Text *)text\ \i\fs28\gray300\fc1\cf1 /*\ Description:\ Color the given text object using the default configuration.\ Uses GNU's regex library (POSIX syntax).\ */ \i0\gray0\fc2\cf2 \ \{\ \b char \b0 errbuf[ \gray93\fc4\cf4 ERRBUF \gray0\fc2\cf2 ];\ \b typedef \b0 \b struct \b0 \{\ regex_t re;\ \b int \b0 begin, end; \i\gray300\fc1\cf1 // offsets \i0\gray0\fc2\cf2 \ \gray93\fc4\cf4 BOOL \gray0\fc2\cf2 compute; \i\gray300\fc1\cf1 // YES => compute match \i0\gray0\fc2\cf2 \ \gray93\fc4\cf4 BOOL \gray0\fc2\cf2 done; \i\gray300\fc1\cf1 // YES => no more matches \i0\gray0\fc2\cf2 \ \} Match;\ Match *match;\ \b char \b0 *s;\ \b int \b0 length;\ \b int \b0 i;\ \b int \b0 skip = \gray71\fc5\cf5 0 \gray0\fc2\cf2 ;\ \b int \b0 errcode;\ \ \i\gray300\fc1\cf1 // first set text to be like the unmatched sample text \i0\gray0\fc2\cf2 \ length = [text textLength];\ [text setSel: \gray71\fc5\cf5 0 \gray0\fc2\cf2 :length];\ [text setSelColor:[unmatchedSampleText textColor]];\ [text setSelFont:[unmatchedSampleText font]];\ [text selectNull];\ \ \i\gray300\fc1\cf1 // allocate the text string and get it \i0\gray0\fc2\cf2 \ \b if \b0 (!(s = malloc(length + \gray71\fc5\cf5 1 \gray0\fc2\cf2 ))) \{\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , \gray550\fc3\cf3 "malloc() failed." \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 );\ \b return \b0 ;\ \}\ \b if \b0 ([text getSubstring:s start: \gray71\fc5\cf5 0 \gray0\fc2\cf2 length:length] <= \gray71\fc5\cf5 0 \gray0\fc2\cf2 ) \{\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , \gray550\fc3\cf3 "Can't get substring." \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 );\ free(s);\ \b return \b0 ;\ \}\ s[length] = \gray71\fc5\cf5 0 \gray0\fc2\cf2 ;\ \ \i\gray300\fc1\cf1 // allocate the match array \i0\gray0\fc2\cf2 \ \b if \b0 (!(match = malloc( \b sizeof \b0 (Match) * [itemList count]))) \{\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , \gray550\fc3\cf3 "malloc() failed." \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 );\ free(s);\ \b return \b0 ;\ \}\ \ \i\gray300\fc1\cf1 // compile all of the regular expressions \i0\gray0\fc2\cf2 \ \b for \b0 (i = \gray71\fc5\cf5 0 \gray0\fc2\cf2 ; i < [itemList count]; i++) \{\ \b id \b0 item = [itemList objectAt:i];\ \b char \b0 *reText = [item getRegularExpressionText];\ \b int \b0 reLength = strlen(reText);\ \b int \b0 j, k;\ \ \i\gray300\fc1\cf1 // remove newlines \i0\gray0\fc2\cf2 \ \b for \b0 (j = \gray71\fc5\cf5 0 \gray0\fc2\cf2 , k = \gray71\fc5\cf5 0 \gray0\fc2\cf2 ; k < reLength; k++)\ \b if \b0 (reText[k] != '\\n')\ reText[j++] = reText[k];\ reText[j] = '\\ \gray71\fc5\cf5 0 \gray0\fc2\cf2 ';\ reLength = j;\ \ \i\gray300\fc1\cf1 // convert "\\n"'s to newlines \i0\gray0\fc2\cf2 \ \b for \b0 (j = \gray71\fc5\cf5 0 \gray0\fc2\cf2 , k = \gray71\fc5\cf5 0 \gray0\fc2\cf2 ; k < reLength; k++)\ \b if \b0 (reText[k] != '\\\\')\ reText[j++] = reText[k];\ \b else \b0 \b if \b0 (k+ \gray71\fc5\cf5 1 \gray0\fc2\cf2 == reLength)\ reText[j++] = '\\\\';\ \b else \b0 \b if \b0 (reText[k+ \gray71\fc5\cf5 1 \gray0\fc2\cf2 ] == 'n') \{\ reText[j++] = '\\n';\ k++;\ \} \b else \b0 \{\ reText[j++] = '\\\\';\ reText[j++] = reText[k+ \gray71\fc5\cf5 1 \gray0\fc2\cf2 ];\ k++;\ \} \ reText[j] = '\\ \gray71\fc5\cf5 0 \gray0\fc2\cf2 ';\ reLength = j;\ \ \i\gray300\fc1\cf1 // make sure regular expression is not empty \i0\gray0\fc2\cf2 \ \b if \b0 (!reLength) \{\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , \gray550\fc3\cf3 "Regular expression %d is empty." \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , i+ \gray71\fc5\cf5 1 \gray0\fc2\cf2 );\ \b goto \b0 cleanup;\ \}\ \ \i\gray300\fc1\cf1 // compile regular expression \i0\gray0\fc2\cf2 \ \b if \b0 ((errcode = regcomp(&match[i].re, reText, \gray93\fc4\cf4 REG_EXTENDED \gray0\fc2\cf2 | \gray93\fc4\cf4 REG_NEWLINE \gray0\fc2\cf2 ))) \{\ regerror(errcode, &match[i].re, errbuf, \gray93\fc4\cf4 ERRBUF \gray0\fc2\cf2 );\ NXRunAlertPanel( \gray550\fc3\cf3 "Error" \gray0\fc2\cf2 , \gray550\fc3\cf3 "regcomp() failed -- %s" \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , \gray71\fc5\cf5 0 \gray0\fc2\cf2 , errbuf);\ \b goto \b0 cleanup;\ \}\ match[i].compute = \gray93\fc4\cf4 YES \gray0\fc2\cf2 ;\ match[i].done = \gray93\fc4\cf4 NO \gray0\fc2\cf2 ;\ \}\ \ \b for \b0 (;;) \{\ \b int \b0 j; \i\gray300\fc1\cf1 // index of match[] to use \i0\gray0\fc2\cf2 \ \ \i\gray300\fc1\cf1 // compute all regular expressions that need it \i0\gray0\fc2\cf2 \ \b for \b0 (i = \gray71\fc5\cf5 0 \gray0\fc2\cf2 ; i < [itemList count]; i++)\ \b if \b0 (!match[i].done && match[i].compute) \{\ regmatch_t rm;\ \b if \b0 (regexec(&match[i].re, s + skip, \gray71\fc5\cf5 1 \gray0\fc2\cf2 , &rm, \gray71\fc5\cf5 0 \gray0\fc2\cf2 ))\ match[i].done = \gray93\fc4\cf4 YES \gray0\fc2\cf2 ;\ \b else \b0 \{\ match[i].begin = rm.rm_so + skip;\ match[i].end = rm.rm_eo + skip;\ match[i].compute = \gray93\fc4\cf4 NO \gray0\fc2\cf2 ;\ \}\ \}\ \ \i\gray300\fc1\cf1 // find the first (earliest) match using all regular expressions \i0\gray0\fc2\cf2 \ \i\gray300\fc1\cf1 // if two RE's find match at the same starting offset, choose the longer match \i0\gray0\fc2\cf2 \ \i\gray300\fc1\cf1 // store the result in j \i0\gray0\fc2\cf2 \ \b for \b0 (i = \gray71\fc5\cf5 0 \gray0\fc2\cf2 , j = \gray71\fc5\cf5 0 \gray0\fc2\cf2 ; i < [itemList count]; i++)\ \b if \b0 (!match[i].done) \{\ j = i;\ \b break \b0 ;\ \}\ \b if \b0 (i == [itemList count]) \i\gray300\fc1\cf1 // no matches \i0\gray0\fc2\cf2 \ \b break \b0 ;\ \b for \b0 (++i; i < [itemList count]; i++)\ \b if \b0 (!match[i].done && (match[j].begin > match[i].begin || \ (match[j].begin == match[i].begin && match[j].end < match[i].end)))\ j = i;\ \ \i\gray300\fc1\cf1 // reset all matches that started before match[j].end \i0\gray0\fc2\cf2 \ \b for \b0 (i = \gray71\fc5\cf5 0 \gray0\fc2\cf2 ; i < [itemList count]; i++)\ \b if \b0 (!match[i].done && match[i].begin < match[j].end)\ match[i].compute = \gray93\fc4\cf4 YES \gray0\fc2\cf2 ;\ \ \i\gray300\fc1\cf1 // color the matching text \i0\gray0\fc2\cf2 \ \i\gray300\fc1\cf1 // note that these 4 lines take up about 70% of the program's CPU time \i0\gray0\fc2\cf2 \ [text setSel:match[j].begin :match[j].end];\ [text setSelColor:[[[itemList objectAt:j] getMatchedSampleText] textColor]];\ [text setSelFont:[[[itemList objectAt:j] getMatchedSampleText] font]];\ [text selectNull];\ \ skip = match[j].end;\ \}\ \ \gray587\fc6\cf6 cleanup: \gray0\fc2\cf2 \ \i\gray300\fc1\cf1 // free the compiled regular expressions and other stuff \i0\gray0\fc2\cf2 \ \b for \b0 (i = \gray71\fc5\cf5 0 \gray0\fc2\cf2 ; i < [itemList count]; i++)\ regfree(&match[i].re);\ free(match);\ \}\ \ \fs36 - showInfo:sender\ \fs28 \{\ \b if \b0 (!infoPanel)\ infoPanel = [NXApp loadNibSection: \gray550\fc3\cf3 "InfoPanel.nib" \gray0\fc2\cf2 owner: \b self \b0 ];\ [infoPanel makeKeyAndOrderFront: \b self \b0 ];\ \b return \b0 \b self \b0 ; \ \}\ \ \b @end \b0 \ }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.