PBWOCategories.m


max21 Unternehmensgruppe
#import "Aprica.h"
#include <math.h>
#import <Foundation/NSDebug.h>
//	Aprica2
//	copyright Pirmin Braun 1997-2007 - pirmin@pirmin.de
//	all Rights reserved;
#ifdef GNUSTEP
#include <stdint.h>
#else
typedef unsigned int uint32_t;
@implementation NSTimeZone (PB3)
+ (NSTimeZone *)systemTimeZone;
{
// weil auf Windows 2003 Server die system-Timezone nicht richtig ermittelt wird: 
    return [NSTimeZone timeZoneWithName:@"Europe/Rome"];
}
@end
#endif
/*
in Shell:
export NSZombieEnabled=YES
@interface _NSZombie : NSObject
{
}
@end
@implementation _NSZombie 
- (id)retain;
{
    LOGS([(NSMutableDictionary *)self description]);
    PRINTCURRENTSTACK;
    [super retain];
    return self;
}
@end
*/
@implementation WOContext (PB3)
- (void)setCurrentComponent:(WOComponent *)c;
{
    if(_currentComponent == c)return;
    [_currentComponent release];
    _currentComponent = [c retain];
}
@end
@implementation WOComponent (PB3)
- (NSMutableDictionary *)_subcomponents;
{
#ifdef GNUSTEP
    return _subComponents;
#else
    return _subcomponents;
#endif
}
@end
@implementation NSObject ( PB3 )
- (int)length;
{
    //wg. FILLED() macro; NSString ueberschreibt diese Methode
    return 1;
}
- (NSString *)classNameNS;
{
    return [[self class]description];
}
#ifdef GNUSTEP
- (BOOL)isGreaterThan:(NSObject *)o
{
    return ([self compare:o]==NSOrderedDescending);
}
#endif
@end
@implementation NSNumber ( PB3 )
- (BOOL)boolValuePB3 { return [self boolValue]; };
@end
@implementation NSArray ( PB3 )
- (NSString *)quotedElements;
{
    LMA;
    int i,j;
    for(i=0,j=[self count];i<j;i++){
        [lma addObject:[NSSWF @"'%@'",[[self oai:i]mysqlEscapedString]]];
    }
    return [lma componentsJoinedByString:@","];
}
- (NSString *)csvString;
{
    return [self csvStringSep:@"," escape:@"\"" alwaysEscape:NO];
}
- (NSString *)csvStringOutlook;
{
    return [self csvStringSep:@"," escape:@"\"" alwaysEscape:YES];
}
- (NSString *)csvStringSep:(NSString *)sep escape:(NSString *)escape alwaysEscape:(BOOL)alwaysEscape;
{
// erzeugt eine Zeile eines csv-Files
// das abgrundtief dumme Outlook versteht sein eigenes csv Format nicht, daher Felder immer escapen
    NSMutableString *ms;
    int i,j;
    NSString *escape2;
    if(!FILLED(sep) || !FILLED(escape))return EON;
    ms = [NSMutableString stringWithCapacity:100];
    [ms setString:EON];
    escape2 = [escape stringByAppendingString:escape];
    for(i=0,j=[self count];i<j;i++){
        NSString *s = [self oai:i];
        if((alwaysEscape && FILLED(s)) || [s rangeOfString:sep].length || [s rangeOfString:@"\n"].length ){
// sep in s muss escaped werden
            if([s rangeOfString:escape].length){
// wenn s escaped wird, muss escape in s durch doppeltes escape escaped werden
                s = [s replace:escape with:escape2];
            }
            s = [NSSWF @"%@%@%@",escape,s,escape];
        }
        if(i)[ms appendString:sep];
        [ms appendString:s];
    }
    return ms;
}
- (NSArray *)sortedArray;
{
    return [self sortedArrayUsingSelector:@selector(compare:)];
}
- (NSArray *)sortedArrayNumeric;
{
    return [self sortedArrayUsingSelector:@selector(compareNumeric:)];
}
- (NSArray *)sortedArrayCaseInsensitive;
{
    return [self sortedArrayUsingSelector:@selector(compareCaseInsensitive:)];
}
+ (NSArray *)soaFrom:(NSString *)s;
{
    return [NSArray soaFrom:s selector:EOCompareAscending];
}
+ (NSArray *)soaCaseInsensitiveFrom:(NSString *)s;
{
    return [NSArray soaFrom:s selector:@selector(compareCaseInsensitive:)];
}
+ (NSArray *)soaDFrom:(NSString *)s;
{
    return [NSArray soaFrom:s selector:EOCompareDescending];
}
+ (NSArray *)soaNFrom:(NSString *)s;
{
    return [NSArray soaFrom:s selector:@selector(compareNumeric:)];
}
+ (NSArray *)soaNDFrom:(NSString *)s;
{
    return [NSArray soaFrom:s selector:@selector(compareNumericD:)];
}
+ (NSArray *)soaFrom:(NSString *)s selector:(SEL)sel;
{
    LMA;
    NSArray *a = [s componentsSeparatedByString:@","];
    int i,j;
    for(i=0,j=[a count];i<j;i++){
        [lma addObject:[PBSortOrdering sortOrderingWithKey:[a oai:i] selector:sel]];
    }
    return lma;
}
+ (NSArray *)soaADFrom:(NSString *)s;
{
//an den key angehaengtes :a bzw. :d steuert ascending/descending; :a ist default;
// an = ascending numeric, dn = descending numeric
    LMA;
    NSArray *a = [s componentsSeparatedByString:@","];
    int i,j;
    if(!FILLED(s))return lma;
    for(i=0,j=[a count];i<j;i++){
        NSArray *a1 = [[a oai:i] componentsSeparatedByString:@":"];
        SEL sel=EOCompareAscending;
        if([a1 count]==2){
            NSString *sm = [[a1 oai:1]lowercaseString];
            if([sm iE:@"d"]){
                sel=EOCompareDescending;
            }else if([sm iE:@"a"]){
                sel=EOCompareAscending;
            }else if([sm iE:@"an"]){
                sel=@selector(compareNumeric:);
            }else if([sm iE:@"n"]){
                sel=@selector(compareNumeric:);
            }else if([sm iE:@"dn"]){
                sel=@selector(compareNumericD:);
            }
        }
        [lma addObject:[PBSortOrdering sortOrderingWithKey:[a1 oai:0] selector:sel]];
    }
    return lma;
}
- (void)objectsTakeValue:value forKey:(NSString *)key;
{
    int i,j = [self count];
    for(i=0;i<j;i++){
        [[self oai:i]takeValue:value forKey:key];
    }
}
- (NSArray *)valuesForKey:(NSString *)s;
{
    int i,j = [self count];
    id o,o1;
    LMA;
    if(!FILLED(s)){
        return lma;
    }
    for(i=0;i<j;i++){
        o = [self oai:i];
        if((o1 = [o vfk:s])){ //muss ein object sein; int oder so ist schlecht
            [lma addObject:o1];
        }
    }
    return lma;
}
- firstObject;
{
    if(![self count])return nil;
    return [self oai:0];
}
- (NSArray *)objectsWithout:(NSArray *)a;
{
    int i,j;
    LMA;
    if(![a count])return self;
    for(i=0,j=[self count];i<j;i++){
        NSObject *o=[self oai:i];
        if([a indexOfObject:o] != NSNotFound)continue;
        [lma addObject:o];
    }
    return lma;
}
- (NSArray *)arrayFromIndex:(int)i;
{
    LMA;
    int j = [self count];
    if(i<0)return nil;
    if(i==0)return self;
    for(;i<j;i++){
        [lma addObject:[self oai:i]];
    }
    return lma;
}
- (NSArray *)arrayToIndex:(int)j;
{
    LMA;
    int i;
    if(j<=0)return nil;
    j = MIN(j,[self count]);
    for(i=0;i<j;i++){
        [lma addObject:[self oai:i]];
    }
    return lma;
}
@end
@implementation NSDictionary ( PB3 )
- (BOOL)containsObject:(id)anObject;
{
// eigentlich contains Key
    if(!anObject)return NO;
    return ([self ofk:anObject]>0);
}
- (NSArray *)valuesArrayForKeys:(NSArray *)a;
{
    LMA;
    int i,j;
    for(i=0,j=[a count];i<j;i++){
        NSString *s = [self valueForKey:[a oai:i]];
        if(!s)s=@"";
        [lma addObject:s];
    }
    return lma;
}
- (NSArray *)objectsForKeys:(NSArray *)keys;
{
    return [self objectsForKeys:keys notFoundMarker:@""];
}
- (NSArray *)allObjects;
{
    return [self allValues];	//language Correction
}
+ (NSDictionary *)lookupDictForArray:(NSArray *)a;	//objekte mit sich selbst als Keys registrieren
{
    if(![a count])return nil;
    return [NSDictionary dictionaryWithObjects:a forKeys:a];
}
+ (NSDictionary *)lookupDictForArray:(NSArray *)a key:(NSString *)key;
{
    NSMutableDictionary *md;
    int i,j=[a count];
    NSObject *o;
    if(!j)return nil;
    if(!key){
        LOGS(@"lookupDictForArray: kein key");
        return nil;
    }
    md = [NSMutableDictionary dictionaryWithCapacity:j];
    for(i=0;i<j;i++){
        o = [a oai:i];
        [md setSecureObject:o forKey:[o vfk:key]];
    }
    return (NSDictionary *)md;
}
@end
@implementation NSMutableString ( PB3 )
- valueForKey:(NSString *)key;
{
    return self;
}
- (void)takeValue:value forKey:(NSString *)key;
{
    if([value isKindOfClass:[NSString class]]){
        [self setString:(NSString *)value];
    }
}
@end
@implementation NSMutableDictionary ( PB3 )
- (NSMutableDictionary *)values;
{
    return self;
}
- (NSArray *)allObjects;
{
    return [self allValues];	//language Correction
}
- (void)setSecureObject:o forKey:(NSString *)key;
{
    if(!o){
//        LOGS(([NSSWF @"trying to set nil object forKey:%@",key]));
//        PRINTCURRENTSTACK;
        if(key)[self removeObjectForKey:key];
        return;
    }
    if(!key){
//        LOGS(([NSSWF @"trying to set object %@ for nil Key",[o description]]));
//        PRINTCURRENTSTACK;
        return;
    }
    [self setObject:o forKey:key];
}
- (void)setObjects:(NSArray *)oa forKeys:(NSArray *)ka;
{
    int i,j=MIN([oa count],[ka count]);
    for(i=0;i<j;i++){
        [self setSecureObject:[oa oai:i] forKey:[ka oai:i]];
    }
}
- (void)removeAllEntriesWithPrefix:(NSString *)prefix;
{
    NSArray *a;
    int i,j;
    if(!prefix)return;
    a = [self allKeys];
    for(i=0,j=[a count];i<j;i++){
        NSString *s = [a oai:i];
        if([s hasSecurePrefix:prefix]){
            [self removeObjectForKey:s];
        }
    }
}
- (void)removeEmpty;
{
    int i,j;
    NSArray *a;
    NSString *k;
    NSString *s;
    a = [self allKeys];
    for(i=0,j=[a count];i<j;i++){
        k = [a oai:i];
        s = [self ofk:k];
        if(!FILLED(s)){
            [self rofk:k];
        }
    }
}
@end
@implementation NSMutableArray ( PB3 )
- (void)secureRemoveLastObject;
{
// Workaround
// original Methode wirft exception, wenn Array leer; das wollen wir nicht.
    if([self count]>0){
        [self removeLastObject];
    }
}
- (void)addObjects:(NSArray *)a afterIndex:(int)i;
{
    int j;
    if(!a)return;
    i++;
    if(i >= [self count] || i < 0){
        [self addObjectsFromArray:a];
        return;
    }
    j = [a count]-1;
    while(j>=0){
        [self insertObject:[a oai:j] atIndex:i];
        j--;
    }
}
- (void)addObjectOverride:anObject;
{
    int i = [self indexOfObject:anObject];
    if(i==NSNotFound){
        [self addObject:anObject];
    }else{
        [self replaceObjectAtIndex:i withObject:anObject];
    }
}
- (void)sort;
{
    [self sortUsingSelector:@selector(compare:)];
}
- (void)sortD;
{
    [self sortUsingSelector:EOCompareDescending];
}
- (void)addObjectUniq:anObject;
{
    if([self indexOfObject:anObject]==NSNotFound){
        [self addObject:anObject];
    }
}
- (void)addObjectsUniq:(NSArray *)a;
{
    int i,j;
    
    for(i=0,j=[a count];i<j;i++){
        [self addObjectUniq:[a oai:i]];
    }
}
@end
@implementation NSString ( PB3_Date )
// Sammlung scriptfreundlicher normalizedDate Methoden
- (NSString *)normalizedDate;	// macht 14 stelliges timestampformat; ausgangsstring darf mysql, dbdate format sein
{
    int l = [self length];
    if(!l)return EON;
    if((l == 8 || l == 14) && [self isNumeric]){
        if(l==8)return [self sbas:@"000000"];
        return self;
    }
    if((l == 10 || l== 19) && [self rangeOfString:@"-"].location == 4){ // vermutlich im mysql-format
        NSString *s = [self onlyDigits];
        if([s intValue]==0)return EON;
        l = [s length];
        if(l==14)return s;
        if(l==8)return [s sbas:@"000000"];
    }
    return EON;
}
- (NSCalendarDate *)nd_NSCD;
{
// liefert das NSCalendarDate zum normalizedDate oder nil
    if(!FILLED(self))return nil;
    return [NSCalendarDate dateWithString:self calendarFormat:ND_FORMAT];
}
- (NSString *)nd_by_adding:(int)amount interval:(NSString *)interval;
{
// interval = {y,m,d,w,wd,H,M,S}
    NSCalendarDate *nscd = [self nd_NSCD];
    if(!nscd)return EON;
    if(amount==0)return self;
    if([interval iE:@"y"])return [[nscd dateByAddingYears:amount months:0 days:0 hours:0 minutes:0 seconds:0]descriptionWithCalendarFormat:ND_FORMAT];
    if([interval iE:@"m"])return [[nscd dateByAddingYears:0 months:amount days:0 hours:0 minutes:0 seconds:0]descriptionWithCalendarFormat:ND_FORMAT];
    if([interval iE:@"d"])return [[nscd dateByAddingYears:0 months:0 days:amount hours:0 minutes:0 seconds:0]descriptionWithCalendarFormat:ND_FORMAT];
    if([interval iE:@"w"])return [[nscd dateByAddingYears:0 months:0 days:amount * 7 hours:0 minutes:0 seconds:0]descriptionWithCalendarFormat:ND_FORMAT]; // sommerzeit bug behoben??
    if([interval iE:@"H"])return [[nscd dateByAddingYears:0 months:0 days:0 hours:amount minutes:0 seconds:0]descriptionWithCalendarFormat:ND_FORMAT];
    if([interval iE:@"M"])return [[nscd dateByAddingYears:0 months:0 days:0 hours:0 minutes:amount seconds:0]descriptionWithCalendarFormat:ND_FORMAT];
    if([interval iE:@"S"])return [[nscd dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:amount]descriptionWithCalendarFormat:ND_FORMAT];
    if([interval iE:@"wd"]){ // workday
        int step;
        NSString *nd = self;
        if(abs(amount)>1000){
            LOGS(TRANSLATION(@"max. 1000 Werktage"));
            return self;
        }
        if(amount <0){
            step=-1;
        }else{
            step=1;
        }
        while(amount!=0){
            int wd;
            nscd = [nscd dateByAddingYears:0 months:0 days:step hours:0 minutes:0 seconds:0];
            nd = [nscd descriptionWithCalendarFormat:ND_FORMAT];
            wd = [nscd dayOfWeek];
            if((wd==0) || (wd==6))continue;
            if([_APP isFeiertag:nd])continue;
            amount-=step;
        }
        return nd;
    }
    LOG(([NSSWF TRANSLATION(@"ungueltiges interval:%@"),interval]));
    return EON;
}
- (NSString *)nd_isWorkday;
{
    if(!FILLED(self))return @"N";
    {
// returned {J,N}
        int wd = [self nd_weekday];
        if((wd == 6) || (wd == 0))return @"N"; // Sa, So
        if([_APP isFeiertag:self])return @"N";
        return @"J";
    }
}
- (int)nd_weekday;
{
// returned die Nr. des Wochentages als String
    if(!FILLED(self))return 0; // naja
    return [[self nd_NSCD] dayOfWeek];
}
- (NSString *)nd_weekdayName;
{
    if(!FILLED(self))return EON;
    return [[_APP weekdayNames] oai:[self nd_weekday]];
}
- (NSString *)nd_fullWeekdayName;
{
    if(!FILLED(self))return EON;
    return [[_APP weekdayFullNames] oai:[self nd_weekday]];
}
- (NSString *)nd_withoutTime;
{
// macht den Zeit-Anteil auf 0 -> Tagesstart
    if(!FILLED(self))return EON;
    return [NSSWF @"%@000000",[self secureSubstringToIndex:8]];
}
- (NSString *)nd_guiDate;
{
    NSCalendarDate *nscd = [self nd_NSCD];
    if(!nscd)return EON;
    if([self hasSecureSuffix:@"000000"]){
        return [nscd descriptionWithCalendarFormat:ND_GUID_FORMAT]; // nur Tag
    }else{
        return [nscd descriptionWithCalendarFormat:ND_GUIDT_FORMAT]; // tag und zeit
    }
}
- (NSString *)nd_mysql;
{
    NSCalendarDate *nscd = [self nd_NSCD];
    if(!nscd)return EON;
    return [nscd descriptionWithCalendarFormat:ND_MYSQL_FORMAT]; // tag und zeit
}
- (NSString *)nd_day;
{
    if(!FILLED(self))return EON;
    return [[self secureSubstringFromIndex:6]secureSubstringToIndex:2];
}
- (NSString *)nd_month;
{
    if(!FILLED(self))return EON;
    return [[self secureSubstringFromIndex:4]secureSubstringToIndex:2];
}
- (NSString *)nd_year;
{
    if(!FILLED(self))return EON;
    return [self secureSubstringToIndex:4];
}
- (NSString *)nd_second;
{
    if(!FILLED(self))return EON;
    return [self secureSubstringFromIndex:12];
}
- (NSString *)nd_minute;
{
    if(!FILLED(self))return EON;
    return [[self secureSubstringFromIndex:10]secureSubstringToIndex:2];
}
- (NSString *)nd_hour;
{
    if(!FILLED(self))return EON;
    return [[self secureSubstringFromIndex:8]secureSubstringToIndex:2];
}
- (NSString *)nd_firstOfMonth;
{
    if(!FILLED(self))return EON;
    return [NSSWF @"%@01%@",[self secureSubstringToIndex:6],[self secureSubstringFromIndex:8]];
}
- (NSString *)nd_startOfWeek;
{
    NSCalendarDate *nscd = [self nd_NSCD];
    int wd;
    if(!nscd)return EON;
    wd = [nscd dayOfWeek];
    if(wd==1)return self;	// ist schon Montag
    if(!wd)wd=7;		//Sonntag am Ende;
    return [[nscd dateByAddingYears:0 months:0 days:- (wd - 1) hours:0 minutes:0 seconds:0]descriptionWithCalendarFormat:ND_FORMAT];
}
- (double)nd_deltaSecondsTo:(NSString *)nd1;
{
    NSCalendarDate *nscd = [self nd_NSCD];
    if(!nscd)return 0;
    {
        NSCalendarDate *nscd1 = [nd1 nd_NSCD];
        if(!nscd1)return 0;
        return [nscd1 timeIntervalSinceDate:nscd];
    }
}
- (NSString *)nd_isFuture;
{
    NSCalendarDate *nscd = [self nd_NSCD];
    if(!nscd)return @"N";
    if([nscd timeIntervalSinceDate:[NSCalendarDate date]]>0)return @"J";
    return @"N";
}
- (NSString *)nd_isHistory;
{
    NSCalendarDate *nscd = [self nd_NSCD];
    if(!nscd)return @"N";
    if([nscd timeIntervalSinceDate:[NSCalendarDate date]]<0)return @"J";
    return @"N";
}
/* nd_ = normalized Date; 14 stelliger oder leerer String haelt alle Variablen als substrings
PBDate ersetzen
on demand weitere Methoden implementieren:
nd_weekOfYear  // 1 <= woche <= 53
nd_yearForWeek // kann ein anderes sein als Jahr des Datums
nd_weekAndYear
nd_deltaWorkDaysTo:
nd_age; // in jahren
nd_workday
nd_withYear: andWeek:
*/
- (NSString *)initWithString:(NSString *)s calendarFormat:(NSString *)cf;
{
//wg. mySQL Bug
    return [self initWithString:s];
}
- (int)date_age_days;
{
// liefert alter in Tagen
    return [[PBDate dbDeltaFrom:self to:[[_APP today]secureSubstringToIndex:8]]doubleValue] / 3600 / 24;
    
}
- (NSString *)dbDateFromMySQLDate;
{
    return [self onlyDigits];
}
- (NSString *)year;
{
    return [[self normalizedDate]nd_year];
}
- (NSString *)month;
{
    return [[self normalizedDate]nd_month];
}
- (NSString *)guiDate;
{
// darf in mysql oder db-Format sein;
    if(!FILLED(self))return EON;
    return [[self normalizedDate]nd_guiDate];
}
+ (NSString *)timeIntervalDescription:(NSTimeInterval)aTimeInterval;
{
    NSString *aTimeIntervalString;
    int timeInterval = (int)aTimeInterval;
    int days;
    int hours;
    int minutes;
    int seconds;
    days = (timeInterval / (60*60*24));
    timeInterval = timeInterval - (days * (60*60*24));
    hours = (timeInterval / (60*60));
    timeInterval = timeInterval - (hours * (60*60));
    minutes = (timeInterval / (60));
    timeInterval = timeInterval - (minutes * (60));
    seconds = timeInterval;
    aTimeIntervalString = [NSString stringWithFormat:@"%d Tage, %d Std, %d Min, %d Sec", days, hours, minutes, seconds];
    return aTimeIntervalString;
}
- (NSString *)stringByAddingDays:(int)i;
{
// muss in dbFormat sein
    PBDate *pbd = [PBDate dateWithDBString:self];
    [pbd addDay:i];
    if([self length]==8){
        return [pbd dateAsDBString];
    }else{
        return [pbd dateAsDBDTString];
    }
}
- (NSString *)doubleFromDate;
{
    return [NSSWF @"%f",[[[self normalizedDate]nd_NSCD] timeIntervalSinceReferenceDate]];
}
- (PBDate *)pbdateWithDBString;
{
// mysqldate oder dbdate format; optional mit Stunden/Minuten/Sec.
    return [PBDate dateWithDBString:self];
}
- (PBDate *)pbdate;
{
// convenience
// mysqldate oder dbdate format; optional mit Stunden/Minuten/Sec.
    return [PBDate dateWithDBString:self];
}
@end
@implementation NSString ( PB3_File )
+ (NSString *)stringWithContentsOfFileUTF8:(NSString *)fn;
{
    NSData *data;
    if (![myFM fileExistsAtPath: fn]) return nil;
    data = [NSData dataWithContentsOfFile:fn];
    if(!fn)return nil;
    return [NSString stringWithData:data];
}
+ (NSString *)stringWithContentsOfFilePE:(NSString *)fn;
{
    NSData *data = [NSData dataWithContentsOfFile:fn];
    NSStringEncoding enc = [[_APP currentSession] preferredEncoding];
    return [[[NSString alloc] initWithData:data encoding:enc] autorelease];
}
- (NSString *)writeToFilePE:(NSString *)path;
{
    BOOL rc = [self WTFPE:path];
    return (rc?@"J": @"N");
}
- (NSString *)writeToFile:(NSString *)path;
{
    BOOL rc = [self WTF:path];
    return (rc?@"J": @"N");
}
- (BOOL)writeToFileUTF8:(NSString *)fn;
{
    NSData *data;
    if(!fn)return NO;
    data = [self dataUsingEncoding:NSUTF8StringEncoding];
    return [data writeToFile:fn atomically:YES];
}
- (BOOL)writeToFileLatin1:(NSString *)fn;
{
    NSData *data;
    if(!fn)return NO;
    data = [self dataUsingEncoding:NSWindowsCP1252StringEncoding];
    return [data writeToFile:fn atomically:YES];
}
- (BOOL)writeToFilePreferredEncoding:(NSString *)fn;
{
    NSData *data;
    NSStringEncoding enc;
    if(!fn)return NO;
    enc = [[_APP currentSession] preferredEncoding];
    if ([self canBeConvertedToEncoding: enc]) {
        data = [self dataUsingEncoding: enc];
    } else {
        LOGS(([NSSWF @"File %@: conversion to encoding %d is lossy!",fn,enc]));
        data = [self dataUsingEncoding: enc
                     allowLossyConversion: YES];
    }
    return [data writeToFile:fn atomically:YES];
}
@end
@implementation NSString ( PB3_GUI )
+ (NSString *)readableValueOf:(int)i;
{
    int j = abs(i);
    if(j > 1000000){
        return [NSSWF @"%i M",i/1000000];
    }
    if(j > 1000){
        return [NSSWF @"%i K",i/1000];
    }
    return NSS(i);
}
- (NSString *)readableValue;
{
    return [NSString readableValueOf:[self intValue]];
}
- (NSString *)blankWhen0;
{
    if([self doubleValue]==0.0)return EON;
    return self;
}
- (NSString *)point;
{
    char s[128];
    char	*p;
    strncpy(s, [self lossyCString], 127); //nur dezimalzahlen normalerweise
    p = s;
    while(*p){
        if(*p == ','){
            *p = '.';
        }
        p++;
    }
    return [NSString stringWithCString:s];
}
- (NSString *)comma;
{
    char s[128];
    char	*p;
    strncpy(s, [self lossyCString], 127); //nur dezimalzahlen normalerweise
    p = s;
    while(*p){
        if(*p == '.'){
            *p = ',';
        }
        p++;
    }
    return [NSString stringWithCString:s];
}
+ (NSString *)dottedGuiFromDouble:(double)d nak:(int)nak;
{
//nur f. money
    NSString *fs,*s;
    NSString *ns;
    int offset;
    int lastlength=(d<0)?4:3;
    NSString *ts = @".";
    fs = [@"%0." stringByAppendingFormat:@"%if",nak];
    s = [NSSWF fs,d];
    s=[s comma];
    offset = nak?nak+4:3;
    if([s length] <= ((d<0)?offset+1:offset))return s; // 123,00 bzw. 123
    ns = [s substringFromIndex:[s length]-offset];
    s = [s substringToIndex:[s length]-offset];
    while([s length] > lastlength){
        ns = [[s substringFromIndex:[s length]-3] stringByAppendingFormat:@"%@%@",ts,ns];
        s = [s substringToIndex:[s length]-3];
    }
    ns = [s stringByAppendingFormat:@"%@%@",ts,ns];
    return ns;
}
+ (NSString *)guiFromDouble:(double)d;
{
    return [[NSString stringWithFormat:@"%0.2f",round2(d)]comma];
}
- (NSString *)guiint;
{
    return NSS((int)round0([self doubleValue]));
}
- (NSString *)guiMoney;
{
    return [NSString dottedGuiFromDouble:[self doubleValue] nak:2];
}
@end
@implementation NSString ( PB3_CSV )
- (NSArray *)csvRecords;
{
    return [self csvRecordsWithSeparator:@"," andEscape:@"\""];
}
- (NSArray *)csvRecordsWithSeparator:(NSString *)sep andEscape:(NSString *)escape;
{
// parsen: character fuer character; liefert ein array von records; ein record ist wiederum ein array von feldern
    LMAN(records);
    NSMutableArray *currentRecord;
    NSMutableString *currentField;
    BOOL separatorOff = NO;
    BOOL escapePending = NO;
    int i,j;
    NSAutoreleasePool *pool = nil;
    currentRecord = [NSMutableArray arrayWithCapacity:30];
    [records addObject:currentRecord];
    currentField = [NSMutableString stringWithCapacity:100];
    [currentField setString:EON];
    [currentRecord addObject:currentField];
    for(i=0,j=[self length];i<j;i++){
        NSString *s;
        {
            if(!(i%10000)){
                [pool release];
                pool = [[NSAutoreleasePool alloc]init];
            }
        }
        s = [self charAt:i];
        if([self characterAtIndex:i] == 13)continue;  // das will man nirgends haben
        if(escapePending && ![s iE:escape]){
            // wenn nicht unmittelbar nach einem escape, das ein escape escapen soll, ein escape kommt, verfaellt das escapende escape; z.B. ...,"",...
            escapePending = NO;
            // gleichzeitig ist die Wirkung eines escape am Feldanfang aufgehoben;
            separatorOff = NO;
        }
        // \n beendet record, wenn nicht innerhalb eines mit escape geschuetzten feldes
        if([s iE:@"\n"]){
            if(separatorOff){
                // \n ist normales Zeichen
                [currentField appendString:s];
            }else{
                // neuer Record, neues Feld
                currentRecord = [NSMutableArray arrayWithCapacity:30];
                [records addObject:currentRecord];
                currentField = [NSMutableString stringWithCapacity:100];
                [currentField setString:@""];
                [currentRecord addObject:currentField];
                separatorOff = NO;
                escapePending = NO;
            }
            continue;
        }
        // escape umschliesst Felder, die separators oder \n enthalten oder immer
        if([s iE:escape]){
            if(escapePending){
                // f. doppelte escape, die ein escape escapen
                [currentField appendString:s];
                escapePending=NO;
                continue;
            }
            if(!FILLED(currentField) && !separatorOff){
                // am Feldanfang wird ein escape verbraucht, um im Feld separators auszuschalten
                separatorOff = YES;
                continue;
            }
            escapePending = YES;
            continue;
        }
        if([s iE:sep]){
            if(separatorOff){
                // Separator ist normales Zeichen
                [currentField appendString:s];
            }else{
                // neues Feld
                currentField = [NSMutableString stringWithCapacity:100];
                [currentField setString:EON];
                [currentRecord addObject:currentField];
                escapePending = NO;
                separatorOff = NO;
            }
            continue;
        }
        [currentField appendString:s];
        continue;
    }
    [pool release];
    return records;
}
- (NSArray *)csvFields;
{
    return [self csvFieldsWithSeparator:@"," andEscape:@"\""];
}
- (NSArray *)csvFieldsWithSeparator:(NSString *)sep andEscape:(NSString *)escape;
{
// parsen: character fuer character; liefert ein array von feldern
    LMA;
    NSMutableString *currentField;
    BOOL separatorOff = NO;
    BOOL escapePending = NO;
    int i,j;
    currentField = [NSMutableString stringWithCapacity:100];
    [currentField setString:EON];
    [lma addObject:currentField];
    for(i=0,j=[self length];i<j;i++){
        NSString *s = [self charAt:i];
        if([s iE:escape]){
            if(escapePending){
                [currentField appendString:s];
                escapePending=NO;
                continue;
            }
            if(!FILLED(currentField)){
                separatorOff = YES;
                continue;
            }
            escapePending = YES;
            continue;
        }
        if([s iE:sep]){
            if(separatorOff){
                if(escapePending){
                    currentField = [NSMutableString stringWithCapacity:100];
                    [currentField setString:EON];
                    [lma addObject:currentField];
                    escapePending = NO;
                    separatorOff = NO;
                    continue;
                }else{
                    [currentField appendString:s];
                    continue;
                }
            }else{
                currentField = [NSMutableString stringWithCapacity:100];
                [currentField setString:EON];
                [lma addObject:currentField];
                escapePending = NO;
                separatorOff = NO;
                continue;
            }
        }
        [currentField appendString:s];
        continue;
    }
    return lma;
}
@end
@implementation NSString ( PB3_Base )
+ (NSString *)stringWithCP1252Char:(unsigned int)i;
{
    unsigned char c = i;
    return [[[NSString alloc]initWithData:[NSData dataWithBytes:&c length:1] encoding:NSWindowsCP1252StringEncoding]autorelease];
}
+ (NSString *)stringWithUnichar:(unichar)uni;
{
    return [NSString stringWithCharacters:&uni length:1];
}
- (NSString *)stringWithoutString:(NSString *)s;
{
    return [[self componentsSeparatedByString:s]componentsJoinedByString:EON];
}
- (NSString *)text;
{
    return self;
}
+ (NSString *)stringWithFormat:(NSString *)fs andParmStrings:(NSArray *)a;
{
//darf %...i u. %@ platzhalter f. int u. strings enthalten
    NSMutableString *ms,*fsm;
    NSString *c;
    int i,j,i1=0,j1=[a count];
    BOOL proz=NO;
    if(!j1)return fs;
    ms = [NSMutableString stringWithCapacity:100];
    fsm = [NSMutableString stringWithCapacity:100];
    [ms setString:EON];
    [fsm setString:@"%"];
    for(i=0,j=[fs length];i<j;i++){
        c = [fs charAt:i];
        if([c iE:@"%"]){
            if(!proz){
                proz=YES;	//in erwartung eines parameters
            }else{
                [ms appendString:c]; //%% -> escaped %
                proz=NO;
                [fsm setString:@"%"];
            }
            continue;
        }
        if(proz){
            if([c iE:@"@"]){
                if(i1<j1){
                    [ms appendString:[a oai:i1++]];
                }else{
                    [ms appendString:@"%@"];
                }
                proz=NO;	//parameter fertig
                [fsm setString:@"%"];
                continue;
            }
            if([c iE:@"0"] || [c intValue]>0){
                [fsm appendString:c];
                continue;
            }
            if([c iE:@"i"]){
                [fsm appendString:c];
                if(i1<j1){
                    [ms appendFormat:fsm,[[a oai:i1++]intValue]];
                }else{
                    [ms appendString:fsm];
                }
                proz=NO;
                [fsm setString:@"%"];
                continue;
            }
            [fsm setString:@"%"];
            [ms appendString:@"%"]; //alles andere hebt proz auf;
            proz=NO;
            continue;
        }
        [ms appendString:c];
    }
    return [[ms copy]autorelease]; // muss ein immutable String sein
}
+ (NSString *)stringWith:(int)i timesString:(NSString *)s;
{
    if(!s)return @"";
    {
        LMA;
        while(i-->0){
            [lma addObject:s];
        }
        return [lma componentsJoinedByString:@""];
    }
}
+ (NSString *)stringWithData:(NSData *)data;
{
    NSString *s = nil;
    const unsigned char *p,*pend;
    unsigned int len;
    if(!data)return nil;
    len = [data length];
    if(!len)return EON;
    p = [data bytes];
    if(len>=2){
        unsigned short i = *p;
        i <<= 8;
        i+=*(p+1);
        if(i == 0xFEFF || i == 0xFFFE){
            s = [[NSString alloc]initWithData:data encoding:NSUnicodeStringEncoding];
            return [s autorelease];
        }
    }
    if(len>=3){
//utf8 maker as used by some editors
        if(*p == 0xEF && *(p+1)==0xBB && *(p+2)==0xBF){
            data = [NSData dataWithBytes:(p+3) length: len-3];
            s = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            return [s autorelease];
        }
    }
// mit //utf8 erzwingen, dass file als utf8 interpretiert wird
    s = [[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]autorelease];
    if([s hasSecurePrefix:@"//utf8"]||[s hasSecurePrefix:@"#utf8"]){
//        LOGS(([NSSWF @"%@: forcing utf8",fn]));
        return s;
    }
// ein utf8 sequenz starter zeichen ist >= 192, es wird gefolgt von mindestens einem zeichen >= 128 und < 192
// wenn ein zeichen >= 192 von einem Zeichen gefolgt wird, das < 128 oder > 192 ist, ist es kein utf8
// wenn ein zeichen < 128 von einem zeichen gefolgt wird, das zwischen 128 und 192 ist, ist es kein utf8
    pend = p + len - 1;
    while(p < pend){
        unsigned char i = *p, ip1 = *(p + 1);
        if(i >= 192 && (ip1 < 128 || ip1 >= 192)){
            s = [[NSString alloc]initWithData:data encoding:NSWindowsCP1252StringEncoding];
            return [s autorelease];
        }
        if(i < 128 && (ip1 >= 128 && ip1 < 192)){
            s = [[NSString alloc]initWithData:data encoding:NSWindowsCP1252StringEncoding];
            return [s autorelease];
        }
        p++;
    }
// es koennte technisch latin1 oder utf8 sein.
// jedoch wenn es die ueblichen latin1 Sonderzeichen enthalten wuerde
// (ae,oe,ue,ss) haetten wir sie oben schon erkannt.
// vermutlich ist es daher utf8 oder ascii
// welches man auch als utf8 interpretieren darf.
    s = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    return [s autorelease];
}
- (NSString *)stringValue;
{
    return self;
}
- (NSString *)replace:(NSString *)a with:(NSString *)b;
{
    NSArray *a1 = [self componentsSeparatedByString:a];
    return [a1 componentsJoinedByString:b];
}
- (NSString *)iso8859DecodedString;
{
    LMA;
    NSArray *a1 = [self componentsSeparatedByString:@"?="];
    NSString *s1 = [a1 firstObject];
// =E4abc=F6=FC=C4=D6=DC=DF
// FotoMail f=FCr Dich
    int i1,j1;
    for(i1=0,j1=[s1 length];i1<j1;i1++){
        NSString *ch = [s1 charAt:i1];
        if(![ch iE:@"="]){
            [lma addObject:ch];
        }else{
            unichar uni;
            uni = [[[s1 secureSubstringFromIndex:i1 + 1]secureSubstringToIndex:2]hexValue];
            if(uni){
                NSString *s4 = [NSString stringWithCharacters:&uni length:1];
                if(s4)[lma addObject:s4];
            }
            i1+=2;
        }
    }
// alles was nach dem ?= kam
    for(i1=1,j1=[a1 count];i1<j1;i1++){
        s1 = [a1 oai:i1];
        if(FILLED(s1))[lma addObject:s1];
    }
    return [lma componentsJoinedByString:@""];
}
- (NSString *)utf8DecodedString;
{
    NSMutableData *mdata = [NSMutableData dataWithCapacity:100];
// f=C3=BCr
    int i1,j1;
    for(i1=0,j1=[self length];i1<j1;i1++){
        NSString *ch = [self charAt:i1];
        if(![ch iE:@"="]){
            [mdata appendData:[ch dataUsingEncoding:NSUTF8StringEncoding]];
        }else{
            unsigned char c;
            c = (unsigned char)[[[self secureSubstringFromIndex:i1 + 1]secureSubstringToIndex:2]hexValue];
            [mdata appendBytes:&c length:1];
            i1+=2;
        }
    }
    return [[[NSString alloc]initWithData:mdata encoding:NSUTF8StringEncoding]autorelease];
}
- (NSData *)dataUsingCodePage858;
{
    NSMapTable *map = [_APP codePage858Map];
    unsigned i,length = [self length];
    NSMutableData *data = [NSMutableData dataWithLength:length];
    char *cp = [data mutableBytes];
    const unichar *up = [[self dataUsingEncoding: NSUnicodeStringEncoding] bytes];
    up++; //skip BOM
    for (i=0;i<length;i++) {
        *cp = (char)(int)NSMapGet(map,(void *)(int)*up);
        if (!*cp) *cp = (char)*up;
        cp++;
        up++;
    }
    return data;
}
- (BOOL)containsObject:(id)anObject;
{
    return [self containsString:anObject];
}
- (BOOL)containsString:(NSString *)s options:(unsigned int)mask;
{
  return [self rangeOfString: s options: mask].length ? YES : NO;
}
- (BOOL)containsString:(NSString *)s;
{
  return [self containsString:s options: 0];
}
- (BOOL)containsCharactersFromSet:(NSCharacterSet *)s;
{
  return [self rangeOfCharacterFromSet:s].length ? YES : NO;
}
- (char)charValue;
{
// f. Kompatiblitaet bei takeValue: forKey:
    return [self intValue];
}
- (NSString *)charAt:(int)i;
{
    if(i>=[self length])return nil;
    if(i<0)return nil;
    return [[self substringFromIndex:i]substringToIndex:1];
}
- (BOOL)boolValue;
{
    if ([self caseInsensitiveCompare: @"J"] == NSOrderedSame)
      {
        return YES;
      }
    if ([self caseInsensitiveCompare: @"YES"] == NSOrderedSame)
      {
        return YES;
      }
    if ([self caseInsensitiveCompare: @"true"] == NSOrderedSame)
      {
        return YES;
      }
    return [self intValue] != 0 ? YES : NO;
}
- (BOOL)boolValuePB3;
{
    return [self boolValue];
}
- (NSString *)string;
{
//wg. kompatibilitaet zu PBStringWrapper
    return self;
}
@end
@implementation NSString ( PB3_Escaping )
- (NSString *)sqlWildCardEscapedString;
{
    BOOL isEscaped = NO;
    int i,j;
    NSMutableString *ms = [NSMutableString stringWithCapacity:20];
    unichar c=0;
    [ms setString:EON];
    for(i=0,j=[self length];i<j;i++){
        c = [self characterAtIndex:i];
        if(!isEscaped){
            switch(c){
                case '%':
                    [ms appendString:@"\\%"];
                    continue;
                case '_':
                    [ms appendString:@"\\_"];
                    continue;
                case '\\':
                    isEscaped = YES;
                    continue;
             }
        }else{
            isEscaped = NO;
        }
        [ms appendString:[NSString stringWithCharacters:&c length:1]];
    }
    return (NSString *)ms;
}
- (NSString *)mysqlEscapedString;
{
// fuer Feldinhalte und literals
    NSMutableString *ms = [NSMutableString stringWithString:self];
    int totalLen = [self length];
    int index=0,len=0;
    unichar    curr;
    while(index < totalLen)
    {
        curr = [self characterAtIndex:index];
        switch(curr)
        {
            case '\n':
            case '\'':
            case '\\':
            case '"':
                [ms insertString:@"\\" atIndex:len];
                len++;
        }
        len++;
        index++;
    }
    return (NSString *)ms;
}
- (NSString *)xmlEscapedString;
{
    NSMutableString *ms = [NSMutableString stringWithCapacity:[self length]+3];
    int i,j;
    [ms setString:EON];
    for(i=0,j=[self length];i<j;i++){
        NSString *s = [self charAt:i];
        if([s iE:@"<"]){
            [ms appendString:@"&lt;"];
            continue;
        }
        if([s iE:@">"]){
            [ms appendString:@"&gt;"];
            continue;
        }
        if([s iE:@"&"]){
            [ms appendString:@"&amp;"];
            continue;
        }
        if([s iE:@"\""]){
            [ms appendString:@"&quote;"];
            continue;
        }
        if([s iE:@"'"]){
            [ms appendString:@"&apos;"];
            continue;
        }
        [ms appendString:s];
    }
    return ms;
}
- (NSString *)stringBySubstitutingXMLEscapes;
{
    NSString *s = [self replace:@"&lt;" with:@"<"];
    s = [s replace:@"&gt;" with:@">"];
    s = [s replace:@"&amp;" with:@"&"];
    return s;
}
- (NSString *)stringByEscapingCRTAB;
{
// fuer ASCII-Archivierung von EOs
  static NSCharacterSet *set=nil;
  if (set==nil) set = [[NSCharacterSet characterSetWithCharactersInString:@"\\\n\t"] retain];
  if ([self rangeOfCharacterFromSet: set].length)
    {
      NSString *ns;
      unichar *rd, *wr, *st;
      NSZone *zone;
      unsigned len, i;
      len = [self length];
      zone = NSDefaultMallocZone();
      rd = NSZoneMalloc(zone,sizeof(unichar)*len);
      wr = NSZoneMalloc(zone,sizeof(unichar)*len*2);
      st = wr;
      [self getCharacters: rd];
      for (i=0; i<len; i++, rd++) {
// the unichar (int16) values of the characters we are looking for
// happen to correspond to the char (int8) values. (\\ \n and \t)
// which makes this code safe.  Otherwise we would need to use the unichar
// 0x0000 values in the case statements.
          switch (*rd) {
              case '\\': *wr++='\\'; *wr++='\\'; break;
              case '\n': *wr++='\\'; *wr++='n';  break;
              case '\t': *wr++='\\'; *wr++='t';  break;
              default: *wr++=*rd; break;
          }
      }
      ns = [NSString stringWithCharacters:st length:wr-st];
      NSZoneFree(zone,rd-len);
      NSZoneFree(zone,st);
      return ns;
    }
  return self;
}
- (NSString *)stringByUnescapingCRTAB;
{
  static NSCharacterSet *set=nil;
  if (set==nil) set = [[NSCharacterSet characterSetWithCharactersInString:@"\\"] retain];
  if ([self rangeOfCharacterFromSet: set].length) {
      NSString *ns;
      unichar *rd, *wr, *st;
      NSZone *zone;
      unsigned len, i;
      len = [self length];
      zone = NSDefaultMallocZone();
      rd = NSZoneMalloc(zone,sizeof(unichar)*len);
      wr = NSZoneMalloc(zone,sizeof(unichar)*len);
      st = wr;
      [self getCharacters: rd];
      for (i=0; i<len; i++, rd++) {
// the unichar (int16) values of the characters we are looking for
// happen to correspond to the char (int8) values. (\\ \n and \t)
// which makes this code safe.  Otherwise we would need to use the unichar
// 0x0000 values in the case statements.
          if (*rd == '\\') {
              rd++; i++;
              switch (*rd) {
                  case '\\': *wr++='\\'; break;
                  case  'n': *wr++='\n'; break;
                  case  't': *wr++='\t'; break;
                  default:   *wr++=*rd;  break;
              }
          } else {
              *wr++=*rd;
          }
      }
      ns = [NSString stringWithCharacters:st length:wr-st];
      NSZoneFree(zone,rd-len);
      NSZoneFree(zone,st);
      return ns;
    }
  return self;
}
- (NSString *)latexEscapedString;
{
    NSMutableString *ms = [NSMutableString stringWithString:@""];
    int i,j = [self length];
    unichar    curr;
    for(i=0;i<j;i++){
        curr = [self characterAtIndex:i];
        switch(curr){
            case '$':
                [ms appendString:@"\\$"];
                continue;
            case '&':
                [ms appendString:@"\\&"];
                continue;
            case '%':
                [ms appendString:@"\\%"];
                continue;
            case '#':
                [ms appendString:@"\\#"];
                continue;
            case '_':
                [ms appendString:@"\\_"];
                continue;
            case '{':
                [ms appendString:@"\\{"];
                continue;
            case '}':
                [ms appendString:@"\\}"];
                continue;
            case '~':
                [ms appendString:@"\\~{}"];
                continue;
            case '^':
                [ms appendString:@"\\^{}"];
                continue;
            case '"':
                [ms appendString:@"\\dq{}"];
                continue;
            case '\\':
                [ms appendString:@"\\textbackslash "];
                continue;
            case '|':
                [ms appendString:@"\\textbar "];
                continue;
            case '<':
                [ms appendString:@"\\textless "];
                continue;
            case '>':
                [ms appendString:@"\\textgreater "];
                continue;
        }
        [ms appendString:[NSString stringWithCharacters:&curr length:1]];
    }
    return (NSString *)ms;
}
- (NSString *)htmlEscapedString;
{
    NSMutableString *ms = [NSMutableString stringWithString:@""];
    int i,j = [self length];
    unichar    curr;
    for(i=0;i<j;i++){
        curr = [self characterAtIndex:i];
        switch(curr){
            case '&':
                [ms appendString:@"&amp;"];
                continue;
            case '"':
                [ms appendString:@"&quot;"];
                continue;
            case '<':
                [ms appendString:@"&lt;"];
                continue;
            case '>':
                [ms appendString:@"&gt;"];
                continue;
        }
        [ms appendString:[NSString stringWithCharacters:&curr length:1]];
    }
    return (NSString *)ms;
// tut nicht; " wird zu ungueltigen zeichen... return [WOResponse stringByEscapingHTMLString:self];
}
- (NSString *)pdfEscapedString;
{
    int j = [self length];
    int i=0;
    NSMutableString *ms = [NSMutableString stringWithCapacity:j];
    [ms setString:@"("];
    for(i=0,j=[self length];i<j;i++){
        unichar ch = [self characterAtIndex:i];
        if( (ch == '\\') || (ch == '(') || (ch == ')') ){
            [ms appendString:@"\\"];
            [ms appendString:[NSString stringWithCharacters:&ch length:1]];
        } else if(ch == '\r') {
            [ms appendString:@"\\"];
            [ms appendString:@"r"];
        }else{
            [ms appendString:[NSString stringWithCharacters:&ch length:1]];
        }
    }
    [ms appendString:@")"];
    return ms;
}
@end
@implementation NSString ( PB3_Substring )
- (NSString *)withoutPrefix:(NSString *)pref;
{
    if(!pref)return self;
    if([self hasPrefix:pref]){
        return [self substringFromIndex:[pref length]];
    }
    return self;
}
- (NSString *)stringWithoutLeadingWhiteSpace;
{
    int i,j;
    unichar c;
    for(i=0,j=[self length];i<j;i++){
        c = [self characterAtIndex:i];
        if(c != ' ' && c != '\t')return (i?[self substringFromIndex:i]:self);
    }
    return EON;
}
- (NSString *)stringWithoutLeadingWhiteSpaceCRLF;
{
    int i,j;
    unichar c;
    for(i=0,j=[self length];i<j;i++){
        c = [self characterAtIndex:i];
        if(c != ' ' && c != '\t' && c != '\n' && c != '\r')return (i?[self substringFromIndex:i]:self);
    }
    return EON;
}
- (NSString *)stringWithoutTrailingWhiteSpace;
{
    int j,i=[self length];
    unichar c;
    for(j=i-1;j>=0;j--){
        c = [self characterAtIndex:j];
        if(c != ' ' && c != '\t' && c != '\r')return ((j<(i-1))?[self substringToIndex:j+1]:self);
    }
    return EON;
}
- (NSString *)stringWithoutTrailingWSC;
{
// trailing White Space und Comment weg;
    NSRange r = [self rangeOfString:@"//"];
    NSString *s = self;
    if(r.length)s = [s substringToIndex:r.location];
    return [s stringWithoutTrailingWhiteSpace];
}
- (NSString *)stringWithoutSuffix:(NSString *)s;
{
    int l = [s length];
    if([self length] < l || l==0)return self;
    return [self substringToIndex:[self length] - l];
}
- (NSString *)stringWithoutLastChar;
{
    return [self secureSubstringToIndex:[self length]-1];
}
- (NSString *)stringByDeletingLastKeypath;
{
    LMA;
    [lma addObjectsFromArray:[self componentsSeparatedByString:@"."]];
    [lma secureRemoveLastObject];
    return [lma componentsJoinedByString:@"."];
}
- (BOOL)hasPrefixFrom:(NSArray *)a;
{
    int i,j;
    if(!a)return NO;
    if(!(j=[a count]))return NO;
    for(i=0;i<j;i++){
        if([self hasPrefix:[a oai:i]])return YES;
    }
    return NO;
}
- (BOOL)hasSecurePrefix:(NSString *)s;
{
    if(s && [self length] >= [s length])return [self hasPrefix:s];
    return NO;
}
- (BOOL)hasSecureSuffix:(NSString *)s;
{
    if(s && [self length] >= [s length])return [self hasSuffix:s];
    return NO;
}
- (NSString *)secureSubstringFromIndex:(int)i;
{
    if(!i)return self;
    if(i>=[self length] || i<0)return EON;
    return [self substringFromIndex:i];
}
- (NSString *)abbreviatedString;
{
    return [self abbrevToLength:15]; //in wod zu verwenden, damit auswahllisten nicht zu breit werden
}
- (NSString *)abbreviated30String;
{
    return [self abbrevToLength:30]; //in wod zu verwenden, damit strings nicht zu lang werden
}
- (NSString *)abbreviated40String;
{
    return [self abbrevToLength:40]; //in wod zu verwenden, damit strings nicht zu lang werden
}
- (NSString *)abbreviated60String;
{
    return [self abbrevToLength:60]; //in wod zu verwenden, damit strings nicht zu lang werden
}
- (NSString *)abbreviated80String;
{
    return [self abbrevToLength:80]; //in wod zu verwenden, damit strings nicht zu lang werden
}
- (NSString *)abbrevToLength:(int)i;
{
    if(i<1)return self;
    if([self length]<=i)return self;
    return [[self substringToIndex:i]stringByAppendingString:@"..."];
}
- (NSString *)lastChar;
{
    int i = [self length];
    if(!i)return EON;
    return [self substringFromIndex:i-1];
}
- (NSString *)secure0LPaddedSubstringToIndex:(int)i;
{
    return [self secureSubstringToIndex:i pad:PAD_LEFT padString:@"0"];
}
- (NSString *)secure0RPaddedSubstringToIndex:(int)i;
{
    return [self secureSubstringToIndex:i pad:PAD_RIGHT padString:@"0"];
}
- (NSString *)secureBlankLPaddedSubstringToIndex:(int)i;
{
    return [self secureSubstringToIndex:i pad:PAD_LEFT padString:@" "];
}
- (NSString *)secureBlankRPaddedSubstringToIndex:(int)i;
{
    return [self secureSubstringToIndex:i pad:PAD_RIGHT padString:@" "];
}
- (NSString *)secureSubstringToIndex:(int)i;
{
    int l = [self length];
    if(!i)return @"";
    if(l<=i)return self;
    return [self substringToIndex:i];
}
- (NSString *)secureSubstringToIndex:(int)i pad:(int)pad padString:(NSString *)padString;
{
    int l = [self length];
    if(l >= i)return [self substringToIndex:i];
    if(pad != PAD_NONE){
        NSMutableString *ms = [NSMutableString stringWithCapacity:20];
        int i1;
        if(!padString || [padString length]!=1)padString=@"0";
        if(pad == PAD_RIGHT){
            [ms setString:self];
            for(i1=l;i1<i;i1++){
                [ms appendString:padString];
            }
            return ms;
        }
        if(pad == PAD_LEFT){
            [ms setString:padString];
            for(i1=1;i1<i-l;i1++){
                [ms appendString:padString];
            }
            [ms appendString:self];
            return ms;
        }
    }
    return self;
}
@end
@implementation NSString ( PB3_Transform )
- (int)nvePruefziffer;
{
    /* Die Prüfziffer errechnet sich nach folgendem Verfahren: Zunächst werden die Nutzziffern mit den Gewichten 3 (ungerade Ziffern) und 1 (gerade Ziffern) multipliziert und die Ergebnisse addiert. Ist die letzte Stelle dieser Summe eine 0, so ist auch die Prüfziffer 0, sonst die Differenz aus 10 und der letzten Stelle.
        */
    int i = [self length] - 1;
    int sum = 0;
    int gewichtung = 3;
    int pz;
    while(i>=0){
        sum += [[self charAt:i]intValue] * gewichtung;
        if(gewichtung == 3){
            gewichtung = 1;
        }else{
            gewichtung = 3;
        }
        i--;
    }
    pz = sum % 10;
    if(pz)pz = 10 - pz;
    return pz;
}
- (NSString *)ean13String;
{
    if([self length]!=12){
        LOG(@"kein geeigneter ean13 Code");
        return @"";
    }
    if(![[self onlyDigits]iE:self]){
        LOG(@"kein geeigneter ean13 Code, da nicht ausschliesslich numerisch");
        return @"";
    }
    
// input: 12 stellig numerisch; 2 stellen Land, 5 stellen Firma, 5 stellen Produkt
// ouput: aufbereitet f. ean13.ttf: 1 Stelle Start, 6 Stellen odd/even, Trenner, 5 Stellen, Pruefziffer, Ende-Kennug
    {
        unsigned char erg[16]; // mit 0 terminieren dann
        const char *s = [self lossyCString];
        unsigned char first = *s - '0';
        int erg_index = 0;
        int pz;
        int i;
        unsigned char odd_even[10][6] = {
          {0,0,0,0,0,0},
          {0,0,1,0,1,1},
          {0,0,1,1,0,1},
          {0,0,1,1,1,0},
          {0,1,0,0,1,1},
          {0,1,1,0,0,1},
          {0,1,1,1,0,0},
          {0,1,0,1,0,1},
          {0,1,0,1,1,0},
          {0,1,1,0,1,0}
        };
        erg[erg_index++] = *s;
        pz = first;
        for(i=1;i<=11;i++){
            int digit = *(s + i) - '0';
            pz += (digit * ((i % 2)?3:1));
            if(i<=6){
                erg[erg_index++] = digit + 'A' + (odd_even[first][i - 1] * 10);
                if(i==6) erg[erg_index++]=42;
            }else{
                erg[erg_index++] = digit + 'a';
            }
        }
        pz = pz%10;
        if(pz)pz = 10 - pz;
        erg[erg_index++] = pz + 'a';
        erg[erg_index++] = 43;
        erg[erg_index++] = 0;
        return [NSString stringWithCString:erg];
    }
    return @"";
}
- (NSString *)ean128C00String;
{
// fuer numerische Nutzdaten
// CP1252
// pruefziffer ermitteln, Nutzdaten kodieren
// Start-C, fnc1, 00, Nutzdaten, Pruefziffer, Pruefzeichen, Stop
    int i,ii,j;
    int sum = 0;
    int gewichtung=2;
    int pz;
    NSString *s;
    NSMutableData *mdata = [NSMutableData dataWithCapacity:100];
    unsigned char c;
    c = 155; // Start C
    sum += 105;
    [mdata appendBytes:&c length:1];
    c = 152; // FNC1
    [mdata appendBytes:&c length:1];
    sum += 102;
    pz = [self nvePruefziffer];
    s = [NSSWF @"00%@%i",self,pz];
// 2er Bloecke in Buchstaben kodieren
    for(i=0,j=[s length];i<j;i+=2){
        ii = [[[s substringFromIndex:i]secureSubstringToIndex:2]intValue];
        sum += ii * gewichtung++;
        if(ii==0){
            c = 128;
        }else{
            if(ii<=94){
                c = ii + 32;
            }else{
                c = ii + 50;
            }
        }
        [mdata appendBytes:&c length:1];
    }
    /*
     Pruefzeichen berechnen lt. http://www.fh-gelsenkirchen.de/fb01/laboratorien/02-Informatik/sy/Diplom/SY2-Diplom_2008_9/EAN128_im_Detail.pdf
     1 Multipliziere den Wert des Startzeichens und des FN1 Zeichens mit 1. Gewichte den Wert des 3.
     Symbolzeichens mit 2 und alle weiteren Werte der Symbolzeichen (auch Hilfszeichen) mit Faktoren in
     aufsteigender Reichenfolge (3,4,5,...).
     2 Summiere alle gewichteten Werte
     3 Dividiere die Summe aus Schritt 2 durch 103
     4 Das Symbolprüfzeichen hat den Wert des Divisionsrestes von Schritt 3
     */
    ii = sum % 103;
    if(ii==0){
        c = 128;
    }else{
        if(ii<=94){
            c = ii + 32;
        }else{
            c = ii + 50;
        }
    }
    [mdata appendBytes:&c length:1];
    c = 156; // stop
    [mdata appendBytes:&c length:1];
    return [[[NSString alloc] initWithData:mdata encoding:NSWindowsCP1252StringEncoding]autorelease];
}
- (NSString *)translatedString;
{
    return [[_APP currentSession] transFor:self];
}
- (NSString *)simplyfiedString;
{
//nur grossbuchstaben uebriglassen
    char s[1024];
    char s1[1024];
    char	*p,*p1;
    char c;
    char *c1;
    c1 = (char *)[self lossyCString]; //f. Bank-suche, Modulsuche
    strncpy(s, c1, 1023);
    p = s;
    p1 = s1;
    while((c = *p)){
        if(c < '@'){
            p++;
            continue;
        }
        if(c > 'z'){
            p++;
            continue;
        }
        if(c > 'Z' && c < 'a'){
            p++;
            continue;
        }
        if(c >= 'a'){
            c-=32; //to upper
        }
        *p1=c;
        p1++;
        p++;
    }
    *p1 = 0; //ergebnis terminieren
    return [NSString stringWithCString:s1];
}
- (NSString *)str12;
{
    return [self str:12];
}
- (NSString *)str9;
{
    return [self str:9];
}
- (NSString *)str7;
{
    return [self str:7];
}
- (NSString *)str:(int)l;
{
// erzeugt rechtsbuendigen money-formatierten String der laenge l
    NSString *s = [self guiMoney];
    int i = [s length];
    if(i>l)return @"######,##";
    if(i==l)return s;
    return [NSSWF @"%@%@",[@"                                                     " secureSubstringToIndex:(l - i)],s];
}
- (NSString *)strs12;
{
    return [self strs:12];
}
- (NSString *)strs9;
{
    return [self strs:9];
}
- (NSString *)strs7;
{
    return [self strs:7];
}
- (NSString *)strs:(int)l;
{
// erzeugt rechtsbuendigen String der laenge l
    int i = [self length];
    if(i==l)return self;
    if(i>l)return [self substringToIndex:l];
    return [NSSWF @"%@%@",[@"                                                     " secureSubstringToIndex:(l - i)],self];
}
- (NSString *)stl12;
{
    return [self stl:12];
}
- (NSString *)stl9;
{
    return [self stl:9];
}
- (NSString *)stl7;
{
    return [self stl:7];
}
- (NSString *)stl:(int)l;
{
// erzeugt linksbuendigen String der laenge l
    return [[NSSWF @"%@                                                                                                                         ",self] secureSubstringToIndex:l];
}
- (NSString *)rfcRFC1342DecodedString;
{
// =?ISO-8859-15?Q?=E4=F6=FC=C4=D6=DC=DF?=blafasel
// =?ISO-8859-1?Q?Question_for_item_#320065132667_-_Einma?= =?ISO-8859-1?Q?liger_Kn=FCller_-_legend=E4res_Sammlerst=FCck?=
    NSArray *a = [self componentsSeparatedByString:@"=?ISO-8859-15?Q?"];
    int i,j;
    LMA;
    if([a count]<2)a = [self componentsSeparatedByString:@"=?iso-8859-15?Q?"];
    if([a count]<2)a = [self componentsSeparatedByString:@"=?ISO-8859-1?Q?"];
    if([a count]<2)a = [self componentsSeparatedByString:@"=?iso-8859-1?Q?"];
    if([a count]<2)return self;
    for(i=0,j=[a count];i<j;i++){
        NSString *s = [a oai:i];
// =E4=F6=FC=C4=D6=DC=DF?=blafasel
// FotoMail f=FCr Dich?=
        if(FILLED(s)){
            [lma addObject:[s iso8859DecodedString]];
        }
    }
    return [lma componentsJoinedByString:@""];
}
- (NSString *)onlyDigits;
//alles ausser digits strippen; Category
{
    char *c,cx;
    char c1[20];
    int count = 0;
    if([self length] < 1) return self;
    c = (char *)[self lossyCString]; //interessieren nur digits
    while((cx=*c++) && count < 18){
        if(!isdigit(cx))continue;
        c1[count++]=cx;
    }
    c1[count]=0;
    return [NSString stringWithCString:c1];
}
- (NSString *)normalizedTel;
// Telefonnr. normalisieren
{
    char *c,x;
    char c1[40];
    int count = 0;
    NSString *s = [self stringWithoutSpace];
    if([s hasSecurePrefix:@"+"]){
        s = [NSSWF @"00%@",[s substringFromIndex:1]];
    }
    if([s length] < 1) return s;
    c = (char *)[s lossyCString]; //interessieren nur digits
    while((x=*c++) && count < 39){
        if(x=='/'){
            c1[count++]='-';
            continue;
        }
        if(!(((x)>='0' && (x)<='9') || x == '-'))continue;
        c1[count++]=x;
    }
    c1[count]=0;
    return [NSString stringWithCString:c1];
}
- (NSString *)dbMoneyFromGui;
{
    return [[[self componentsSeparatedByString:@"."]componentsJoinedByString:EON] point];
}
- (NSString *)stringWithout0xD;
{
    return [self stringWithoutString:@"\r"];
}
- (NSString *)stringWithForwardSlashes;
{
    return [[self componentsSeparatedByString:@"\\"]componentsJoinedByString:@"/"];
}
- (NSString *)stringWithBackSlashes;
{
    return [[self componentsSeparatedByString:@"/"]componentsJoinedByString:@"\\"];
}
+ (NSString *)dbFromDouble:(double)d;
{
    return [NSString stringWithFormat:@"%0.2f", round2(d)];
}
+ (NSString *)dbFromDouble:(double)d nak:(int)nak;
{
    NSString *fs;
//lt. doku: wird gerundet, precision 0 -> keine Nachkommastellen;
    fs = [NSSWF @"%%0.%if",nak];
    return [NSSWF fs,d];
}
+ (NSString *)dbFromDouble:(double)d pba:(PBDDAttribute *)pba;
{
    switch([pba dataTyp]){
        case DT_MONEY:
            return [NSString dbFromDouble:d nak:2];
        case DT_FLOAT:
            return [NSString dbFromDouble:d nak:[pba nak]];
        case DT_INT:
            return [NSString dbFromDouble:d nak:0];
    }
    return nil;
}
- (NSString *)stringWithoutTextareaCR;
{
    NSString *ns;
    unichar *rd, *wr, *st, *rd_save;
    NSZone *zone;
    unsigned len, i;
    BOOL hasContent = NO;
    len = [self length];
    zone = NSDefaultMallocZone();
    rd = NSZoneMalloc(zone,sizeof(unichar)*len);
    rd_save = rd;
    wr = NSZoneMalloc(zone,sizeof(unichar)*len);
    st = wr;
    [self getCharacters: rd];
    for (i=0; i<len; i++, rd++) {
// the unichar (int16) values of the characters we are looking for
// happen to correspond to the char (int8) values. (\\ \n and \t)
// which makes this code safe.  Otherwise we would need to use the unichar
// 0x0000 values in the case statements.
        switch (*rd) {
            case 0xd:  break;
            case 0xf:  break;
            case 0xa0:  break;
            case 0xa:  *wr++=*rd; break;
            default: *wr++=*rd; hasContent = YES; break;
        }
    }
    if(!hasContent){
        ns = @"";
    }else{
        ns = [NSString stringWithCharacters:st length:wr-st];
    }
    NSZoneFree(zone,rd_save);
    NSZoneFree(zone,st);
    return ns;
}
- (int)hexValue;
{
    int i=[self length];
    int hv;
    int p = 16;
    if(!i)return 0;
    hv = [[self lastChar]valueForHexDigit];
    i-=2;
    while(i >= 0){
        hv += ([[self charAt:i]valueForHexDigit] * p);
        i--;
        p*=16;
    }
    return hv;
}
- (NSString *)hexFormat;
{
    LMA;
    int i,j;
    for(i=0,j=[self length];i<j;i++){
        unichar uni = [self characterAtIndex:i];
        [lma addObject:[NSSWF @"%x",(unsigned char)uni]];
    }
    return [lma componentsJoinedByString:@" "];
}
- (int)valueForHexDigit;
{
    NSString *s = [self lowercaseString];
    if([s length]!=1)return 0;
    if([s iE:@"a"])return 10;
    if([s iE:@"b"])return 11;
    if([s iE:@"c"])return 12;
    if([s iE:@"d"])return 13;
    if([s iE:@"e"])return 14;
    if([s iE:@"f"])return 15;
    return [s intValue];
}
- (NSString *)firstCapitalizedString;
{
    if([self length] <= 1)return [self uppercaseString];
    return [[[self substringToIndex:1]uppercaseString] sbas:[self substringFromIndex:1]];
}
- (NSString *)stringWithoutWindowsShit;
{
    int l = [self length];
    if(!l)return self;
    if([self characterAtIndex:l-1] == 13)return [self substringToIndex:l-1];
    return self;
}
- (NSString *)stringWithoutDigits;
{
    NSString *s = [self stringWithoutString:@"0"],*zahl;
    int i;
    for(i=1;i<10;i++){
        zahl = [NSSWF @"%i",i];
        s = [s stringWithoutString:zahl];
    }
    return s;
}
- (NSString *)urlEncodedString;
{
    return [self replace:@" " with:@"%20"];
}
- (NSString *)doubleQuotedString;
{
    return [NSSWF @"\"%@\"",self];
}
- (NSString *)mysqlCompliantString;
{
// fuer Namen in Mysql; feld und Tabellennamen
    return [[[self replace:@"." with:@"_"] replace:@"-" with:@"_"]lowercaseString];
}
- (NSString *)sageString;
{
// doppelte Hochkomma durch einfach ersetzen, weil Sage kein csv beherrscht
    return [self replace:@"\"" with:@"'"];
}
- (NSString *)sageDate;
{
// TTMMJJJJ
    NSString *s;
    if([self rangeOfString:@"-"].length){
        s = [self dbDateFromMySQLDate];
    }else{
        s = self;
    }
    if([s intValue]==0)return @"30071966";
    {
        long l = [[s secureSubstringToIndex:8] intValue];
        int day = l % 100;
        int month = (l / 100) % 100;
        int year = l / 10000;
        return [NSSWF @"%02i%02i%04i",day,month,year];
    }
}
- (NSString *)stringWithoutDatevSonderzeichen;
/* datev Sonderzeichen raus; */
{
    char s[255];
    unsigned char c;
    char s1[255];
    char *p,*p1;
    NSData *data;
    NSString *v;
    strncpy(s, [self lossyCString], 254); // Datev egal, ob unicode verluste
    p = s;
    p1 = s1;
    while(*p){ //umlaute umschluesseln wg. datev encoding
        c=*p;
/*
 */
        if(c==0xe4)c=0x84;
        if(c==0xf6)c=0x94;
        if(c==0xfc)c=0x81;
        if(c==0xc4)c=0x8e;
        if(c==0xd6)c=0x99;
        if(c==0xdc)c=0x9a;
        if(c==0xdf)c=0xe1;
        if((c < 0x20 && c != 0x15) || (c > 0x5a && c < 0x5f)  || (c > 0x7a && c != 0x81 && c != 0x84 && c != 0x8e && c != 0x94 && c != 0x99  && c != 0x9a && c != 0xe1 && c != 0xf9 && c != 0xfe)){
            p++;
            continue;
        }
        *p1++ = c;
        p++;
    }
    *p1=0;
    data = [[NSData alloc]initWithBytes:s1 length:strlen(s1)];
    v = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding]; //datev ist halt speziell
    [data release];
    [v autorelease];
    return v;
}
- (NSString *)stringWithHTMLTabs;
{
    return [[self replace:@"\t" with:@"&nbsp;&nbsp;&nbsp;&nbsp;"]replace:@" " with:@"&nbsp;"];
}
- (NSString *)asciiString;
    /* alles kleiner Space auf '_' setzen; */
{
    char s[1024];
    char	*p;
    strncpy(s, [self lossyCString], 1023); //sowieso nur ASCII gewollt
    p = s;
    while(*p){
        if(*p < ' '){
            *p = '_';
        }
        p++;
    }
    return [NSString stringWithCString:s];
}
- (NSString *)umlautExpandedString;
{
// fuer Filesysteme, die keine Umlaute koennen
    int i,j;
    NSMutableString *ms = [NSMutableString stringWithCapacity:100];
    [ms setString:@""];
    for(i=0,j=[self length];i<j;i++){
        unichar ch = [self characterAtIndex:i];
        switch(ch){
            case 0x00e4: [ms appendString:@"ae"]; continue;
            case 0x00f6: [ms appendString:@"oe"]; continue;
            case 0x00fc: [ms appendString:@"ue"]; continue;
            case 0x00c4: [ms appendString:@"Ae"]; continue;
            case 0x00d6: [ms appendString:@"Oe"]; continue;
            case 0x00dc: [ms appendString:@"Ue"]; continue;
            case 0x00df: [ms appendString:@"ss"]; continue;
            default:    [ms appendString:[NSString stringWithCharacters:&ch length:1]];
        }
    }
    return ms;
}
- (NSString *)stringWithoutCR;
{
    return [[self componentsSeparatedByString:SC_NewLine]componentsJoinedByString:@" "];
}
- (NSString *)stringWithoutSpace;
{
    return [self stringWithoutString:@" "];
}
@end
@implementation NSString ( PB3 )
/* Deprecated
- (NSString *)mySQLDateFromDbDate;
{
    return [[PBDate dateWithDBString:self]dateAsMysqlDBDTString];
}
- (NSString *)stringByAddingHour:(int)i;
{
    NSArray *a;
    int h,m;
    if([self length]!=5)return @"00:00";
    a = [self componentsSeparatedByString:@":"];
    if([a count]!=2)return @"00:00";
    h = [[a oai:0]intValue];
    m = [[a oai:1]intValue];
    h += i;
    if(h >=24 || h < 0)return @"00:00";
    return [NSSWF @"%02i:%02i",h,m];
}
- (int)zeitDifferenz:(NSString *)s;
{
    int hh1,mm1,hh2,mm2,i;
//HHMM
    if(!s || [s length]!=4 || [self length]!=4)return 0;
    hh1=[[self substringToIndex:2]intValue];
    hh2=[[s substringToIndex:2]intValue];
    mm1=[[self substringFromIndex:2]intValue];
    mm2=[[s substringFromIndex:2]intValue];
    i=hh2 * 60 + mm2 - hh1 * 60 - mm1;
    return i;
}
+ (NSString *)formattedDouble:(double )d;
{
    BOOL neg = NO;
    NSString *unit=EON;
    if(d < 0){
        neg =YES;
        d*=-1.0;
    }
    if(d > 10000.0){
        if(d<1000000.0){
            d /= 1000.0;
            unit = @" T";
        }else{
            d /= 1000000.0;
            unit = @" M";
        }
    }
    if(neg)d*=-1.0;
    return [[NSString stringWithFormat:@"%0.2f%@",round2(d),unit]comma];
}
- (BOOL)matches:(NSString *)s;
{
    NSArray *a;
    int i,j;
    if(!s)return NO;
    if([self iE:s])return YES;
    if([self iE:@"*"])return YES;
    a = [self componentsSeparatedByString:@","];
    j = [a count];
    if(j>1){
        for(i=0,j=[a count];i<j;i++){
            if([[a oai:i]matches:s])return YES;
        }
    }else{
        if([self hasSuffix:@"*"]){
            NSString *s1 = [self stringWithoutSuffix:@"*"];
            if([s hasSecurePrefix:s1])return YES;
        }
    }
    return NO;
}
- (NSString *)extendWith:(NSString *)s toLength:(int)l;
{
    NSMutableString *ms;
    if([self length]>=l)return [self substringToIndex:l];
    if(!FILLED(s))s=@".";
    ms = [NSMutableString stringWithCapacity:l];
    [ms setString:self];
    while([ms length] < l){
        [ms appendString:s];
    }
    return ms;
}
- (BOOL)matchesAt:(int)i1;
{
// * = immer
// 2,3,4,10-20   beliebig viele Einzelwerte und Ranges
    NSArray *a = [self componentsSeparatedByString:@","],*a1;
    int i,j;
    NSString *s;
    for(i=0,j=[a count];i<j;i++){
        s = [a oai:i];
        if([s iE:@"*"])return YES;
        if([s intValue]==i1)return YES;
        a1 = [self componentsSeparatedByString:@"-"];
        if([a1 count]!=2)continue;
        if([[a1 oai:0]intValue] <= i1 && [[a1 oai:1]intValue] >= i1)return YES;
    }
    return NO;
}
- (BOOL)matchesWotag:(NSString *)s1;
{
// So,Mo,Di
    NSArray *a = [self componentsSeparatedByString:@","];
    int i,j;
    NSString *s;
    for(i=0,j=[a count];i<j;i++){
        s = [a oai:i];
        if([s iE:@"*"])return YES;
        if([[s lowercaseString]iE:[s1 lowercaseString]])return YES;
    }
    return NO;
}
- (NSString *)beautifiedDescription;
{
    NSString *s = [self stringWithoutCR];
    if([s length]<2)return s;
    return [[s substringToIndex:[s length]-1] substringFromIndex:1];
}
- (NSString *)brForCR;
{
    return [[self componentsSeparatedByString:@"\n"]componentsJoinedByString:@"<br>"];
}
- (NSString *)replaceAtSigns;
{
    return [[self componentsSeparatedByString:@"&atsign;"]componentsJoinedByString:@"@"];
}
- (NSString *)stringByDeletingCarraigeReturnCtrlO;
{
    const char *sb=[self lossyCString],*sp=sb;
    char *db=NSZoneMalloc(0,strlen(sb)+1),*dp=db;
    NSAssert(db,@"NSZoneMalloc failed.");
    for (*dp=0;*sp;sp++) if (*sp!=15 && *sp!=13 && *sp!=10) *dp++=*sp;
    return [[[NSString alloc] initWithCStringNoCopy:db length:dp-db freeWhenDone:YES ] autorelease];
}
*/
- (int)levenshtein:(NSString *)s;
{
    {
        if(!FILLED(self))return [s length];
        if(!FILLED(s))return [self length];
    }
    {
        char c1[100],c2[100];
        char d[100][100];
        int n,m;
        int i,j,cost,dist1,dist2,dist3;
        [self prepareLevenshtein:c1];
        [s prepareLevenshtein:c2];
        n = strlen(c1);
        m = strlen(c2);
        if(!n)return m;
        if(!m)return n;
        for(i=0;i<=n;i++){
            d[i][0]=i;
        }
        for(j=0;j<=m;j++){
            d[0][j]=j;
        }
        for(i=1;i<=n;i++){
            for(j=1;j<=m;j++){
                if(c1[i-1] == c2[j-1]){
                    cost=0;
                }else{
                    cost=1;
                }
                dist1 = d[i-1][j  ] + 1;
                dist2 = d[i  ][j-1] + 1;
                dist3 = d[i-1][j-1] + cost;
                d[i][j]=MIN(MIN(dist1,dist2),dist3);
            }
        }
        return d[n][m];
    }
    /*
    int LevenshteinDistance(char s[1..n], char t[1..m])
     declare int d[0..n,0..m]
     declare int i, j, cost
     for i := 0 to n
     d[i,0] := i
     for j := 0 to m
     d[0,j] := j
     for i := 1 to n
     for j := 1 to m
     if s[i] = t[j] then cost := 0
     else cost := 1
     d[i,j] := minimum(d[i-1,j  ] + 1,    // insertion
                       d[i,  j-1] + 1,    // deletion
                       d[i-1,j-1] + cost) // substitution
     return d[n,m]
     */
}
- (void)prepareLevenshtein:(char *)l;
{
// l zeigt auf einen max. 100 char. grossen char Buffer
// String moeglichst klein machen fuer Levenshtein
    char *p,*l_start = l;
    char c;
    const char *c1 = [[self lowercaseString] lossyCString];
    p = (char *)c1;
    while((c = *p) && (l - l_start) < 99){
        // nur Ziffern und kleinbuchstaben uebriglassen und die umlaute
        if(c < 48){
            p++;
            continue;
        }
        if(c > 57 && c < 97){
            p++;
            continue;
        }
        *l=c;
        l++;
        p++;
    }
    *l = 0; //ergebnis terminieren
}
- (BOOL)isSearchCode;
{
// darf nur +,-,=,k,t,& enthalten
    NSString *searchCodes = @"+&f";
    int i,j;
    for(i=0,j=[self length];i<j;i++){
        NSString *s = [self charAt:i];
        if(![searchCodes rangeOfString:s].length)return NO;
    }
    return YES;
}
- (NSDictionary *)partsFromEMLPath;
{
// mail analysieren
    NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:self];
    LMD;
    if(fh){
        NSData *data = [fh readDataOfLength:60 * 1024]; // muss reichen fuer headers und body;
        [fh closeFile];
        if(data){
            NSString *s = [NSString stringWithData:data]; // erraet encoding
            NSArray *a = [s componentsSeparatedByString:@"\n"];
            int i,j,i1;
            LMA; // zeilen f. body
            NSString *line;
            BOOL isUTF8 = NO;
            NSString *llc;
            
// headers beschaffen
            for(i=0,j=[a count];(i<j);i++){
                line = [[a oai:i]stringWithoutWindowsShit];
                llc = [line lowercaseString];
// Date: Sun, 06 Aug 2006 00:18:03 +0200 (CEST)
// From: Pirmin Braun <pb@seat-1.com>
// Subject: Re: Anfrage ERP-Software =?ISO-8859-1?Q?L=F6sung?=
                if([llc hasSecurePrefix:@"date: "]){
                    NSString *s = [[[[line secureSubstringFromIndex:5]componentsSeparatedByString:@","]lastObject]stringWithoutLeadingWhiteSpace];
                    NSCalendarDate *date;
                    date = [NSCalendarDate dateWithString:s calendarFormat:@"%d %b %Y %H:%M:%S %z"];
                    if(!date){
                        date = [NSCalendarDate dateWithString:s calendarFormat:@"%d %b %Y %H:%M %z"];
                    }
                    if(!date){
                        date = [NSCalendarDate dateWithString:s calendarFormat:@"%d %b %Y %H:%M"];
                    }
                    if(!date){
                        date = [NSCalendarDate dateWithString:s calendarFormat:@"%d %b %Y %H:%M:%S"];
                    }
                    [lmd setSecureObject:[date descriptionWithCalendarFormat:ND_FORMAT] forKey:@"Date:"];
                }
                if([llc hasSecurePrefix:@"from: "]){
                    [lmd setSecureObject:[[line secureSubstringFromIndex:[@"From: " length]]rfcRFC1342DecodedString] forKey:@"From:"];
                }
                if([llc hasSecurePrefix:@"to: "]){
                    [lmd setSecureObject:[[line secureSubstringFromIndex:[@"to: " length]]rfcRFC1342DecodedString] forKey:@"To:"];
                }
                if([llc hasSecurePrefix:@"message-id: "]){
                    [lmd setSecureObject:[[line secureSubstringFromIndex:[@"Message-ID: " length]]rfcRFC1342DecodedString] forKey:@"Message-ID:"];
                }
                if([llc hasSecurePrefix:@"reply-to: "]){
                    [lmd setSecureObject:[[line secureSubstringFromIndex:[@"Reply-To: " length]]rfcRFC1342DecodedString] forKey:@"Reply-To:"];
                }
                if([llc hasSecurePrefix:@"subject: "]){
                    [lmd setSecureObject:[[line secureSubstringFromIndex:[@"Subject: " length]]rfcRFC1342DecodedString] forKey:@"Subject:"];
                }
                if([llc hasSecurePrefix:@"in-reply-to: "]){
                    [lmd setSecureObject:[[line secureSubstringFromIndex:[@"In-Reply-To: " length]]rfcRFC1342DecodedString] forKey:@"In-Reply-To:"];
                }
 
                if(!FILLED(llc))break; // erste leerzeile beendet header;
                if([llc rangeOfString:@"charset=\"utf-8\""].length)isUTF8 = YES;
                if([llc rangeOfString:@"charset=utf-8"].length)isUTF8 = YES;
            }
// mit boundary zu arbeiten, ist muehsam; es kann mehrere boundary arten geben; 
// ersten Part beschaffen; einfach einen text/plain part suchen; dann nach naechster leerzeile bis zum rest;
// dabei auch encoding ermitteln;
// falls es keinen text/plain gibt, nach erster leerzeile beginnen
// manche mails enthalten auch  nur html...
            i++;
            i1=0;
            for(;(i<j);i++){
		i1++;
                if(i1 > 200)break; // zu lange mails wollen wir nicht; Google Desktop indiziert auch nur den Anfang von Dateien;
                line = [[a oai:i]stringWithoutWindowsShit];
                llc = [line lowercaseString];
                if(i1 < 30){
// am Anfang die Steuerzeilen ueberlesen
                    if(([llc rangeOfString:@"charset=\"utf-8\""].length) || ([llc rangeOfString:@"charset=utf-8"].length)){
                        isUTF8 = YES;
                        continue;
                    }
                    if([llc rangeOfString:@"--"].length)continue;
                    if([llc rangeOfString:@"content-type:"].length)continue;
                    if([llc rangeOfString:@"boundary="].length)continue;
                    if([llc rangeOfString:@"charset="].length)continue;
                    if([llc rangeOfString:@"content-transfer-encoding:"].length)continue;
                    if([llc rangeOfString:@"this is a multi-part message"].length)continue;
                }
                if([llc rangeOfString:@"content-transfer-encoding: base64"].length)break;
                if(!FILLED(line)){
                    line = @"\n";
                }else{
                    if([line hasSecureSuffix:@"="]){
                        line = [line stringWithoutLastChar];
                    }else{
                        line = [line stringByAppendingString:@"\n"];
                    }
                    if(isUTF8){
                        line = [line utf8DecodedString];
                    }else{
                        line = [line iso8859DecodedString];
                    }
                }
                [lma addObject:line];
            }
            [lmd setObject:[lma componentsJoinedByString:@""] forKey:@"body"];
        }
    }
    return lmd;
}
- (BOOL)matchesGruppenstring:(NSString *)gs;
{
    int i;
// self sei ein gruppenstring, gs auch;
// ist wenigstens an 1 Stelle gemeinsam ein "x", matchen die Strings
    
// fuer nix berechtigt
    if([gs iE:@"----------"])return NO;
    if([self iE:@"----------"])return NO;
    for(i=0;i<ANZ_GRUPPEN;i++){
        if([[[gs charAt:i]lowercaseString]iE:@"x"] && [[[self charAt:i]lowercaseString]iE:@"x"]) return YES;
    }
    return NO;
}
- (NSString *)thumbPath;
{
    NSString *ext = [self pathExtension];
    NSString *s= [[self stringByDeletingPathExtension]stringByAppendingFormat:@"_tn.%@",ext];
    return [s stringWithForwardSlashes];
}
- (BOOL)isJ;
{
    return [self iE:@"J"];
}
- (BOOL)isFilled;
{
    return FILLED(self);
}
/* kann man sich schenken, bringt nix
- (double)doubleValue;
{
    double d;
    sscanf([self c String],"%lf",&d);
    return d;
//wg. Genauigkeit: bringt nur 1 Stelle mehr    return [[NSDecimalNumber decimalNumberWithString:self]doubleValue];
}
*/
- (NSComparisonResult)compareCaseInsensitive:(NSString *)s;
{
    return [[self lowercaseString]compare:[s lowercaseString]];
}
- (NSComparisonResult)compareCaseInsensitiveD:(NSString *)s;
{
    return [[s lowercaseString]compare:[self lowercaseString]];
}
- (NSComparisonResult)compareNumeric:(NSString *)s;
{
    double d1,d2;
    if(!s)return NSOrderedDescending;
    d1 = [self doubleValue];
    d2 = [s doubleValue];
    if(d1==d2)return NSOrderedSame;
    if(d1 > d2) return NSOrderedDescending;
    return NSOrderedAscending;
}
- (NSComparisonResult)compareNumericD:(NSString *)s;
{
    double d1,d2;
    if(!s)return NSOrderedAscending;
    d1 = [self doubleValue];
    d2 = [s doubleValue];
    if(d1==d2)return NSOrderedSame;
    if(d1 > d2) return NSOrderedAscending;
    return NSOrderedDescending;
}
- (NSComparisonResult)compareBySecondIntField:(NSString *)s;
{
    NSArray *a1,*a2;
    int i1,i2;
    a1 = [self componentsSeparatedByString:@"|"];
    a2 = [s componentsSeparatedByString:@"|"];
    if([a1 count]!=3)return NSOrderedDescending;
    if([a2 count]!=3)return NSOrderedAscending;
    i1 = [[a1 oai:1] intValue];
    i2 = [[a2 oai:1] intValue];
    if(i1 > i2)return NSOrderedDescending;
    if(i1 < i2)return NSOrderedAscending;
    return [(NSString *)[a1 oai:2] compare:(NSString *)[a2 oai:2]];
}
- (NSComparisonResult)compareHierarchie:(NSString *)s;
{
// ...2..  ...10...
// 002 010
    NSArray *a;
    int i,j;
    LMA;
    NSString *si,*s1,*s2;
    a = [self componentsSeparatedByString:@"."];
    for(i=0,j=[a count];i<j;i++){
        si = [NSSWF @"%03i",[[a oai:i]intValue]];
        [lma addObject:si];
    }
    s1 = [lma componentsJoinedByString:EON];
    [lma removeAllObjects];
    a = [s componentsSeparatedByString:@"."];
    for(i=0,j=[a count];i<j;i++){
        si = [NSSWF @"%03i",[[a oai:i]intValue]];
        [lma addObject:si];
    }
    s2 = [lma componentsJoinedByString:EON];
// LOGS(([NSSWF @"%@ %@",s1,s2]));
    return [s1 compare:s2];
}
- (BOOL)isNumeric;
// enthaelt nur ziffern
{
    char *c,cx;
    if([self length] < 1) return NO;
    c = (char *)[self lossyCString]; //interessieren nur digits
    while((cx=*c++)){
        if(!isdigit(cx))return NO;
    }
    return YES;
}
@end
#ifndef GNUSTEP
@interface NSCalendarDate_ODBugfix : NSCalendarDate
@end
@implementation NSCalendarDate_ODBugfix
static IMP original = NULL;
#define sel @selector(dateByAddingYears:months:days:hours:minutes:seconds:)
+ (void)load
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    Class superclass = [self superclass];
    original = [superclass instanceMethodForSelector:sel];
    [self poseAsClass:superclass];
    [pool release];
}
static id _addToDate(NSCalendarDate *date, int y, int m, int d, int hh, int
                     mm, int ss)
{
    /* [dateByAddingYears:...] hangs for any Jan 1st later than 2001 */
    if ([date yearOfCommonEra] > 2001 && [date dayOfYear] == 1) {
        date = [date addTimeInterval:60 * 60 * 24];
        d--;
    }
    return original(date, sel, y, m, d, hh, mm, ss);
}
/* ([self monthOfYear] + month) must be within the range from -128 to +127
for the original implementation to work correctly. The same restriction
applys to ([self dayOfMonth] + day) */
#define MAX_MONTHS_ADD    115  /* (max. monthOfYear) 12 + 115 =  127 */
#define MAX_MONTHS_SUB   -129  /* (min. monthOfYear)  1 - 129 = -128 */
#define MAX_DAYS_ADD       96  /* (max. dayOfMonth)  31 +  96 =  127 */
#define MAX_DAYS_SUB     -129  /* (min. dayOfMonth)   1 - 129 = -128 */
- (NSCalendarDate *)dateByAddingYears:(int)year
                               months:(int)month
                                 days:(int)day
                                hours:(int)hour
                              minutes:(int)minute
                              seconds:(int)second
{
    NSCalendarDate *date = self;
    while (month > MAX_MONTHS_ADD) {
        date = _addToDate(date, 0, MAX_MONTHS_ADD, 0, 0, 0, 0);
        month -= MAX_MONTHS_ADD;
    }
    while (month < MAX_MONTHS_SUB) {
        date = _addToDate(date, 0, MAX_MONTHS_SUB, 0, 0, 0, 0);
        month -= MAX_MONTHS_SUB;
    }
    while (day > MAX_DAYS_ADD) {
        date = _addToDate(date, 0, 0, MAX_DAYS_ADD, 0, 0, 0);
        day -= MAX_DAYS_ADD;
    }
    while (day < MAX_DAYS_SUB) {
        date = _addToDate(date, 0, 0, MAX_DAYS_SUB, 0, 0, 0);
        day -= MAX_DAYS_SUB;
    }
    return _addToDate(date, year, month, day, hour, minute, second);
}
@end
#endif
@implementation NSFileManager (PB3)
- (NSArray *)directoriesAtPath:(NSString *)path;
{
    LMA;
    NSArray *a = [self directoryContentsAtPath:path];
    NSString *s;
    int i,j=[a count];
    NSDictionary *d;
    for(i=0;i<j;i++){
        s = [a oai:i];
        if(!(d = [myFM fileAttributesAtPath:[path stringByAppendingPathComponent:s] traverseLink:NO]))continue;
        if([[d ofk:NSFileType]iE:NSFileTypeDirectory]){
            [lma addObject:s];
        }
    }
    return lma;
}
- (NSArray *)directoryContentsAtPath:(NSString *)path suffix:(NSString *)suffix fullName:(BOOL)fullName;
{
    if(!suffix) return [self directoryContentsAtPath:path suffixes:nil skips:nil fullName:fullName];
    return [self directoryContentsAtPath:path suffixes:[NSArray arrayWithObject:suffix] skips:nil fullName:fullName];
}
- (NSArray *)directoryContentsAtPath:(NSString *)path suffixes:(NSArray *)suffixes skips:(NSArray *)skips fullName:(BOOL)fullName;
{
// fullName bedeutet "mit extension"; es wird immer nur der filename geliefert, ohne pfad;
    LMA;
    NSArray *a = [self directoryContentsAtPath:path];
    NSString *s,*suffix;
    int i,ii,j=[a count],jj=[suffixes count];
    for(i=0;i<j;i++){
        s = [a oai:i];
        if([s hasPrefixFrom:skips])continue;
        if(!jj){
            if(fullName){
                [lma addObject:s];
            }
        }else{
            for(ii=0;ii<jj;ii++){
                suffix = [suffixes oai:ii];
                if([s hasSuffix:suffix]){
                    if(fullName){
                        [lma addObject:s];
                    }else{
                        [lma addObject:[s substringToIndex:[s length]-[suffix length]]];
                    }
                }
            }
        }
    }
    return lma;
}
- (NSArray *)directoryDeepContentsAtPath:(NSString *)path suffixes:(NSArray *)suffixes skips:(NSArray *)skips fullName:(BOOL)yn;
{
    return [self directoryDeepContentsAtPath:path suffixes:suffixes skips:skips fullName:yn newer:nil];
}
- (NSArray *)directoryDeepContentsAtPath:(NSString *)path suffixes:(NSArray *)suffixes skips:(NSArray *)skips fullName:(BOOL)yn newer:(NSDate *)newer;
{
    //geht subdirectories nach
    LMA;
    NSString *s,*suffix;
    int ii,jj=[suffixes count];
    NSDirectoryEnumerator *e = [self enumeratorAtPath:path];
    while ((s = [e nextObject])) {
        if([s hasPrefixFrom:skips])continue;
        for(ii=0;ii<jj;ii++){
            suffix = [suffixes oai:ii];
            if([s hasSuffix:suffix]){
                if(newer){
                    NSDate *fd = [[myFM fileAttributesAtPath:[NSSWF @"%@/%@",path,s] traverseLink:NO] fileModificationDate];
                    if([fd compare:newer]==NSOrderedAscending)continue;
                }
                if(yn){
                    [lma addObject:s];
                }else{
                    [lma addObject:[s substringToIndex:[s length]-[suffix length]]];
                }
            }
        }
    }
    return lma;
}
- (NSArray *)sourceFilesWith:(NSString *)s atPath:(NSString *)path;
{
    NSArray *a = [self directoryContentsAtPath:path suffixes:[NSArray arrayWithObjects:@".m",@".h",nil] skips:nil fullName:YES];
    int i,j=[a count];
    NSMutableArray *ma = [NSMutableArray arrayWithCapacity:j];
    NSString *fn,*file;
    NSRange r1;
    if(!FILLED(s))return (NSArray *)ma;
    for(i=0;i<j;i++){
        fn = [a oai:i];
        file = [NSString stringWithContentsOfFile:[path stringByAppendingPathComponent:fn]];
        if(!file)continue;
        r1 = [file rangeOfString:s options:NSCaseInsensitiveSearch];
        if(!(r1.length))continue;
        [ma addObject:fn];
    }
    return (NSArray *)ma;
}
- (void)copyPathWithDir:(NSString *)path toPath:(NSString *)to;
{
    NSString *dirName;
    if(!FILLED(path))return;
    if(!FILLED(to))return;
    dirName = [to stringByDeletingLastPathComponent];
    if(![self fileExistsAtPath:dirName]){
        if(![self createAllDirsAtPath:dirName]){
            LOGS(([NSSWF @"copyPathWithDir: Directory %@ konnte nicht angelegt werden",dirName]));
            return;
        }
    }
    [self copyPath:path toPath:to handler:nil];
}
- (BOOL)createAllDirsAtPath:(NSString *)dirName;
{
    NSArray *a = [[dirName stringWithForwardSlashes] componentsSeparatedByString:@"/"];
    int i,j;
    NSString *s;
    
    j=[a count];
    if(j<2)return YES;
    i=1;
    s = [a firstObject];
    if([s hasSuffix:@":"]){
        s = [s sbas:@"/"];
    }else{
        s = @"/";
    }
    s = [s sbas:[a oai:i++]];
    while([self fileExistsAtPath:s]){
        if(i>=j)break;
        s = [s stringByAppendingFormat:@"/%@",[a oai:i++]];
    }
    while([self createDirectoryAtPath:s attributes:nil]){
        if(i>=j)break;
        s = [s stringByAppendingFormat:@"/%@",[a oai:i++]];
    }
    if(i==j)return YES;
    return NO;
}
@end
@implementation NSData (PB3)
- (NSString *)string;
{
    return [NSString stringWithData:self];
}
- (BOOL)writeToFile:(NSString *)path;
{
    return [self writeToFile:path atomically:YES];
}
- (char)charAt:(int)i;
{
    char *p;
    if(i>=[self length])return 0;
    p = ((char *)[self bytes] + i);
    return *p;
}
- (NSString *)hexFormat;
{
    int i,j;
    unsigned char *pin = (unsigned char *)[self bytes];
    unsigned char ch;
    NSString *hex[16] = {@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"A",@"B",@"C",@"D",@"E",@"F"};
    NSMutableString *ms = [NSMutableString stringWithCapacity:10];
    for(i=0,j=[self length]; i < j; i++) {
        ch = *pin >> 4;
        [ms appendString:hex[ch]];
        ch = *pin++ & 0x0f;
        [ms appendString:hex[ch]];
    }
    return ms;
}
@end
Foto