2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "NSString+Formatting.h"
24 #include "NGMemoryAllocation.h"
29 # ifndef USE_VA_LIST_PTR
30 # define USE_VA_LIST_PTR 1
33 # define USE_VA_LIST_PTR 0
38 // %|[justification]|[fieldwidth]|[precision]|formatChar
39 // %[-]{digit}*[.{digit}*]conv
41 static const char *formatChars =
44 "c" // int arg -> uchar
49 static const char *formatAttrs = "#0- +,";
50 static const char *formatConv = "hlLqZ";
51 static Class StrClass = Nil;
62 static inline NSString *
63 wideString(const char *str, int width, char fillChar, BOOL isRightAligned)
69 if (StrClass == Nil) StrClass = [NSString class];
72 return str ? [StrClass stringWithCString:str] : nil;
74 if ((len = strlen(str)) >= width)
75 return str ? [StrClass stringWithCString:str] : nil;
77 tmp = malloc(width + 2);
78 if (isRightAligned) { // right aligned
79 for (cnt = 0; cnt < (width - len); cnt++)
81 strncpy(tmp + (width - len), str, len);
84 else { // left aligned
85 for (cnt = len; cnt < width; cnt++)
87 strncpy(tmp, str, len);
91 return [[[StrClass alloc]
92 initWithCStringNoCopy:tmp length:strlen(tmp)
93 freeWhenDone:YES] autorelease];
96 static inline NSString *xsSimpleFormatObject(char *fmt, id _value, NSString *_ds) {
98 formats an object value in an simple format
99 (this is only the '%{mods}{type}' part of a real
101 The method to get the value of the object is
102 determined by the {type} of the Format,
103 eg: i becomes intValue, @ becomes description ..
105 the decimal point in float-strings can be replaced by the _ds
106 String. To do this, the result of the Format is scanned for
107 '.' and _ds is expanded for this.
109 int fmtLen = strlen(fmt);
110 char fmtChar = fmt[fmtLen - 1];
111 NGFormatType typeMode = DEFAULT_TYPE;
114 BOOL alignRight = YES;
118 if (StrClass == Nil) StrClass = [NSString class];
120 while (index(formatAttrs, *pos)) {
128 while (isdigit((int)*pos)) pos++;
140 while (isdigit((int)*pos)) pos++;
150 if (index(formatConv, *pos)) {
153 typeMode = SHORT_TYPE;
157 typeMode = LONG_TYPE;
159 if (*pos == 'l') { // long-long
160 typeMode = VERY_LONG_TYPE;
166 typeMode = VERY_LONG_TYPE;
172 if (index("diouxX", fmtChar)) {
178 unsigned short int i = [(NSNumber *)_value unsignedShortValue];
179 len = sprintf(buf, fmt, i);
183 unsigned long int i = [(NSNumber *)_value unsignedLongValue];
184 len = sprintf(buf, fmt, i);
187 case VERY_LONG_TYPE: {
188 long long i = [(NSNumber *)_value unsignedLongLongValue];
189 len = sprintf(buf, fmt, i);
193 unsigned int i = [(NSNumber *)_value unsignedIntValue];
194 len = sprintf(buf, fmt, i);
199 return (len >= 0) ? [StrClass stringWithCString:buf length:len] : nil;
202 if (fmtChar == 'c') {
204 int i = [(NSNumber *)_value unsignedCharValue];
207 len = sprintf(buf, fmt, (unsigned char)i);
209 return (len > 0) ? [StrClass stringWithCString:buf length:len] : nil;
211 if (fmtChar == 'C') {
212 // TODO: implement correctly
213 /* 16bit unichar value */
217 /* TODO: evil hack */
218 fmt[fmtLen - 1] = 'c';
219 i = [(NSNumber *)_value unsignedCharValue];
220 len = sprintf(buf, fmt, (unsigned char)i);
222 return (len > 0) ? [StrClass stringWithCString:buf length:len] : nil;
225 if (index("feEgG", fmtChar)) {
226 unsigned char buf[256];
229 if (typeMode == VERY_LONG_TYPE) {
230 long double i = [(NSNumber *)_value doubleValue];
231 len = sprintf(buf, fmt, i);
234 double i = [(NSNumber *)_value doubleValue];
235 len = sprintf(buf, fmt, i);
239 NSMutableString *result;
242 unsigned int bufCount = 0;
245 return [StrClass stringWithCString:buf length:len];
248 [NSMutableString stringWithCapacity:len + [_ds cStringLength]];
250 for (cnt = 0; cnt < len; cnt++) {
251 if (buf[cnt] == '.') {
255 // TODO: cache selector
256 s = [[StrClass alloc] initWithCString:ptr length:bufCount];
257 if (s) [result appendString:s];
261 if (_ds) [result appendString:_ds];
263 ptr = &(buf[cnt + 1]);
273 // TODO: cache selector
274 s = [[StrClass alloc] initWithCString:ptr length:bufCount];
275 if (s) [result appendString:s];
286 case 's': case 'S': {
287 /* TODO: implement 'S', current mech is evil hack */
293 return wideString("<null>", width, ' ', alignRight);
295 len = [(NSString *)_value cStringLength];
296 buffer = malloc(len + 10);
297 [_value getCString:buffer];
300 result = wideString(buffer, width, ' ', alignRight);
307 NSString *dstr = obj ? [obj description] : @"<nil>";
312 return wideString("<null>", width, ' ', alignRight);
314 len = [dstr cStringLength];
315 buffer = malloc(len + 10);
316 [dstr getCString:buffer];
319 dstr = wideString(buffer, width, ' ', alignRight);
329 dstr = obj ? [obj stringValue] : @"<nil>";
330 cstr = (char *)[dstr cString];
331 if (cstr == NULL) cstr = "<null>";
333 return wideString(cstr, width, ' ', alignRight);
337 fprintf(stderr, "WARNING(%s): unknown printf format used: '%s'\n",
338 __PRETTY_FUNCTION__, fmt);
346 static inline NSString *handleFormat(char *fmt, int fmtLen, va_list *_ap) {
348 static inline NSString *handleFormat(char *fmt, int fmtLen, va_list _ap) {
351 char typeMode = DEFAULT_TYPE;
354 BOOL alignRight = YES;
357 if (StrClass == Nil) StrClass = [NSString class];
362 fmtChar = fmt[fmtLen - 1];
364 while (index(formatAttrs, *pos)) {
372 while (isdigit((int)*pos)) pos++;
384 while (isdigit((int)*pos)) pos++;
394 if (index(formatConv, *pos)) {
397 typeMode = SHORT_TYPE;
401 typeMode = LONG_TYPE;
403 if (*pos == 'l') { // long-long
404 typeMode = VERY_LONG_TYPE;
410 typeMode = VERY_LONG_TYPE;
417 printf(" width=%i prec=%i\n", width, prec); fflush(stdout);
420 if (index("diouxX", fmtChar)) {
427 unsigned short int i = va_arg(*_ap, int);
429 unsigned short int i = va_arg(_ap, int);
431 len = sprintf(buf, fmt, i);
436 unsigned long int i = va_arg(*_ap, unsigned long int);
438 unsigned long int i = va_arg(_ap, unsigned long int);
440 len = sprintf(buf, fmt, i);
443 case VERY_LONG_TYPE: {
445 long long i = va_arg(*_ap, long long);
447 long long i = va_arg(_ap, long long);
449 len = sprintf(buf, fmt, i);
454 unsigned int i = va_arg(*_ap, unsigned int);
456 unsigned int i = va_arg(_ap, unsigned int);
458 len = sprintf(buf, fmt, i);
463 return (len >= 0) ? [StrClass stringWithCString:buf length:len] : nil;
466 if (fmtChar == 'c') {
469 int i = va_arg(*_ap, int);
471 int i = va_arg(_ap, int);
476 return @"<'\\0'-char>";
478 len = sprintf(buf, fmt, (unsigned char)i);
481 xsprintf("got format %s char %i made %s len %i\n",
485 if (len == 0) return nil;
486 return [StrClass stringWithCString:buf length:len];
488 if (fmtChar == 'C') {
492 int i = va_arg(*_ap, int);
494 int i = va_arg(_ap, int);
499 return @"<'\\0'-unichar>";
501 /* TODO: implement properly, evil hack */
502 fmt[fmtLen - 1] = 'c';
503 len = sprintf(buf, fmt, (unsigned char)i);
506 xsprintf("got format %s unichar %i made %s len %i\n",
510 if (len == 0) return nil;
511 return [StrClass stringWithCString:buf length:len];
514 if (index("feEgG", fmtChar)) {
518 if (typeMode == VERY_LONG_TYPE) {
520 long double i = va_arg(*_ap, long double);
522 long double i = va_arg(_ap, long double);
524 len = sprintf(buf, fmt, i);
528 double i = va_arg(*_ap, double);
530 double i = va_arg(_ap, double);
532 len = sprintf(buf, fmt, i);
535 return (len >= 0) ? [StrClass stringWithCString:buf length:len] : nil;
543 case 'S': /* unicode char array */
549 if (fmtChar == 's') {
551 cstr = va_arg(*_ap, char *);
553 cstr = va_arg(_ap, char *);
558 id obj = va_arg(*_ap, id);
560 id obj = va_arg(_ap, id);
567 if ((d = (fmtChar == '@') ?[obj description]:[obj stringValue])) {
568 unsigned len = [d cStringLength];
570 cstr = NGMalloc(len + 1);
578 if (cstr == NULL) cstr = "<null>";
580 result = wideString(cstr, width, ' ', alignRight);
581 if (owned) NGFree(cstr);
586 fprintf(stderr, "WARNING(%s): unknown printf format used: '%s'\n",
587 __PRETTY_FUNCTION__, fmt);
595 static inline NSString *_stringWithCFormat(const char *_format, va_list _ap) {
596 const char *firstPercent;
597 NSMutableString *result;
599 if (StrClass == Nil) StrClass = [NSString class];
600 firstPercent = index(_format, '%');
602 fprintf(stderr, "OWN: format='%s'\n", _format);
606 // first check whether there are any '%' in the format ..
607 if (firstPercent == NULL) {
608 // no formatting contained in _format
609 return [StrClass stringWithCString:_format];
612 result = [NSMutableString stringWithCapacity:256];
614 if ((firstPercent - _format) > 0) {
616 s = [[StrClass alloc] initWithCString:_format
617 length:(firstPercent - _format)];
618 if (s) [result appendString:s];
620 _format = firstPercent;
623 while (*_format != '\0') { // until end of format string
624 if (*_format == '%') { // found formatting character
625 _format++; // skip '%'
627 if (*_format == '%') { // was a quoted '%'
628 [result appendString:@"%"];
631 else { // check format
638 while ((*_format != '\0') &&
639 (index(formatChars, *_format) == NULL)) {
649 // printf("handling ext format '%s'\n", extFmt); fflush(stdout);
650 /* hack for iSeries port, ix86 seems to copy va_list iSeries
651 don`t like pointers to va_list
656 s = handleFormat(extFmt, strlen(extFmt), &_ap);
658 s = handleFormat(extFmt, strlen(extFmt), _ap);
660 if (s) [result appendString:s];
664 else { // normal char
665 const char *start = _format; // remember start
667 _format++; // skip found char
669 // further increase format until '\0' or '%'
670 while ((*_format != '\0') && (*_format != '%'))
673 s = [[StrClass alloc] initWithCString:start
674 length:(_format - start)];
675 if (s) [result appendString:s];
682 @implementation NSString(XSFormatting)
684 + (id)stringWithCFormat:(const char *)_format arguments:(va_list)_ap {
685 return [self stringWithString:_stringWithCFormat(_format, _ap)];
688 + (id)stringWithFormat:(NSString *)_format arguments:(va_list)_ap {
693 len = [_format cStringLength] + 1;
694 cfmt = malloc(len + 1);
695 [_format getCString:cfmt]; cfmt[len] = '\0';
696 s = [self stringWithString:_stringWithCFormat(cfmt, _ap)];
701 + (id)stringWithCFormat:(const char *)_format, ... {
705 va_start(ap, _format);
706 result = [self stringWithString:_stringWithCFormat(_format, ap)];
711 + (id)stringWithFormat:(NSString *)_format, ... {
717 len = [_format cStringLength];
718 cfmt = malloc(len + 1);
719 [_format getCString:cfmt]; cfmt[len] = '\0';
720 va_start(ap, _format);
721 result = [self stringWithString:_stringWithCFormat(cfmt, ap)];
727 - (id)initWithFormat:(NSString *)_format arguments:(va_list)_ap {
731 len = [_format cStringLength];
732 cfmt = malloc(len + 1);
733 [_format getCString:cfmt]; cfmt[len] = '\0';
734 self = [self initWithString:_stringWithCFormat(cfmt, _ap)];
739 @end /* NSString(XSFormatting) */
741 @implementation NSMutableString(XSFormatting)
743 - (void)appendFormat:(NSString *)_format arguments:(va_list)_ap {
748 len = [_format cStringLength];
749 cfmt = malloc(len + 4);
751 [_format getCString:cfmt]; cfmt[len] = '\0';
752 s = _stringWithCFormat(cfmt, _ap);
753 if (cfmt) free(cfmt);
754 if (s) [self appendString:s];
756 - (void)appendFormat:(NSString *)_format, ... {
762 len = [_format cStringLength];
763 cfmt = malloc(len + 4);
764 [_format getCString:cfmt]; cfmt[len] = '\0';
765 va_start(ap, _format);
766 s = _stringWithCFormat(cfmt, ap);
768 if (cfmt) free(cfmt);
769 if (s) [self appendString:s];
772 @end /* NSMutableString(XSFormatting) */
778 int xs_sprintf(char *str, const char *format, ...) {
781 va_start(ap, format);
782 result = xs_vsprintf(str, format, ap);
786 int xs_snprintf(char *str, size_t size, const char *format, ...) {
789 va_start(ap, format);
790 result = xs_vsnprintf(str, size, format, ap);
797 void __link_NSString_Formatting(void) {
798 __link_NSString_Formatting();