OSDN Git Service

暫定登録
[iptd/iPTd.git] / src / Raym / String.cpp
1 //\r
2 // String.cpp\r
3 //\r
4 \r
5 #include <stdlib.h>\r
6 #include <string.h>\r
7 #include <regex>\r
8 #ifdef _WIN32\r
9 #include <time.h>\r
10 #else\r
11 #include <sys/time.h>\r
12 #endif\r
13 #include <errno.h>\r
14 \r
15 #define DBG_LEVEL 0\r
16 #include <Raym/Log.h>\r
17 #include <Raym/String.h>\r
18 #include <Raym/Array.h>\r
19 #include <Raym/Data.h>\r
20 \r
21 #define HEX2DEC(P1) (('a' <= P1) && (P1 <= 'f') ? (P1 - 'a' + 10) : ('A' <= P1) && (P1 <= 'F') ? (P1 - 'A' + 10) : ('0' <= P1) && (P1 <= '9') ? (P1 - '0') : 0)\r
22 \r
23 namespace Raym\r
24 {\r
25 \r
26 String *String::alloc()\r
27 {\r
28     DebugLog2("String::alloc()");\r
29 \r
30     return new String();\r
31 }\r
32 \r
33 String *String::string()\r
34 {\r
35     DebugLog2("String::string()");\r
36 \r
37     String *result = String::alloc()->init();\r
38     if (result != NULL)\r
39     {\r
40         result->autorelease();\r
41     }\r
42     return result;\r
43 }\r
44 \r
45 String *String::stringWithCString(const char *nullTerminatedCString, StringEncoding encoding)\r
46 {\r
47     DebugLog2("String::stringWithCString()");\r
48 \r
49     String *result = String::alloc()->initWithCString(nullTerminatedCString, encoding);\r
50     if (result != NULL)\r
51     {\r
52         result->autorelease();\r
53     }\r
54     return result;\r
55 }\r
56 \r
57 String *String::stringWithUTF8String(const char *nullTerminatedCString)\r
58 {\r
59     DebugLog2("String::stringWithUTF8String()");\r
60 \r
61     String *result = String::alloc()->initWithUTF8String(nullTerminatedCString);\r
62     if (result != NULL)\r
63     {\r
64         result->autorelease();\r
65     }\r
66     return result;\r
67 }\r
68 \r
69 String *String::stringWithFormat(String *format, ...)\r
70 {\r
71     DebugLog2("String::stringWithFormat(String *, ...)");\r
72 \r
73     String *result = NULL;\r
74     if (format != NULL)\r
75     {\r
76         va_list ap;\r
77         va_start(ap, format);\r
78         result = String::alloc()->initWithFormat(format->cString(), ap);\r
79         va_end(ap);\r
80         if (result != NULL)\r
81         {\r
82             result->autorelease();\r
83         }\r
84     }\r
85     return result;\r
86 }\r
87 \r
88 String *String::stringWithFormat(const char *format, ...)\r
89 {\r
90     DebugLog2("String::stringWithFormat(const char *, ...)");\r
91 \r
92     String *result = NULL;\r
93     if (format != NULL)\r
94     {\r
95         va_list ap;\r
96         va_start(ap, format);\r
97         result = String::alloc()->initWithFormat(format, ap);\r
98         va_end(ap);\r
99         if (result != NULL)\r
100         {\r
101             result->autorelease();\r
102         }\r
103     }\r
104     return result;\r
105 }\r
106 \r
107 String *String::stringWithContentsOfFile(const char *path, StringEncoding encoding)\r
108 {\r
109     DebugLog2("String::stringWithContentsOfFile()");\r
110 \r
111     String *result = NULL;\r
112     if (path != NULL)\r
113     {\r
114         Data *data = Data::dataWithContentsOfFile(path);\r
115         if (data != NULL)\r
116         {\r
117 #if 0\r
118             char *tmp = (char *)malloc(data->length() + 1);\r
119             if (tmp != NULL)\r
120             {\r
121                 memcpy(tmp, data->bytes(), data->length());\r
122                 tmp[data->length()] = '\0';\r
123                 result = String::stringWithUTF8String(tmp);\r
124                 free(tmp);\r
125             }\r
126 #else\r
127             result = String::alloc()->initWithData(data, encoding);\r
128             if (result != NULL)\r
129             {\r
130                 result->autorelease();\r
131             }\r
132 #endif\r
133         }\r
134     }\r
135     return result;\r
136 }\r
137 \r
138 String *String::stringWithString(String *string)\r
139 {\r
140     DebugLog2("String::stringWithString()");\r
141 \r
142     String *result = NULL;\r
143     if (string != NULL)\r
144     {\r
145         result = String::alloc()->initWithUTF8String(string->cString());\r
146         if (result != NULL)\r
147         {\r
148             result->autorelease();\r
149         }\r
150     }\r
151     return result;\r
152 }\r
153 \r
154 String *String::base64StringWithBytes(const char *bytes, UInteger length)\r
155 {\r
156     DebugLog2("String::base64StringWithBytes()");\r
157 \r
158     static char base64EncodingTable[64] =\r
159     {\r
160         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',\r
161         'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',\r
162         'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',\r
163         'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'\r
164     };\r
165 \r
166     if (bytes == NULL)\r
167     {\r
168         return NULL;\r
169     }\r
170     if (length == 0)\r
171     {\r
172         return String::string();\r
173     }\r
174 \r
175     unsigned long long buflen = ((length + 3) / 3) * 4 + 1;\r
176     char *encoded = (char *)malloc(buflen);\r
177     if (encoded == NULL)\r
178     {\r
179         return NULL;\r
180     }\r
181     encoded[buflen - 1] = '\0';\r
182 \r
183     unsigned long long in_offset = 0;\r
184     unsigned long long out_offset = 0;\r
185     while (in_offset < length)\r
186     {\r
187         switch (length - in_offset)\r
188         {\r
189         case 1:\r
190             encoded[out_offset]     = base64EncodingTable[(bytes[in_offset] & 0xFC) >> 2];\r
191             encoded[out_offset + 1] = base64EncodingTable[((bytes[in_offset] & 0x03) << 4)];\r
192             encoded[out_offset + 2] = '=';\r
193             encoded[out_offset + 3] = '=';\r
194             break;\r
195         case 2:\r
196             encoded[out_offset]     = base64EncodingTable[(bytes[in_offset] & 0xFC) >> 2];\r
197             encoded[out_offset + 1] = base64EncodingTable[((bytes[in_offset] & 0x03) << 4) | ((bytes[in_offset + 1] & 0xF0) >> 4)];\r
198             encoded[out_offset + 2] = base64EncodingTable[((bytes[in_offset + 1] & 0x0F) << 2)];\r
199             encoded[out_offset + 3] = '=';\r
200             break;\r
201         default:\r
202             encoded[out_offset]     = base64EncodingTable[(bytes[in_offset] & 0xFC) >> 2];\r
203             encoded[out_offset + 1] = base64EncodingTable[((bytes[in_offset] & 0x03) << 4) | ((bytes[in_offset + 1] & 0xF0) >> 4)];\r
204             encoded[out_offset + 2] = base64EncodingTable[((bytes[in_offset + 1] & 0x0F) << 2) | ((bytes[in_offset + 2] & 0xC0) >> 6)];\r
205             encoded[out_offset + 3] = base64EncodingTable[bytes[in_offset + 2] & 0x3F];\r
206             break;\r
207         }\r
208         in_offset += 3;\r
209         out_offset += 4;\r
210     }\r
211 \r
212     String *result = String::stringWithUTF8String(encoded);\r
213     free(encoded);\r
214 \r
215     return result;\r
216 }\r
217 \r
218 String::String()\r
219 {\r
220     DebugLog2("String::String()");\r
221 \r
222     _str = NULL;\r
223     _length = -1;\r
224 }\r
225 \r
226 String::~String()\r
227 {\r
228     DebugLog2("String::~String(\"%s\")", _str);\r
229 \r
230     if (_str != NULL)\r
231     {\r
232         free(_str);\r
233         _str = NULL;\r
234     }\r
235     _length = -1;\r
236 }\r
237 \r
238 String *String::init()\r
239 {\r
240     DebugLog2("String::init()");\r
241 \r
242     _str = (char *)malloc(1);\r
243     if (_str == NULL)\r
244     {\r
245         release();\r
246         return NULL;\r
247     }\r
248     _str[0] = '\0';\r
249     _length = 0;\r
250     return this;\r
251 }\r
252 \r
253 String *String::initWithCString(const char *nullTerminatedCString, StringEncoding encoding)\r
254 {\r
255     DebugLog2("String::initWithCString(\"%s\", %d)", nullTerminatedCString, encoding);\r
256 \r
257     if (nullTerminatedCString != NULL)\r
258     {\r
259         UInteger length = (UInteger)strlen(nullTerminatedCString);\r
260         if (length != 0)\r
261         {\r
262             switch (encoding)\r
263             {\r
264             case ShiftJISStringEncoding:\r
265                 {\r
266 #ifdef _WIN32\r
267                     int wlen = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, nullTerminatedCString, (int)(strlen(nullTerminatedCString) + 1), NULL, 0);\r
268                     if (wlen != 0)\r
269                     {\r
270                         wchar_t *wbuf = (wchar_t *)malloc(sizeof(wchar_t) * (wlen + 1));\r
271                         if (wbuf != NULL)\r
272                         {\r
273                             memset(wbuf, 0x00, sizeof(wchar_t) * (wlen + 1));\r
274                             MultiByteToWideChar(CP_ACP, 0, nullTerminatedCString, (int)(strlen(nullTerminatedCString) + 1), wbuf, sizeof(wchar_t) * (wlen + 1));\r
275                             int ulen = WideCharToMultiByte(CP_UTF8, 0, wbuf, wlen, NULL, 0, NULL, NULL);\r
276                             if (ulen != 0)\r
277                             {\r
278                                 _str = (char *)malloc(ulen + 1);\r
279                                 if (_str != NULL)\r
280                                 {\r
281                                     memset(_str, 0x00, ulen + 1);\r
282                                     WideCharToMultiByte(CP_UTF8, 0, wbuf, wlen, _str, ulen + 1, NULL, NULL);\r
283                                     free(wbuf);\r
284                                     _length = ulen;\r
285                                 }\r
286                                 else\r
287                                 {\r
288                                     DebugLog0("error: %s(): malloc", __FUNCTION__);\r
289                                     free(wbuf);\r
290                                     release();\r
291                                     return NULL;\r
292                                 }\r
293                             }\r
294                             else\r
295                             {\r
296                                 DebugLog0("error: %s(): WideCharToMultiByte", __FUNCTION__);\r
297                                 release();\r
298                                 return NULL;\r
299                             }\r
300                         }\r
301                         else\r
302                         {\r
303                             DebugLog0("error: %s(): malloc", __FUNCTION__);\r
304                             release();\r
305                             return NULL;\r
306                         }\r
307                     }\r
308                     else\r
309                     {\r
310                         DebugLog0("error: %s(): MultiByteToWideChar, 0x%08x", __FUNCTION__, GetLastError());\r
311                         release();\r
312                         return NULL;\r
313                     }\r
314 #else\r
315                     DebugLog0("not implement.");\r
316 #endif\r
317                 }\r
318                 break;\r
319 \r
320             case UTF8StringEncoding:\r
321                 _str = (char *)malloc(length + 1);\r
322                 if (_str != NULL)\r
323                 {\r
324                     _length = length;\r
325                     memcpy(_str, nullTerminatedCString, _length);\r
326                     _str[_length] = '\0';\r
327                 }\r
328                 else\r
329                 {\r
330                     DebugLog3("error: %s(): malloc", __FUNCTION__);\r
331                     release();\r
332                     return NULL;\r
333                 }\r
334                 break;\r
335 \r
336             default:\r
337                 DebugLog0("warning: Unknown String Encoding: 0x%08x", encoding);\r
338                 release();\r
339                 return NULL;\r
340             }\r
341         }\r
342         else\r
343         {\r
344             // length is zero\r
345             return init();\r
346         }\r
347     }\r
348     else\r
349     {\r
350         DebugLog0("warning: string is null.");\r
351         release();\r
352         return NULL;\r
353     }\r
354 \r
355     return this;\r
356 }\r
357 \r
358 String *String::initWithUTF8String(const char *bytes)\r
359 {\r
360     DebugLog2("String::initWithUTF8String()");\r
361 \r
362     return initWithCString(bytes, UTF8StringEncoding);\r
363 }\r
364 \r
365 String *String::initWithData(Data *data, StringEncoding encoding)\r
366 {\r
367     DebugLog2("String::initWithData()");\r
368 \r
369     String *result = NULL;\r
370     if (data != NULL)\r
371     {\r
372         char *tmp = (char *)malloc(data->length() + 1);\r
373         if (tmp != NULL)\r
374         {\r
375             memcpy(tmp, data->bytes(), data->length());\r
376             tmp[data->length()] = '\0';\r
377             result = String::initWithCString(tmp, encoding);\r
378             free(tmp);\r
379         }\r
380     }\r
381     else\r
382     {\r
383         DebugLog0("warning: data is null.");\r
384         release();\r
385     }\r
386 \r
387     return result;\r
388 }\r
389 \r
390 String *String::initWithFormat(const char *format, ...)\r
391 {\r
392     DebugLog2("String::initWithFormat(const char *, ...)");\r
393 \r
394     if (format == NULL)\r
395     {\r
396         release();\r
397         return NULL;\r
398     }\r
399 \r
400     va_list ap;\r
401     va_start(ap, format);\r
402     String *result = initWithFormat(format, ap);\r
403     va_end(ap);\r
404 \r
405     return result;\r
406 }\r
407 \r
408 //\r
409 // format(書式は1つのみ)とvalueで、新たにmallocした領域にsnprintfして返す。\r
410 // 呼び元はfreeすること。malloc失敗等の場合はNULLを返す。\r
411 //\r
412 static char *print_1(const char *format, void *value)\r
413 {\r
414     static const int DEFAULT_LENGTH = 32;\r
415     char *buf = (char *)malloc(DEFAULT_LENGTH);\r
416     if (buf != NULL)\r
417     {\r
418 #ifdef _WIN32\r
419         int require = _snprintf_s(buf, DEFAULT_LENGTH, _TRUNCATE, format, value);\r
420 #else\r
421         int require = snprintf(buf, DEFAULT_LENGTH, format, value);\r
422 #endif\r
423         if (require >= DEFAULT_LENGTH)\r
424         {\r
425             ++require;\r
426             char *buf2 = (char *)realloc(buf, require);\r
427             if (buf2 != NULL)\r
428             {\r
429                 buf = buf2;\r
430 #ifdef _WIN32\r
431                 _snprintf_s(buf, require, _TRUNCATE, format, value);\r
432 #else\r
433                 snprintf(buf, require, format, value);\r
434 #endif\r
435             }\r
436             else\r
437             {\r
438                 free(buf);\r
439                 buf = NULL;\r
440             }\r
441         }\r
442     }\r
443     return buf;\r
444 }\r
445 \r
446 //\r
447 // allocしたs1をs2分加算してreallocしてstrcatし、新しい(または同じ)アドレスを返す。\r
448 // 失敗した場合はNULLを返す。その場合 s1 は解放はしない。\r
449 static char *strcat_with_realloc(char *s1, const char *s2)\r
450 {\r
451     char *buf = (char *)realloc(s1, strlen(s1) + strlen(s2) + 1);\r
452     if (buf != NULL)\r
453     {\r
454         strcat_s(buf, (strlen(s1) + strlen(s2) + 1), s2);\r
455     }\r
456     return buf;\r
457 }\r
458 \r
459 String *String::initWithFormat(const char *format, va_list ap)\r
460 {\r
461     DebugLog2("String::initWithFormat(const char *, va_list)");\r
462 \r
463     char *buf = NULL;\r
464 \r
465 #if 0\r
466     va_list ap2;\r
467     va_copy(ap2, ap);\r
468     printf("initWithFormat: format: %s\n", format);\r
469     void *p = va_arg(ap2, void *);\r
470     printf("initWithFormat: arg1: 0x%016lx\n", p);\r
471 \r
472     va_end(ap2);\r
473 #endif\r
474 \r
475     if (strstr(format, "%@") == NULL)\r
476     {\r
477         // とりあえず 256バイトくらいで実行してみて\r
478 #define INITIAL_LENGTH 256\r
479         buf = (char *)malloc(INITIAL_LENGTH);\r
480         if (buf == NULL)\r
481         {\r
482             DebugLog3("malloc NG. (1)\n");\r
483             release();\r
484             return NULL;\r
485         }\r
486 \r
487         va_list ap2;\r
488         va_copy(ap2, ap);\r
489 \r
490 #ifdef _WIN32\r
491         int result = vsnprintf_s(buf, INITIAL_LENGTH, INITIAL_LENGTH - 1, format, ap);\r
492         // バッファに収まっていなければ\r
493         if (result < 0)\r
494         {\r
495             int count = 2;\r
496             while (true)\r
497             {\r
498                 char *buf2 = (char *)realloc(buf, INITIAL_LENGTH * count);\r
499                 if (buf2 == NULL)\r
500                 {\r
501                     DebugLog3("realloc NG. (1)\n");\r
502                     free(buf);\r
503                     va_end(ap2);\r
504                     release();\r
505                     return NULL;\r
506                 }\r
507                 buf = buf2;\r
508                 result = vsnprintf_s(buf, INITIAL_LENGTH * count, INITIAL_LENGTH * count - 1, format, ap2);\r
509                 if (result >= INITIAL_LENGTH)\r
510                 {\r
511                     break;\r
512                 }\r
513                 ++count;\r
514             }\r
515 \r
516         }\r
517 #else\r
518         int require = vsnprintf(buf, INITIAL_LENGTH, format, ap);\r
519 \r
520         // バッファに収まっていなければ\r
521         if (require >= INITIAL_LENGTH)\r
522         {\r
523             // 必要なサイズのバッファを再捕捉\r
524             ++require;\r
525             char *buf2 = (char *)realloc(buf, require);\r
526             if (buf2 == NULL)\r
527             {\r
528                 DebugLog3("realloc NG. (1)\n");\r
529                 free(buf);\r
530                 va_end(ap2);\r
531                 release();\r
532                 return NULL;\r
533             }\r
534             buf = buf2;\r
535             vsnprintf(buf, require, format, ap2);\r
536         }\r
537 #endif\r
538         va_end(ap2);\r
539     }\r
540     else\r
541     {\r
542         // windows で動かしたら %@ がうまくいかないので\r
543         // しばらく封印する。。。\r
544         printf("%%@ tu ka e nai\n");\r
545         abort();\r
546 \r
547 #ifdef _WIN32\r
548         char *fmt = _strdup(format);\r
549 #else\r
550         char *fmt = strdup(format);\r
551 #endif\r
552         if (fmt == NULL)\r
553         {\r
554             DebugLog3("strdup NG.\n");\r
555             release();\r
556             return NULL;\r
557         }\r
558 \r
559         // とりあえず、null文字分確保\r
560         buf = (char *)malloc(1);\r
561         if (buf == NULL)\r
562         {\r
563             DebugLog3("malloc NG. (2)\n");\r
564             free(fmt);\r
565             release();\r
566             return NULL;\r
567         }\r
568 \r
569         char *p1 = fmt;\r
570         char *p2;\r
571         while (true)\r
572         {\r
573             if (strlen(p1) == 0)\r
574             {\r
575                 break;\r
576             }\r
577             p2 = strchr(p1 + 1, '%');\r
578             if (p2 != NULL)\r
579             {\r
580                 if (p2 == p1 + 1)\r
581                 {\r
582                     p1 += 2;\r
583                     buf = (char *)realloc(buf, strlen(buf) + 1 + 2);\r
584                     if (buf == NULL)\r
585                     {\r
586                         DebugLog3("realloc NG. (2)\n");\r
587                         free(fmt);\r
588                         release();\r
589                         return NULL;\r
590                     }\r
591                     strcat_s(buf, (strlen(buf) + 3), "%%");\r
592                 }\r
593                 else\r
594                 {\r
595                     *p2 = '\0';\r
596 \r
597                     if (strstr(p1, "%") != NULL)\r
598                     {\r
599                         void *ptr;\r
600                         char *at;\r
601                         if ((at = strstr(p1, "%@")) != NULL)\r
602                         {\r
603                             ++at;\r
604                             *at = 's';\r
605                             Object *obj = va_arg(ap, Object *);\r
606                             ptr = (void *)obj->description()->cString();\r
607                         }\r
608                         else\r
609                         {\r
610                             ptr = va_arg(ap, void *);\r
611                         }\r
612 \r
613                         char *tmpbuf = print_1(p1, ptr);\r
614                         if (tmpbuf == NULL)\r
615                         {\r
616                             free(fmt);\r
617                             release();\r
618                             return NULL;\r
619                         }\r
620 \r
621                         char *buf2 = strcat_with_realloc(buf, tmpbuf);\r
622                         free(tmpbuf);\r
623                         if (buf2 == NULL)\r
624                         {\r
625                             free(fmt);\r
626                             free(buf);\r
627                             release();\r
628                             return NULL;\r
629                         }\r
630                         buf = buf2;\r
631                     }\r
632                     else\r
633                     {\r
634                         char *buf2 = strcat_with_realloc(buf, p1);\r
635                         if (buf2 == NULL)\r
636                         {\r
637                             free(fmt);\r
638                             free(buf);\r
639                             release();\r
640                             return NULL;\r
641                         }\r
642                         buf = buf2;\r
643                     }\r
644 \r
645                     *p2 = '%';\r
646                     p1 = p2;\r
647                 }\r
648             }\r
649             else\r
650             {\r
651                 if (*p1 == '%')\r
652                 {\r
653                     void *ptr = (void *)"(nil)";\r
654                     char *at;\r
655                     if ((at = strstr(p1, "%@")) != NULL)\r
656                     {\r
657                         ++at;\r
658                         *at = 's';\r
659                         Object *obj = va_arg(ap, Object *);\r
660                         if (obj != NULL)\r
661                         {\r
662                             ptr = (void *)obj->description()->cString();\r
663                         }\r
664                     }\r
665                     else\r
666                     {\r
667                         ptr = va_arg(ap, void *);\r
668                     }\r
669                     char *tmpbuf = print_1(p1, ptr);\r
670                     if (tmpbuf == NULL)\r
671                     {\r
672                         free(fmt);\r
673                         release();\r
674                         return NULL;\r
675                     }\r
676 \r
677                     char *buf2 = strcat_with_realloc(buf, tmpbuf);\r
678                     free(tmpbuf);\r
679                     if (buf2 == NULL)\r
680                     {\r
681                         free(fmt);\r
682                         free(buf);\r
683                         release();\r
684                         return NULL;\r
685                     }\r
686                     buf = buf2;\r
687                 }\r
688                 else\r
689                 {\r
690                     char *buf2 = strcat_with_realloc(buf, p1);\r
691                     if (buf2 == NULL)\r
692                     {\r
693                         free(fmt);\r
694                         free(buf);\r
695                         release();\r
696                         return NULL;\r
697                     }\r
698                     buf = buf2;\r
699                 }\r
700                 break;\r
701             }\r
702         }\r
703         free(fmt);\r
704     }\r
705 \r
706     String *result= initWithUTF8String(buf);\r
707     free(buf);\r
708 \r
709     return result;\r
710 }\r
711 \r
712 String *String::retain()\r
713 {\r
714     DebugLog2("String::retain()");\r
715 \r
716     Object::retain();\r
717     return this;\r
718 }\r
719 \r
720 String *String::autorelease()\r
721 {\r
722     DebugLog2("String::autorelease()");\r
723 \r
724     Object::autorelease();\r
725     return this;\r
726 }\r
727 \r
728 UInteger String::length()\r
729 {\r
730     DebugLog2("String::length()");\r
731 \r
732     return _length;\r
733 }\r
734 \r
735 bool String::hasPrefix(String *prefix)\r
736 {\r
737     bool result = false;\r
738     if (prefix != NULL)\r
739     {\r
740         if (length() >= prefix->length())\r
741         {\r
742             result = (strncmp(_str, prefix->_str, prefix->length()) == 0);\r
743         }\r
744     }\r
745     return result;\r
746 }\r
747 \r
748 bool String::hasPrefix(const char *prefix)\r
749 {\r
750     DebugLog2("String::hasPrefix()");\r
751 \r
752     String *k = String::alloc()->initWithUTF8String(prefix);\r
753     bool ret = hasPrefix(k);\r
754     k->release();\r
755     return ret;\r
756 }\r
757 \r
758 bool String::hasSuffix(String *suffix)\r
759 {\r
760     bool result = false;\r
761     if (suffix != NULL)\r
762     {\r
763         if (length() >= suffix->length())\r
764         {\r
765             result = (strncmp(&_str[length() - suffix->length()], suffix->_str, suffix->length()) == 0);\r
766         }\r
767     }\r
768     return result;\r
769 }\r
770 \r
771 bool String::hasSuffix(const char *suffix)\r
772 {\r
773     DebugLog3("String::hasSuffix()");\r
774 \r
775     String *k = String::alloc()->initWithUTF8String(suffix);\r
776     bool ret = hasSuffix(k);\r
777     k->release();\r
778     return ret;\r
779 }\r
780 \r
781 bool String::isEqualToString(String *string)\r
782 {\r
783     DebugLog3("String::isEqualToString(\"%s\")", string->cString());\r
784 \r
785     bool result = false;\r
786     if (string != NULL)\r
787     {\r
788         result = (strcmp(_str, string->_str) == 0);\r
789     }\r
790     return result;\r
791 }\r
792 \r
793 bool String::isEqualToString(const char *string)\r
794 {\r
795     DebugLog3("String::isEqualToString(\"%s\")", string);\r
796 \r
797     String *k = String::alloc()->initWithCString(string, UTF8StringEncoding);\r
798     bool ret = isEqualToString(k);\r
799     k->release();\r
800     return ret;\r
801 }\r
802 \r
803 bool String::isMatch(String *regex)\r
804 {\r
805     DebugLog3("String::isMatch()");\r
806 \r
807     bool result = false;\r
808 #if 0\r
809     if (regex != NULL)\r
810     {\r
811         std::regex re(regex->cString());\r
812         std::match_results<const char *>results;\r
813 \r
814         result = std::regex_search(_str, results, re, std::regex_constants::match_default);\r
815     }\r
816 #else\r
817     if (regex != NULL)\r
818     {\r
819         result = isMatch(regex->cString());\r
820     }\r
821 #endif\r
822     return result;\r
823 }\r
824 \r
825 bool String::isMatch(const char *regex)\r
826 {\r
827     DebugLog3("String::isMatch()");\r
828 \r
829     bool result = false;\r
830     if (regex != NULL)\r
831     {\r
832         std::regex re(regex);\r
833         std::match_results<const char *>results;\r
834 \r
835         result = std::regex_search(_str, results, re, std::regex_constants::match_default);\r
836     }\r
837     return result;\r
838 }\r
839 \r
840 String *String::stringByAppendingPathComponent(String *pathComponent)\r
841 {\r
842     DebugLog3("String::stringByAppendingPathComponent()");\r
843 \r
844     String *result = NULL;\r
845     if (pathComponent != NULL)\r
846     {\r
847         Array *paths = pathComponent->pathComponents();\r
848         if (paths != NULL)\r
849         {\r
850             if (paths->count() > 0)\r
851             {\r
852                 if (((String *)paths->objectAtIndex(0))->isEqualToString("/") ||\r
853                     ((String *)paths->objectAtIndex(0))->isEqualToString("\\"))\r
854                 {\r
855                     paths->removeObjectAtIndex(0);\r
856                 }\r
857                 if (paths->count() > 0)\r
858                 {\r
859                     if (hasSuffix("/") || hasSuffix("\\"))\r
860                     {\r
861                         result = stringByAppendingString((String *)paths->objectAtIndex(0));\r
862                     }\r
863                     else\r
864                     {\r
865                         result = stringByAppendingString("\\");\r
866                         result = result->stringByAppendingString((String *)paths->objectAtIndex(0));\r
867                     }\r
868                     paths->removeObjectAtIndex(0);\r
869                     if (paths->count() > 0)\r
870                     {\r
871                         for (UInteger i = 0; i < paths->count(); ++i)\r
872                         {\r
873                             result = result->stringByAppendingString("\\");\r
874                             result = result->stringByAppendingString((String *)paths->objectAtIndex(i));\r
875                         }\r
876                     }\r
877                 }\r
878             }\r
879         }\r
880     }\r
881     if (result == NULL)\r
882     {\r
883         result = String::stringWithString(this);\r
884     }\r
885     return result;\r
886 }\r
887 \r
888 String *String::stringByAppendingPathComponent(const char *pathComponent)\r
889 {\r
890     return stringByAppendingPathComponent(String::stringWithUTF8String(pathComponent));\r
891 }\r
892 \r
893 String *String::stringByAppendingString(String *aString)\r
894 {\r
895     String *result = NULL;\r
896     if (aString != NULL)\r
897     {\r
898         result = String::stringWithFormat("%s%s", _str, aString->cString());\r
899     }\r
900     return result;\r
901 }\r
902 \r
903 String *String::stringByAppendingString(const char *aString)\r
904 {\r
905     return stringByAppendingString(String::stringWithUTF8String(aString));\r
906 }\r
907 \r
908 String *String::stringByAbbreviatingWithTildeInPath()\r
909 {\r
910     String *result = NULL;\r
911     result = String::stringWithUTF8String(cString());\r
912     return result;\r
913 }\r
914 \r
915 String *String::stringByReplacingOccurrencesOfString(String *target, String *replacement)\r
916 {\r
917     String *result = NULL;\r
918     if ((target != NULL) && (replacement != NULL))\r
919     {\r
920         std::string tmp = _str;\r
921         while (strstr(tmp.c_str(), target->cString()) != NULL)\r
922         {\r
923             tmp.replace(tmp.find(target->cString(), 0), target->length(), replacement->cString());\r
924         }\r
925         result = String::stringWithUTF8String(tmp.c_str());\r
926     }\r
927     if (result == NULL)\r
928     {\r
929         result = String::stringWithUTF8String(_str);\r
930     }\r
931     return result;\r
932 }\r
933 \r
934 String *String::stringByReplacingOccurrencesOfString(const char *target, const char *replacement)\r
935 {\r
936     return stringByReplacingOccurrencesOfString(String::stringWithUTF8String(target),\r
937                                                 String::stringWithUTF8String(replacement));\r
938 }\r
939 \r
940 String *String::stringByReplacingOccurrencesOfString(const char *target, String *replacement)\r
941 {\r
942     return stringByReplacingOccurrencesOfString(String::stringWithUTF8String(target), replacement);\r
943 }\r
944 \r
945 String *String::stringByStandardizingPath()\r
946 {\r
947     String *result = NULL;\r
948     result = String::stringWithUTF8String(cString());\r
949     return result;\r
950 }\r
951 \r
952 String *String::stringByRemovingPercentEncoding()\r
953 {\r
954     String *result = NULL;\r
955     char *buf = (char *)malloc(_length + 1);\r
956     if (buf != NULL)\r
957     {\r
958         char *dst = buf;\r
959         char *p = _str;\r
960         while (*p != '\0')\r
961         {\r
962             if (*p == '%')\r
963             {\r
964                 *dst = (HEX2DEC(p[1]) << 4) | HEX2DEC(p[2]);\r
965                 DebugLog3("%02x", *dst);\r
966                 ++dst;\r
967                 p += 3;\r
968             }\r
969             else\r
970             {\r
971                 *(dst++) = *(p++);\r
972             }\r
973         }\r
974         *dst = '\0';\r
975         DebugLog3("buf: %s", buf);\r
976         result = String::stringWithUTF8String(buf);\r
977         free(buf);\r
978     }\r
979     else\r
980     {\r
981         result = String::string();\r
982     }\r
983     return result;\r
984 }\r
985 \r
986 String *String::stringByTrimming()\r
987 {\r
988     String *result = NULL;\r
989     char *buf = _strdup(_str);\r
990     if (buf != NULL)\r
991     {\r
992         char *st = buf;\r
993         while (true)\r
994         {\r
995             if (*st == ' ')\r
996             {\r
997                 ++st;\r
998             }\r
999             else\r
1000             {\r
1001                 break;\r
1002             }\r
1003         }\r
1004         while (true)\r
1005         {\r
1006             if (st[strlen(st) - 1] == ' ')\r
1007             {\r
1008                 st[strlen(st) - 1] = '\0';\r
1009             }\r
1010             else\r
1011             {\r
1012                 break;\r
1013             }\r
1014         }\r
1015         result = String::stringWithUTF8String(st);\r
1016         free(buf);\r
1017     }\r
1018     else\r
1019     {\r
1020         result = String::string();\r
1021     }\r
1022     return result;\r
1023 }\r
1024 \r
1025 Array *String::pathComponents()\r
1026 {\r
1027     Array *result = Array::arrayWithCapacity(0);\r
1028 #ifdef _WIN32\r
1029     char *tmp = _strdup(_str);\r
1030 #else\r
1031     char *tmp = strdup(_str);\r
1032 #endif\r
1033     if (tmp != NULL)\r
1034     {\r
1035         char *p = tmp;\r
1036         if ((*p == '\\') || (*p == '/'))\r
1037         {\r
1038             result->addObject(String::stringWithUTF8String("\\"));\r
1039             ++p;\r
1040         }\r
1041         while (p != NULL)\r
1042         {\r
1043             char *p2 = strchr(p, '\\');\r
1044             if (p2 == NULL)\r
1045             {\r
1046                 p2 = strchr(p, '/');\r
1047             }\r
1048             if (p2 != NULL)\r
1049             {\r
1050                 *p2 = '\0';\r
1051             }\r
1052             if (strlen(p) > 0)\r
1053             {\r
1054                 result->addObject(String::stringWithUTF8String(p));\r
1055             }\r
1056             if (p2 != NULL)\r
1057             {\r
1058                 p = p2 + 1;\r
1059             }\r
1060             else\r
1061             {\r
1062                 p = NULL;\r
1063             }\r
1064         }\r
1065 \r
1066         free(tmp);\r
1067     }\r
1068     return result;\r
1069 }\r
1070 \r
1071 String *String::pathExtension()\r
1072 {\r
1073     String *result = NULL;\r
1074     Array *comps = pathComponents();\r
1075     if (comps != NULL)\r
1076     {\r
1077         if (comps->count() > 0)\r
1078         {\r
1079             String *last = (String *)comps->objectAtIndex(comps->count() - 1);\r
1080             char *p = strrchr(last->_str, '.');\r
1081             if (p != NULL)\r
1082             {\r
1083                 result = String::stringWithUTF8String(++p);\r
1084             }\r
1085         }\r
1086     }\r
1087     if (result == NULL)\r
1088     {\r
1089         result = String::stringWithUTF8String("");\r
1090     }\r
1091     return result;\r
1092 }\r
1093 \r
1094 Array *String::componentsSeparatedByString(String *separator)\r
1095 {\r
1096     return componentsSeparatedByString(separator->cString());\r
1097 }\r
1098 \r
1099 Array *String::componentsSeparatedByString(const char *separator)\r
1100 {\r
1101     Array *result = Array::arrayWithCapacity(0);\r
1102     char *tmp = _strdup(_str);\r
1103     char *p1 = tmp;\r
1104     while (true)\r
1105     {\r
1106         char *p2 = strstr(p1, separator);\r
1107         if (p2 != NULL)\r
1108         {\r
1109             *p2 = '\0';\r
1110             result->addObject(String::stringWithUTF8String(p1));\r
1111             p1 = p2 + strlen(separator);\r
1112         }\r
1113         else\r
1114         {\r
1115             result->addObject(String::stringWithUTF8String(p1));\r
1116             break;\r
1117         }\r
1118     }\r
1119     free(tmp);\r
1120     return result;\r
1121 }\r
1122 \r
1123 String *String::substringFromIndex(UInteger anIndex)\r
1124 {\r
1125     String *result = NULL;\r
1126     if (anIndex < _length)\r
1127     {\r
1128         result = String::stringWithUTF8String(&_str[anIndex]);\r
1129     }\r
1130     return result;\r
1131 }\r
1132 \r
1133 String *String::substringToIndex(UInteger anIndex)\r
1134 {\r
1135     String *result = NULL;\r
1136     if (anIndex < _length)\r
1137     {\r
1138         char *tmp = _strdup(_str);\r
1139         if (tmp != NULL)\r
1140         {\r
1141             tmp[anIndex] = '\0';\r
1142             result = String::stringWithUTF8String(tmp);\r
1143             free(tmp);\r
1144         }\r
1145     }\r
1146     else if (anIndex == _length)\r
1147     {\r
1148         result = String::stringWithUTF8String(_str);\r
1149     }\r
1150     return result;\r
1151 }\r
1152 \r
1153 String *String::lowercaseString()\r
1154 {\r
1155     String *result = NULL;\r
1156 #ifdef _WIN32\r
1157     char *tmp = _strdup(_str);\r
1158 #else\r
1159     char *tmp = strdup(_str);\r
1160 #endif\r
1161     if (tmp != NULL)\r
1162     {\r
1163         for (size_t idx = 0; idx < strlen(tmp); ++idx)\r
1164         {\r
1165             tmp[idx] = tolower(tmp[idx]);\r
1166         }\r
1167         result = String::stringWithUTF8String(tmp);\r
1168         free(tmp);\r
1169     }\r
1170     if (result == NULL)\r
1171     {\r
1172         result = String::stringWithUTF8String(_str);\r
1173     }\r
1174     return result;\r
1175 }\r
1176 \r
1177 Range String::rangeOfString(String *aString)\r
1178 {\r
1179     Range result = {NotFound, 0};\r
1180     if (aString != NULL)\r
1181     {\r
1182         result = rangeOfString(aString->cString());\r
1183     }\r
1184     return result;\r
1185 }\r
1186 \r
1187 Range String::rangeOfString(const char *aString)\r
1188 {\r
1189     Range result = {NotFound, 0};\r
1190     if (aString != NULL)\r
1191     {\r
1192         char *p = strstr(_str, aString);\r
1193         if (p != NULL)\r
1194         {\r
1195             result.location = (UInteger)(p - _str);\r
1196             result.length = (UInteger)strlen(aString);\r
1197         }\r
1198     }\r
1199     return result;\r
1200 }\r
1201 \r
1202 int String::intValue()\r
1203 {\r
1204     return atoi(_str);\r
1205 }\r
1206 \r
1207 const char *String::cString()\r
1208 {\r
1209     DebugLog2("String::cString()\n");\r
1210 \r
1211     if (_str == NULL)\r
1212     {\r
1213         DebugLog3("why?\n");\r
1214     }\r
1215 \r
1216     return _str;\r
1217 }\r
1218 \r
1219 const char *String::className()\r
1220 {\r
1221     return "String";\r
1222 }\r
1223 \r
1224 String *String::description()\r
1225 {\r
1226     return this;\r
1227 }\r
1228 \r
1229 } // Raym\r