1 29 30 package com.caucho.quercus.lib.string; 31 32 import com.caucho.quercus.QuercusException; 33 import com.caucho.quercus.QuercusModuleException; 34 import com.caucho.quercus.annotation.NotNull; 35 import com.caucho.quercus.annotation.Optional; 36 import com.caucho.quercus.annotation.Reference; 37 import com.caucho.quercus.annotation.UsesSymbolTable; 38 import com.caucho.quercus.env.*; 39 import com.caucho.quercus.lib.ArrayModule; 40 import com.caucho.quercus.lib.file.BinaryOutput; 41 import com.caucho.quercus.lib.file.FileModule; 42 import com.caucho.quercus.module.AbstractQuercusModule; 43 import com.caucho.util.L10N; 44 import com.caucho.util.RandomUtil; 45 import com.caucho.vfs.ByteToChar; 46 import com.caucho.vfs.Path; 47 48 import java.io.IOException ; 49 import java.io.InputStream ; 50 import java.security.MessageDigest ; 51 import java.text.DecimalFormat ; 52 import java.text.DecimalFormatSymbols ; 53 import java.text.NumberFormat ; 54 import java.util.ArrayList ; 55 import java.util.Iterator ; 56 import java.util.Locale ; 57 import java.util.Map ; 58 import java.util.logging.Level ; 59 import java.util.logging.Logger ; 60 import java.util.zip.CRC32 ; 61 62 65 public class StringModule extends AbstractQuercusModule { 66 private static final Logger log = 67 Logger.getLogger(StringModule.class.getName()); 68 69 private static final L10N L = new L10N(StringModule.class); 70 71 public static final int CRYPT_SALT_LENGTH = 2; 72 public static final int CRYPT_STD_DES = 0; 73 public static final int CRYPT_EXT_DES = 0; 74 public static final int CRYPT_MD5 = 0; 75 public static final int CRYPT_BLOWFISH = 0; 76 77 public static final int CHAR_MAX = 1; 78 79 public static final int LC_CTYPE = 1; 80 public static final int LC_NUMERIC = 2; 81 public static final int LC_TIME = 3; 82 public static final int LC_COLLATE = 4; 83 public static final int LC_MONETARY = 5; 84 public static final int LC_ALL = 6; 85 public static final int LC_MESSAGES = 7; 86 87 public static final int STR_PAD_LEFT = 1; 88 public static final int STR_PAD_RIGHT = 0; 89 public static final int STR_PAD_BOTH = 2; 90 91 private static final DecimalFormatSymbols DEFAULT_DECIMAL_FORMAT_SYMBOLS ; 92 93 102 public static StringValue addcslashes(StringValue source, String characters) 103 { 104 if (characters == null) 105 characters = ""; 106 107 boolean []bitmap = parseCharsetBitmap(characters); 108 109 int length = source.length(); 110 111 StringBuilderValue sb = new StringBuilderValue(length * 5 / 4); 112 113 for (int i = 0; i < length; i++) { 114 char ch = source.charAt(i); 115 116 if (ch >= 256 || ! bitmap[ch]) { 117 sb.append(ch); 118 continue; 119 } 120 121 switch (ch) { 122 case 0x07: 123 sb.append("\\a"); 124 break; 125 case '\b': 126 sb.append("\\b"); 127 break; 128 case '\t': 129 sb.append("\\t"); 130 break; 131 case '\n': 132 sb.append("\\n"); 133 break; 134 case 0xb: 135 sb.append("\\v"); 136 break; 137 case '\f': 138 sb.append("\\f"); 139 break; 140 case '\r': 141 sb.append("\\r"); 142 break; 143 default: 144 if (ch < 0x20 || ch >= 0x7f) { 145 sb.append("\\"); 147 sb.append((char) ('0' + ((ch >> 6) & 7))); 148 sb.append((char) ('0' + ((ch >> 3) & 7))); 149 sb.append((char) ('0' + ((ch) & 7))); 150 break; 151 } 152 else { 153 sb.append("\\"); 154 sb.append(ch); 155 break; 156 } 157 } 158 } 159 160 return sb; 161 } 162 163 169 private static boolean []parseCharsetBitmap(String charset) 170 { 171 boolean []bitmap = new boolean[256]; 172 173 int length = charset.length(); 174 for (int i = 0; i < length; i++) { 175 char ch = charset.charAt(i); 176 177 if (ch >= 256) 179 continue; 180 181 bitmap[ch] = true; 182 183 if (length <= i + 3) 184 continue; 185 186 if (charset.charAt(i + 1) != '.' || charset.charAt(i + 2) != '.') 187 continue; 188 189 char last = charset.charAt(i + 3); 190 191 if (last < ch) { 192 throw new RuntimeException (L.l("Invalid range.")); 194 } 195 196 i += 3; 197 for (; ch <= last; ch++) { 198 bitmap[ch] = true; 199 } 200 201 } 203 204 return bitmap; 205 } 206 207 213 public static StringValue addslashes(StringValue source) 214 { 215 StringBuilderValue sb = new StringBuilderValue(); 216 int length = source.length(); 217 for (int i = 0; i < length; i++) { 218 char ch = source.charAt(i); 219 220 switch (ch) { 221 case 0x0: 222 sb.append("\\0"); 223 break; 224 case '\'': 225 sb.append("\\'"); 226 break; 227 case '\"': 228 sb.append("\\\""); 229 break; 230 case '\\': 231 sb.append("\\\\"); 232 break; 233 default: 234 sb.append(ch); 235 break; 236 } 237 } 238 239 return new StringValueImpl(sb.toString()); 240 } 241 242 245 public static StringValue bin2hex(InputStream is) 246 { 247 try { 248 StringBuilderValue sb = new StringBuilderValue(); 249 250 int ch; 251 while ((ch = is.read()) >= 0) { 252 int d = (ch >> 4) & 0xf; 253 254 if (d < 10) 255 sb.append((char) (d + '0')); 256 else 257 sb.append((char) (d + 'a' - 10)); 258 259 d = (ch) & 0xf; 260 261 if (d < 10) 262 sb.append((char) (d + '0')); 263 else 264 sb.append((char) (d + 'a' - 10)); 265 } 266 267 return sb; 268 } catch (IOException e) { 269 throw new QuercusModuleException(e); 270 } 271 } 272 273 281 public static StringValue chop(Env env, 282 StringValue str, 283 @Optional String charset) 284 { 285 return rtrim(env, str, charset); 286 } 287 288 295 public static String chr(long value) 296 { 297 return String.valueOf((char) value); 298 } 299 300 307 public static String chunk_split(String body, 308 @Optional("76") int chunkLen, 309 @Optional("\"\\r\\n\"") String end) 310 { 311 if (body == null) 312 body = ""; 313 314 if (end == null) 315 end = ""; 316 317 if (chunkLen < 1) throw new IllegalArgumentException (L.l("bad value {0}", chunkLen)); 319 320 StringBuilder sb = new StringBuilder (); 321 322 int i = 0; 323 324 for (; i + chunkLen <= body.length(); i += chunkLen) { 325 sb.append(body.substring(i, i + chunkLen)); 326 sb.append(end); 327 } 328 329 if (i < body.length()) { 330 sb.append(body.substring(i)); 331 sb.append(end); 332 } 333 334 return sb.toString(); 335 } 336 337 343 public static String convert_cyr_string(Env env, 344 String str, 345 String from, 346 String to) 347 { 348 env.stub("convert_cyr_string"); 349 350 return str; 351 } 352 353 public static Value convert_uudecode(Env env, String source) 354 { 355 try { 356 if (source == null || source.length() == 0) 357 return BooleanValue.FALSE; 358 359 ByteToChar byteToChar = env.getByteToChar(); 360 361 int length = source.length(); 362 363 int i = 0; 364 while (i < length) { 365 int ch1 = source.charAt(i++); 366 367 if (ch1 == 0x60 || ch1 == 0x20) 368 break; 369 else if (ch1 < 0x20 || 0x5f < ch1) 370 continue; 371 372 int sublen = ch1 - 0x20; 373 374 while (sublen > 0) { 375 int code; 376 377 code = ((source.charAt(i++) - 0x20) & 0x3f) << 18; 378 code += ((source.charAt(i++) - 0x20) & 0x3f) << 12; 379 code += ((source.charAt(i++) - 0x20) & 0x3f) << 6; 380 code += ((source.charAt(i++) - 0x20) & 0x3f); 381 382 byteToChar.addByte(code >> 16); 383 384 if (sublen > 1) 385 byteToChar.addByte(code >> 8); 386 387 if (sublen > 2) 388 byteToChar.addByte(code); 389 390 sublen -= 3; 391 } 392 } 393 394 return new StringValueImpl(byteToChar.getConvertedString()); 395 } catch (IOException e) { 396 throw new QuercusModuleException(e); 397 } 398 } 399 400 403 public static Value convert_uuencode(String source) 404 { 405 if (source == null || source.length() == 0) 406 return BooleanValue.FALSE; 407 408 StringBuilderValue result = new StringBuilderValue(); 409 410 int i = 0; 411 int length = source.length(); 412 while (i < length) { 413 int sublen = length - i; 414 415 if (45 < sublen) 416 sublen = 45; 417 418 result.append((char) (sublen + 0x20)); 419 420 int end = i + sublen; 421 422 while (i < end) { 423 int code = source.charAt(i++) << 16; 424 425 if (i < length) 426 code += source.charAt(i++) << 8; 427 428 if (i < length) 429 code += source.charAt(i++); 430 431 result.append(toUUChar(((code >> 18) & 0x3f))); 432 result.append(toUUChar(((code >> 12) & 0x3f))); 433 result.append(toUUChar(((code >> 6) & 0x3f))); 434 result.append(toUUChar(((code) & 0x3f))); 435 } 436 437 result.append('\n'); 438 } 439 440 result.append((char) 0x60); 441 result.append('\n'); 442 443 return result; 444 } 445 448 public static Value count_chars(StringValue data, 449 @Optional("0") int mode) 450 { 451 if (data == null) 452 data = StringValue.EMPTY; 453 454 int []count = new int[256]; 455 456 int length = data.length(); 457 458 for (int i = 0; i < length; i++) { 459 count[data.charAt(i) & 0xff] += 1; 460 } 461 462 switch (mode) { 463 case 0: 464 { 465 ArrayValue result = new ArrayValueImpl(); 466 467 for (int i = 0; i < count.length; i++) { 468 result.put(LongValue.create(i), LongValue.create(count[i])); 469 } 470 471 return result; 472 } 473 474 case 1: 475 { 476 ArrayValue result = new ArrayValueImpl(); 477 478 for (int i = 0; i < count.length; i++) { 479 if (count[i] > 0) 480 result.put(LongValue.create(i), new LongValue(count[i])); 481 } 482 483 return result; 484 } 485 486 case 2: 487 { 488 ArrayValue result = new ArrayValueImpl(); 489 490 for (int i = 0; i < count.length; i++) { 491 if (count[i] == 0) 492 result.put(new LongValue(i), new LongValue(count[i])); 493 } 494 495 return result; 496 } 497 498 case 3: 499 { 500 StringBuilder sb = new StringBuilder (); 501 502 for (int i = 0; i < count.length; i++) { 503 if (count[i] > 0) 504 sb.append((char) i); 505 } 506 507 return new StringValueImpl(sb.toString()); 508 } 509 510 case 4: 511 { 512 StringBuilder sb = new StringBuilder (); 513 514 for (int i = 0; i < count.length; i++) { 515 if (count[i] == 0) 516 sb.append((char) i); 517 } 518 519 return new StringValueImpl(sb.toString()); 520 } 521 522 default: 523 return BooleanValue.FALSE; 524 } 525 } 526 527 534 public static long crc32(InputStream is) 535 { 536 try { 537 CRC32 crc = new CRC32 (); 538 539 int ch; 540 while ((ch = is.read()) >= 0) { 541 crc.update((byte) ch); 542 } 543 544 return crc.getValue() & 0xffffffff; 545 } catch (IOException e) { 546 throw new QuercusModuleException(e); 547 } 548 } 549 550 public static String crypt(String string, @Optional String salt) 551 { 552 if (string == null) 553 string = ""; 554 555 if (salt == null || salt.equals("")) { 556 salt = ("" + Crypt.resultToChar(RandomUtil.nextInt(0x40)) + 557 Crypt.resultToChar(RandomUtil.nextInt(0x40))); 558 } 559 560 return Crypt.crypt(string, salt); 561 } 562 563 571 public static Value explode(StringValue separator, 572 StringValue string, 573 @Optional("0x7fffffff") long limit) 574 { 575 if (separator.length() == 0) 576 return BooleanValue.FALSE; 577 578 ArrayValue array = new ArrayValueImpl(); 579 580 int head = 0; 581 int tail; 582 583 int i = 0; 584 while ((tail = string.indexOf(separator, head)) >= 0) { 585 if (limit <= i + 1) 586 break; 587 588 LongValue key = LongValue.create(i++); 589 590 StringValue chunk = string.substring(head, tail); 591 592 array.put(key, chunk); 593 594 head = tail + separator.length(); 595 } 596 597 LongValue key = LongValue.create(i); 598 599 StringValue chunk = string.substring(head); 600 601 array.put(key, chunk); 602 603 return array; 604 } 605 606 612 public static Value fprintf(Env env, 613 @NotNull BinaryOutput os, 614 String format, 615 Value []args) 616 { 617 Value value = sprintf(format, args); 618 619 return FileModule.fwrite(env, os, value.toInputStream(), 620 Integer.MAX_VALUE); 621 } 622 623 631 public static Value implode(Env env, 632 Value glueV, 633 Value piecesV) 634 { 635 StringValue glue; 636 ArrayValue pieces; 637 638 if (piecesV instanceof ArrayValue) { 639 pieces = (ArrayValue) piecesV; 640 glue = glueV.toStringValue(); 641 } 642 else if (glueV instanceof ArrayValue) { 643 pieces = (ArrayValue) glueV; 644 glue = piecesV.toStringValue(); 645 } 646 else { 647 env.error(L.l("neither argument to implode is an array: {0}, {1}", 648 glueV.getClass().getName(), piecesV.getClass().getName())); 649 650 return BooleanValue.FALSE; 651 } 652 653 StringBuilderValue sb = new StringBuilderValue(); 654 boolean isFirst = true; 655 656 for (ArrayValue.Entry entry = pieces.getHead(); 657 entry != null; 658 entry = entry.getNext()) { 659 if (! isFirst) 660 glue.appendTo(sb); 661 662 isFirst = false; 663 664 entry.getValue().appendTo(sb); 665 } 666 667 return sb; 668 } 669 670 678 public static Value join(Env env, 679 Value glueV, 680 Value piecesV) 681 { 682 return implode(env, glueV, piecesV); 683 } 684 685 693 public static StringValue md5(InputStream is, 694 @Optional boolean rawOutput) 695 { 696 try { 697 MessageDigest md = MessageDigest.getInstance("MD5"); 698 699 701 int ch; 702 while ((ch = is.read()) >= 0) { 703 md.update((byte) ch); 704 } 705 706 byte []digest = md.digest(); 707 708 StringBuilderValue sb = new StringBuilderValue(); 709 for (int i = 0; i < digest.length; i++) { 710 int d1 = (digest[i] >> 4) & 0xf; 711 int d2 = (digest[i] & 0xf); 712 713 sb.append(toHexChar(d1)); 714 sb.append(toHexChar(d2)); 715 } 716 717 return sb; 718 } catch (Exception e) { 719 throw new QuercusModuleException(e); 720 } 721 } 722 723 731 public static Value md5_file(Path source, 732 @Optional boolean rawOutput) 733 { 734 try { 735 MessageDigest md = MessageDigest.getInstance("MD5"); 736 InputStream is = null; 737 738 try { 739 is = source.openRead(); 740 int d; 741 742 while ((d = is.read()) >= 0) { 743 md.update((byte) d); 744 } 745 746 return digestToString(md.digest()); 747 } catch (IOException e) { 748 log.log(Level.FINE, e.toString(), e); 749 750 return BooleanValue.FALSE; 751 } finally { 752 try { 753 if (is != null) 754 is.close(); 755 } catch (IOException e) { 756 } 757 } 758 } catch (Exception e) { 759 throw new QuercusModuleException(e); 760 } 761 } 762 763 private static StringValue digestToString(byte []digest) 764 { 765 StringBuilderValue sb = new StringBuilderValue(); 766 for (int i = 0; i < digest.length; i++) { 767 int d1 = (digest[i] >> 4) & 0xf; 768 int d2 = (digest[i] & 0xf); 769 770 sb.append(toHexChar(d1)); 771 sb.append(toHexChar(d2)); 772 } 773 774 return sb; 775 } 776 777 785 public static String money_format(Env env, String format, double value) 786 { 787 Locale monetaryLocale = env.getLocaleInfo().getMonetary(); 788 789 return NumberFormat.getCurrencyInstance(monetaryLocale).format(value); 790 } 791 792 796 public static String metaphone(String string) 797 { 798 if (string == null) 799 string = ""; 800 801 int length = string.length(); 802 int index = 0; 803 char ch = 0; 804 805 for (; index < length; index++) { 807 ch = toUpperCase(string.charAt(index)); 808 809 if ('A' <= ch && ch <= 'Z') 810 break; 811 } 812 813 if (index == length) 814 return ""; 815 816 int lastIndex = length - 1; 817 818 StringBuilder result = new StringBuilder (length); 819 820 822 char nextCh 823 = index < lastIndex 824 ? toUpperCase(string.charAt(index + 1)) 825 : 0; 826 827 switch (ch) { 828 case 'A': 829 if (nextCh == 'E') { 830 result.append('E'); 831 index += 2; 832 } 833 else { 834 result.append('A'); 835 index += 1; 836 } 837 838 break; 839 840 case 'E': 841 case 'I': 842 case 'O': 843 case 'U': 844 result.append(ch); 845 index += 1; 846 break; 847 848 case 'G': 849 case 'K': 850 case 'P': 851 if (nextCh == 'N') { 852 result.append('N'); 853 index += 2; 854 } 855 856 break; 857 858 case 'W': 859 if (nextCh == 'H' || nextCh == 'R') { 860 result.append(nextCh); 861 index += 2; 862 } 863 else { 864 switch (nextCh) { 865 case 'A': 866 case 'E': 867 case 'I': 868 case 'O': 869 case 'U': 870 result.append('W'); 871 index += 2; 872 break; 873 default: 874 break; 875 } 876 } 877 878 break; 879 880 case 'X': 881 result.append('S'); 882 index += 1; 883 break; 884 885 default: 886 break; 887 } 888 889 891 char prevCh; 892 893 for (; index < length; index++) { 894 895 if (index > 0) 896 prevCh = toUpperCase(string.charAt(index - 1)); 897 else 898 prevCh = 0; 899 900 ch = toUpperCase(string.charAt(index)); 901 902 if (ch < 'A' || ch > 'Z') 903 continue; 904 905 if (ch == prevCh && ch != 'C') 906 continue; 907 908 if (index + 1 < length) 909 nextCh = toUpperCase(string.charAt(index + 1)); 910 else 911 nextCh = 0; 912 913 char nextnextCh; 914 915 if (index + 2 < length) 916 nextnextCh = toUpperCase(string.charAt(index + 2)); 917 else 918 nextnextCh = 0; 919 920 921 switch (ch) { 922 case 'B': 923 if (prevCh != 'M') 924 result.append('B'); 925 break; 926 927 case 'C': 928 switch (nextCh) { 929 case 'E': 930 case 'I': 931 case 'Y': 932 if (nextCh == 'I' && nextnextCh == 'A') { 934 result.append('X'); 935 } 936 else if (prevCh == 'S') { 937 } 938 else { 939 result.append('S'); 940 } 941 break; 942 default: 943 if (nextCh == 'H') { 944 result.append('X'); 945 index++; 946 } 947 else { 948 result.append('K'); 949 } 950 break; 951 } 952 953 break; 954 955 case 'D': 956 if (nextCh == 'G') { 957 switch (nextnextCh) { 958 case 'E': 959 case 'I': 960 case 'Y': 961 result.append('J'); 963 index++; 964 break; 965 default: 966 result.append('T'); 967 break; 968 } 969 } 970 else 971 result.append('T'); 972 973 break; 974 975 case 'G': 976 if (nextCh == 'H') { 977 boolean isSilent = false; 978 979 if (index - 3 >= 0) { 980 char prev3Ch = toUpperCase(string.charAt(index - 3)); 981 switch (prev3Ch) { 982 case 'B': 984 case 'D': 985 case 'H': 986 isSilent = true; 987 break; 988 default: 989 break; 990 } 991 } 992 993 if (!isSilent) { 994 if (index - 4 >= 0) { 995 char prev4Ch = toUpperCase(string.charAt(index - 4)); 996 997 isSilent = (prev4Ch == 'H'); 998 } 999 } 1000 1001 if (!isSilent) { 1002 result.append('F'); 1003 index++; 1004 } 1005 } 1006 else if (nextCh == 'N') { 1007 char nextnextnextCh; 1008 1009 if (index + 3 < length) 1010 nextnextnextCh = toUpperCase(string.charAt(index + 3)); 1011 else 1012 nextnextnextCh = 0; 1013 1014 if (nextnextCh < 'A' || nextnextCh > 'Z') { 1015 } 1016 else if (nextnextCh == 'E' && nextnextnextCh == 'D') { 1017 } 1018 else 1019 result.append('K'); 1020 } 1021 else if (prevCh == 'G') { 1022 result.append('K'); 1023 } 1024 else { 1025 switch (nextCh) { 1026 case 'E': 1027 case 'I': 1028 case 'Y': 1029 result.append('J'); 1031 break; 1032 default: 1033 result.append('K'); 1034 break; 1035 } 1036 } 1037 1038 break; 1039 1040 case 'H': 1041 case 'W': 1042 case 'Y': 1043 switch (nextCh) { 1044 case 'A': 1045 case 'E': 1046 case 'I': 1047 case 'O': 1048 case 'U': 1049 1051 if (ch == 'H') { 1052 switch (prevCh) { 1053 case 'C': 1054 case 'G': 1055 case 'P': 1056 case 'S': 1057 case 'T': 1058 break; 1060 default: 1061 result.append('H'); 1062 break; 1063 } 1064 } 1065 else 1066 result.append(ch); 1067 1068 break; 1069 default: 1070 break; 1072 } 1073 1074 break; 1075 1076 case 'K': 1077 if (prevCh != 'C') 1078 result.append('K'); 1079 1080 break; 1081 1082 case 'P': 1083 if (nextCh == 'H') 1084 result.append('F'); 1085 else 1086 result.append('P'); 1087 1088 break; 1089 1090 case 'Q': 1091 result.append('K'); 1092 break; 1093 1094 case 'S': 1095 if (nextCh == 'I' && (nextnextCh == 'O' || nextnextCh == 'A')) { 1096 result.append('X'); 1097 } 1098 else if (nextCh == 'H') { 1099 result.append('X'); 1100 index++; 1101 } 1102 else 1103 result.append('S'); 1104 1105 break; 1106 1107 case 'T': 1108 if (nextCh == 'I' && (nextnextCh == 'O' || nextnextCh == 'A')) { 1109 result.append('X'); 1110 } 1111 else if (nextCh == 'H') { 1112 result.append('0'); 1113 index++; 1114 } 1115 else 1116 result.append('T'); 1117 1118 break; 1119 1120 case 'V': 1121 result.append('F'); 1122 1123 break; 1124 1125 case 'X': 1126 result.append('K'); 1127 result.append('S'); 1128 break; 1129 1130 case 'Z': 1131 result.append('S'); 1132 break; 1133 1134 case 'F': 1135 case 'J': 1136 case 'L': 1137 case 'M': 1138 case 'N': 1139 case 'R': 1140 result.append(ch); 1141 break; 1142 1143 default: 1144 break; 1145 } 1146 } 1147 1148 return result.toString(); 1149 } 1150 1151 1161 public static String number_format(Env env, 1162 double value, 1163 @Optional int decimals, 1164 @Optional Value pointValue, 1165 @Optional Value groupValue) 1166 { 1167 boolean isGroupDefault = (groupValue instanceof DefaultValue); 1168 boolean isPointDefault = (pointValue instanceof DefaultValue); 1169 1170 if (!isPointDefault && isGroupDefault) { 1171 env.warning(L.l("wrong parameter count")); 1172 return null; 1173 } 1174 1175 String pattern; 1176 1177 char point = '.'; 1178 1179 if (!pointValue.isNull()) { 1180 String pointString = pointValue.toString(); 1181 1182 point = (pointString.length() == 0) ? 0 : pointString.charAt(0); 1183 } 1184 1185 char group = ','; 1186 1187 if (!groupValue.isNull()) { 1188 String groupString = groupValue.toString(); 1189 1190 group = (groupString.length() == 0) ? 0 : groupString.charAt(0); 1191 } 1192 1193 if (decimals > 0) { 1194 StringBuilder patternBuilder = new StringBuilder (6 + decimals); 1195 1196 patternBuilder.append(group == 0 ? "###0." : "#,##0."); 1197 1198 for (int i = 0; i < decimals; i++) 1199 patternBuilder.append('0'); 1200 1201 pattern = patternBuilder.toString(); 1202 } 1203 else { 1204 pattern = group == 0 ? "###0" : "#,##0"; 1205 } 1206 1207 DecimalFormatSymbols decimalFormatSymbols; 1208 1209 if (point == '.' && group == ',') 1210 decimalFormatSymbols = DEFAULT_DECIMAL_FORMAT_SYMBOLS; 1211 else { 1212 decimalFormatSymbols = new DecimalFormatSymbols (); 1213 decimalFormatSymbols.setDecimalSeparator(point); 1214 decimalFormatSymbols.setGroupingSeparator(group); 1215 decimalFormatSymbols.setZeroDigit('0'); 1216 } 1217 1218 DecimalFormat format = new DecimalFormat (pattern, decimalFormatSymbols); 1219 1220 String result = format.format(value); 1221 1222 if (point == 0 && decimals > 0) { 1223 int i = result.lastIndexOf(point); 1226 1227 return result.substring(0, i) + result.substring(i + 1, result.length()); 1228 } 1229 else 1230 return result; 1231 } 1232 1233 1240 public static long ord(StringValue string) 1241 { 1242 if (string.length() == 0) 1243 return 0; 1244 else 1245 return string.charAt(0); 1246 } 1247 1248 1255 @UsesSymbolTable 1256 public static Value parse_str(Env env, String str, 1257 @Optional @Reference Value ref) 1258 { 1259 if (str == null) 1260 str = ""; 1261 1262 try { 1263 ByteToChar byteToChar = env.getByteToChar(); 1264 int len = str.length(); 1265 1266 boolean isRef = ref instanceof Var; 1267 1268 ArrayValue result = null; 1269 1270 if (isRef) { 1271 result = new ArrayValueImpl(); 1272 ref.set(result); 1273 } 1274 else if (ref instanceof ArrayValue) { 1275 result = (ArrayValue) ref; 1276 isRef = true; 1277 } 1278 else 1279 result = new ArrayValueImpl(); 1280 1281 for (int i = 0; i < len; i++) { 1282 int ch = 0; 1283 byteToChar.clear(); 1284 1285 for (; i < len && (ch = str.charAt(i)) == '&'; i++) { 1286 } 1287 1288 for (; i < len && (ch = str.charAt(i)) != '='; i++) { 1289 i = addQueryChar(byteToChar, str, len, i, ch); 1290 } 1291 1292 String key = byteToChar.getConvertedString(); 1293 1294 byteToChar.clear(); 1295 1296 String value; 1297 if (ch == '=') { 1298 for (i++; i < len && (ch = str.charAt(i)) != '&'; i++) { 1299 i = addQueryChar(byteToChar, str, len, i, ch); 1300 } 1301 1302 value = byteToChar.getConvertedString(); 1303 } 1304 else 1305 value = ""; 1306 1307 if (isRef) { 1308 Post.addFormValue(result, key, new String [] { value }, env.getIniBoolean("magic_quotes_gpc")); 1309 } else { 1310 int openBracketIndex = key.indexOf('['); 1314 int closeBracketIndex = key.indexOf(']'); 1315 if (openBracketIndex > 0) { 1316 Value v = env.getVar(key.substring(0,openBracketIndex)).getRawValue(); 1317 if (v instanceof ArrayValue) { 1318 if (closeBracketIndex < 0) { 1320 env.warning(L.l("invalid array " + key)); 1321 return NullValue.NULL; 1322 } 1323 if (closeBracketIndex > openBracketIndex + 1) { 1324 String index = key.substring(key.indexOf('[') + 1,key.indexOf(']')); 1325 v.put(new StringValueImpl(index), new StringValueImpl(value)); 1326 } else { 1327 v.put(new StringValueImpl(value)); 1328 } 1329 } else { 1330 Post.addFormValue(result, key, new String [] { value }, env.getIniBoolean("magic_quotes_gpc")); 1331 } 1332 } else { 1333 Post.addFormValue(result, key, new String [] { value }, env.getIniBoolean("magic_quotes_gpc")); 1334 } 1335 } 1336 } 1337 1338 if (! isRef) { 1339 ArrayModule.extract(env, result, 1340 ArrayModule.EXTR_OVERWRITE, 1341 null); 1342 } 1343 1344 return NullValue.NULL; 1345 } catch (IOException e) { 1346 throw new QuercusModuleException(e); 1347 } 1348 } 1349 1350 private static int addQueryChar(ByteToChar byteToChar, String str, int len, 1351 int i, int ch) 1352 throws IOException 1353 { 1354 if (str == null) 1355 str = ""; 1356 1357 switch (ch) { 1358 case '+': 1359 byteToChar.addChar(' '); 1360 return i; 1361 1362 case '%': 1363 if (i + 2 < len) { 1364 int d1 = hexToDigit(str.charAt(i + 1)); 1365 int d2 = hexToDigit(str.charAt(i + 2)); 1366 1367 byteToChar.addByte(d1 * 16 + d2); 1369 1370 return i + 2; 1371 } 1372 else { 1373 byteToChar.addByte((byte) ch); 1374 return i; 1375 } 1376 1377 default: 1378 byteToChar.addByte((byte) ch); 1379 return i; 1380 } 1381 } 1382 1383 public static void addQueryValue(Env env, ArrayValue array, 1384 String key, String valueStr) 1385 { 1386 if (key == null) 1387 key = ""; 1388 1389 if (valueStr == null) 1390 valueStr = ""; 1391 1392 int p; 1393 1394 Value value = new StringValueImpl(valueStr); 1395 1396 if ((p = key.indexOf('[')) > 0 && key.endsWith("]")) { 1397 String index = key.substring(p + 1, key.length() - 1); 1398 key = key.substring(0, p); 1399 1400 Value keyValue = new StringValueImpl(key); 1401 1402 Value part; 1403 1404 if (array != null) 1405 part = array.get(keyValue); 1406 else 1407 part = env.getVar(key); 1408 1409 if (! part.isArray()) 1410 part = new ArrayValueImpl(); 1411 1412 if (index.equals("")) 1413 part.put(value); 1414 else 1415 part.put(new StringValueImpl(index), value); 1416 1417 if (array != null) 1418 array.put(keyValue, part); 1419 else 1420 env.setVar(key, part); 1421 } 1422 else { 1423 if (array != null) 1424 array.put(new StringValueImpl(key), value); 1425 else 1426 env.setVar(key, value); 1427 } 1428 } 1429 1430 1436 public static long print(Env env, Value value) 1437 { 1438 value.print(env); 1439 1440 return 1; 1441 } 1442 1443 1450 public static Value quotemeta(StringValue string) 1451 { 1452 StringBuilderValue sb = new StringBuilderValue(); 1453 1454 int len = string.length(); 1455 for (int i = 0; i < len; i++) { 1456 char ch = string.charAt(i); 1457 1458 switch (ch) { 1459 case '.': case '\\': case '+': case '*': case '?': 1460 case '[': case '^': case ']': case '(': case ')': case '$': 1461 sb.append("\\"); 1462 sb.append(ch); 1463 break; 1464 default: 1465 sb.append(ch); 1466 break; 1467 } 1468 } 1469 1470 return sb; 1471 } 1472 1473 1476 public static String quoted_printable_decode(String str) 1478 { 1479 if (str == null) 1480 str = ""; 1481 1482 StringBuilder sb = new StringBuilder (); 1483 1484 int length = str.length(); 1485 1486 for (int i = 0; i < length; i++) { 1487 char ch = str.charAt(i); 1488 1489 if (33 <= ch && ch <= 60) 1490 sb.append(ch); 1491 else if (62 <= ch && ch <= 126) 1492 sb.append(ch); 1493 else if (ch == ' ' || ch == '\t') { 1494 if (i + 1 < str.length() && 1495 (str.charAt(i + 1) == '\r' || str.charAt(i + 1) == '\n')) { 1496 sb.append('='); 1497 sb.append(toUpperHexChar(ch >> 4)); 1498 sb.append(toUpperHexChar(ch)); 1499 } 1500 else 1501 sb.append(ch); 1502 } 1503 else if (ch == '\r' || ch == '\n') { 1504 sb.append(ch); 1505 } 1506 else { 1507 sb.append('='); 1508 sb.append(toUpperHexChar(ch >> 4)); 1509 sb.append(toUpperHexChar(ch)); 1510 } 1511 } 1512 1513 return sb.toString(); 1514 } 1515 1516 private static final boolean[]TRIM_WHITESPACE = new boolean[256]; 1517 1518 static { 1519 TRIM_WHITESPACE['\0'] = true; 1520 TRIM_WHITESPACE['\b'] = true; 1521 TRIM_WHITESPACE[' '] = true; 1522 TRIM_WHITESPACE['\t'] = true; 1523 TRIM_WHITESPACE['\r'] = true; 1524 TRIM_WHITESPACE['\n'] = true; 1525 } 1526 1527 1534 public static StringValue ltrim(Env env, 1535 StringValue string, 1536 @Optional String characters) 1537 { 1538 if (characters == null) 1539 characters = ""; 1540 1541 boolean []trim; 1542 1543 if (characters.equals("")) 1544 trim = TRIM_WHITESPACE; 1545 else 1546 trim = parseCharsetBitmap(characters); 1547 1548 for (int i = 0; i < string.length(); i++) { 1549 char ch = string.charAt(i); 1550 1551 if (ch >= 256 || ! trim[ch]) { 1552 if (i == 0) 1553 return string; 1554 else 1555 return string.substring(i); 1556 } 1557 } 1558 1559 return StringValue.EMPTY; 1560 } 1561 1562 1570 public static StringValue rtrim(Env env, 1571 StringValue string, 1572 @Optional String characters) 1573 { 1574 if (characters == null) 1575 characters = ""; 1576 1577 boolean []trim; 1578 1579 if (characters.equals("")) 1580 trim = TRIM_WHITESPACE; 1581 else 1582 trim = parseCharsetBitmap(characters); 1583 1584 for (int i = string.length() - 1; i >= 0; i--) { 1585 char ch = string.charAt(i); 1586 1587 if (ch >= 256 || ! trim[ch]) { 1588 if (i == string.length()) 1589 return string; 1590 else 1591 return (StringValue) string.subSequence(0, i + 1); 1592 } 1593 } 1594 1595 return StringValue.EMPTY; 1596 } 1597 1598 1601 public static Value setlocale(Env env, 1602 int category, 1603 Value localeArg, 1604 Value []fallback) 1605 { 1606 LocaleInfo localeInfo = env.getLocaleInfo(); 1607 1608 if (localeArg instanceof ArrayValue) { 1609 for (Value value : ((ArrayValue) localeArg).values()) { 1610 Locale locale = setLocale(localeInfo, category, value.toString()); 1611 1612 if (locale != null) 1613 return new StringValueImpl(locale.toString()); 1614 } 1615 } 1616 else { 1617 Locale locale = setLocale(localeInfo, category, localeArg.toString()); 1618 1619 if (locale != null) 1620 return new StringValueImpl(locale.toString()); 1621 } 1622 1623 for (int i = 0; i < fallback.length; i++) { 1624 Locale locale = setLocale(localeInfo, category, fallback[i].toString()); 1625 1626 if (locale != null) 1627 return new StringValueImpl(locale.toString()); 1628 } 1629 1630 return BooleanValue.FALSE; 1631 } 1632 1633 1636 private static Locale setLocale(LocaleInfo localeInfo, 1637 int category, 1638 String localeName) 1639 { 1640 String language; 1641 String country; 1642 String variant; 1643 1644 int p = localeName.indexOf('_'); 1645 int p1 = localeName.indexOf('-'); 1646 1647 if (p1 > 0 && (p1 < p || p < 0)) 1648 p = p1; 1649 1650 Locale locale; 1651 1652 if (p > 0) { 1653 language = localeName.substring(0, p); 1654 1655 int q = localeName.indexOf('-', p + 1); 1656 int q1 = localeName.indexOf('.', p + 1); 1657 1659 if (q1 > 0 && (q1 < q || q < 0)) 1660 q = q1; 1661 1662 q1 = localeName.indexOf('@', p + 1); 1663 1665 if (q1 > 0 && (q1 < q || q < 0)) 1666 q = q1; 1667 1668 q1 = localeName.indexOf('_', p + 1); 1669 1670 if (q1 > 0 && (q1 < q || q < 0)) 1671 q = q1; 1672 1673 if (q > 0) { 1674 country = localeName.substring(p + 1, q); 1675 variant = localeName.substring(q + 1); 1676 1677 locale = new Locale (language, country, variant); 1678 } 1679 else { 1680 country = localeName.substring(p + 1); 1681 1682 locale = new Locale (language, country); 1683 } 1684 } 1685 else 1686 locale = new Locale (localeName); 1687 1688 if (! isValidLocale(locale)) 1689 return null; 1690 1691 switch (category) { 1692 case LC_ALL: 1693 localeInfo.setAll(locale); 1694 return localeInfo.getMessages(); 1695 case LC_COLLATE: 1696 localeInfo.setCollate(locale); 1697 return localeInfo.getCollate(); 1698 case LC_CTYPE: 1699 localeInfo.setCtype(locale); 1700 return localeInfo.getCtype(); 1701 case LC_MONETARY: 1702 localeInfo.setMonetary(locale); 1703 return localeInfo.getMonetary(); 1704 case LC_NUMERIC: 1705 localeInfo.setNumeric(locale); 1706 return localeInfo.getNumeric(); 1707 case LC_TIME: 1708 localeInfo.setTime(locale); 1709 return localeInfo.getTime(); 1710 case LC_MESSAGES: 1711 localeInfo.setMessages(locale); 1712 return localeInfo.getMessages(); 1713 default: 1714 return null; 1715 } 1716 } 1717 1718 1721 private static boolean isValidLocale(Locale locale) 1722 { 1723 Locale []validLocales = Locale.getAvailableLocales(); 1724 1725 for (int i = 0; i < validLocales.length; i++) { 1726 if (validLocales[i].equals(locale)) { 1727 return true; 1728 } 1729 } 1730 1731 return false; 1732 } 1733 1734 1742 public static String sha1(String source, 1743 @Optional boolean rawOutput) 1744 { 1745 if (source == null) 1746 source = ""; 1747 1748 try { 1749 MessageDigest md = MessageDigest.getInstance("SHA1"); 1750 1751 1753 for (int i = 0; i < source.length(); i++) { 1754 char ch = source.charAt(i); 1755 1756 md.update((byte) ch); 1757 } 1758 1759 byte []digest = md.digest(); 1760 1761 StringBuilder sb = new StringBuilder (); 1762 for (int i = 0; i < digest.length; i++) { 1763 int d1 = (digest[i] >> 4) & 0xf; 1764 int d2 = (digest[i] & 0xf); 1765 1766 sb.append(toHexChar(d1)); 1767 sb.append(toHexChar(d2)); 1768 } 1769 1770 return sb.toString(); 1771 } catch (Exception e) { 1772 throw new QuercusException(e); 1773 } 1774 } 1775 1776 1784 public static Value sha1_file(Path source, 1785 @Optional boolean rawOutput) 1786 { 1787 try { 1788 MessageDigest md = MessageDigest.getInstance("SHA1"); 1789 InputStream is = null; 1790 1791 try { 1792 is = source.openRead(); 1793 int d; 1794 1795 while ((d = is.read()) >= 0) { 1796 md.update((byte) d); 1797 } 1798 1799 return digestToString(md.digest()); 1800 } catch (IOException e) { 1801 log.log(Level.FINE, e.toString(), e); 1802 1803 return BooleanValue.FALSE; 1804 } finally { 1805 try { 1806 if (is != null) 1807 is.close(); 1808 } catch (IOException e) { 1809 } 1810 } 1811 } catch (Exception e) { 1812 throw new QuercusException(e); 1813 } 1814 } 1815 1816 1824 public static Value sscanf(StringValue string, 1825 StringValue format, 1826 @Optional Value []args) 1827 { 1828 1830 int fmtLen = format.length(); 1831 int strlen = string.length(); 1832 1833 int sIndex = 0; 1834 int fIndex = 0; 1835 1836 ArrayValue array = new ArrayValueImpl(); 1837 1838 while (fIndex < fmtLen && sIndex < strlen) { 1839 char ch = format.charAt(fIndex++); 1840 1841 if (isWhitespace(ch)) { 1842 for (; 1843 (fIndex < fmtLen && 1844 isWhitespace(ch = format.charAt(fIndex))); 1845 fIndex++) { 1846 } 1847 1848 ch = string.charAt(sIndex); 1849 if (! isWhitespace(ch)) { 1850 return array; } 1852 1853 for (sIndex++; 1854 sIndex < strlen && isWhitespace(string.charAt(sIndex)); 1855 sIndex++) { 1856 } 1857 } 1858 else if (ch == '%') { 1859 int maxLen = -1; 1860 boolean suppressAssignment = false; 1861 1862 loop: 1863 while (fIndex < fmtLen) { 1864 ch = format.charAt(fIndex++); 1865 1866 switch (ch) { 1867 case '%': 1868 if (string.charAt(sIndex) != '%') 1869 return array; 1870 else 1871 break loop; 1872 1873 case '0': case '1': case '2': case '3': case '4': 1874 case '5': case '6': case '7': case '8': case '9': 1875 if (maxLen < 0) 1876 maxLen = 0; 1877 1878 maxLen = 10 * maxLen + ch - '0'; 1879 break; 1880 1881 case 's': 1882 sIndex = sscanfString(string, sIndex, maxLen, array); 1883 break loop; 1884 1885 default: 1886 log.fine(L.l("'{0}' is a bad sscanf string.", format)); 1887 return array; 1888 } 1889 } 1890 } 1891 else if (ch == string.charAt(sIndex)) { 1892 sIndex++; 1893 } 1894 else 1895 return array; 1896 } 1897 1898 return array; 1899 } 1900 1901 1904 private static int sscanfString(StringValue string, int sIndex, int maxLen, 1905 ArrayValue array) 1906 { 1907 int strlen = string.length(); 1909 1910 if (maxLen < 0) 1911 maxLen = Integer.MAX_VALUE; 1912 1913 StringBuilderValue sb = new StringBuilderValue(); 1914 1915 for (; sIndex < strlen && maxLen-- > 0; sIndex++) { 1916 char ch = string.charAt(sIndex); 1917 1918 if (isWhitespace(ch)) { 1919 array.append(sb); 1920 return sIndex; 1921 } 1922 else 1923 sb.append(ch); 1924 } 1925 1926 array.append(sb); 1927 1928 return sIndex; 1929 } 1930 1931 1940 public static int printf(Env env, String format, Value []args) 1941 { 1942 Value str = sprintf(format, args); 1943 1944 str.print(env); 1945 1946 return str.length(); 1947 } 1948 1949 private static final char[] SOUNDEX_VALUES = "01230120022455012623010202".toCharArray(); 1950 1951 public static Value soundex(String string) 1952 { 1953 if (string == null) 1954 string = ""; 1955 1956 int length = string.length(); 1957 1958 if (length == 0) 1959 return BooleanValue.FALSE; 1960 1961 StringBuilder result = new StringBuilder (4); 1962 1963 int count = 0; 1964 char lastCode = 0; 1965 1966 1967 for (int i = 0; i < length && count < 4; i++) { 1968 char ch = toUpperCase(string.charAt(i)); 1969 1970 if ('A' <= ch && ch <= 'Z') { 1971 char code = SOUNDEX_VALUES[ch - 'A']; 1972 1973 if (count == 0) { 1974 result.append(ch); 1975 count++; 1976 } 1977 else if (code != '0' && code != lastCode) { 1978 result.append(code); 1979 count++; 1980 } 1981 1982 lastCode = code; 1983 } 1984 } 1985 1986 for (; count < 4; count++) { 1987 result.append('0'); 1988 } 1989 1990 return new StringValueImpl(result.toString()); 1991 } 1992 1993 2001 public static Value sprintf(String format, Value []args) 2002 { 2003 if (format == null) 2004 format = ""; 2005 2006 ArrayList <PrintfSegment> segments = parsePrintfFormat(format); 2007 2008 StringBuilderValue sb = new StringBuilderValue(); 2009 2010 for (PrintfSegment segment : segments) 2011 segment.apply(sb, args); 2012 2013 return sb; 2014 } 2015 2016 private static ArrayList <PrintfSegment> parsePrintfFormat(String format) 2017 { 2018 ArrayList <PrintfSegment> segments = new ArrayList <PrintfSegment>(); 2019 2020 StringBuilder sb = new StringBuilder (); 2021 StringBuilder flags = new StringBuilder (); 2022 2023 int length = format.length(); 2024 int index = 0; 2025 2026 for (int i = 0; i < length; i++) { 2027 char ch = format.charAt(i); 2028 2029 if (i + 1 < length && ch == '%') { 2030 2033 sb.append(ch); 2034 2035 boolean isLeft = false; 2036 boolean isAlt = false; 2037 boolean isZero = false; 2038 2039 flags.setLength(0); 2040 2041 int j = i + 1; 2042 2043 loop: 2044 for (; j < length; j++) { 2045 ch = format.charAt(j); 2046 2047 switch (ch) { 2048 case '-': 2049 isLeft = true; 2050 break; 2051 case '#': 2052 isAlt = true; 2053 break; 2054 case '0': 2055 isZero = true; 2056 flags.append(ch); 2057 break; 2058 case '+': case ' ': case ',': case '(': 2059 flags.append(ch); 2060 break; 2061 default: 2062 break loop; 2063 } 2064 } 2065 2066 int head = j; 2067 loop: 2068 for (; j < length; j++) { 2069 ch = format.charAt(j); 2070 2071 switch (ch) { 2072 case '%': 2073 i = j; 2074 segments.add(new TextPrintfSegment(sb)); 2075 sb.setLength(0); 2076 break loop; 2077 2078 case '0': case '1': case '2': case '3': case '4': 2079 case '5': case '6': case '7': case '8': case '9': 2080 case '.': case '$': 2081 break; 2082 2083 case 'b': case 'B': 2084 if (isLeft) 2085 sb.append('-'); 2086 if (isAlt) 2087 sb.append('#'); 2088 sb.append(format, head, j); 2089 sb.append(ch); 2090 i = j; 2091 break loop; 2092 2093 case 's': case 'S': 2094 sb.setLength(sb.length() - 1); 2095 segments.add(new StringPrintfSegment(sb, 2096 isLeft || isAlt, 2097 isZero, 2098 ch == 'S', 2099 format.substring(head, j), 2100 index++)); 2101 sb.setLength(0); 2102 i = j; 2103 break loop; 2104 2105 case 'c': case 'C': 2106 sb.setLength(sb.length() - 1); 2107 segments.add(new CharPrintfSegment(sb, 2108 isLeft || isAlt, 2109 isZero, 2110 ch == 'C', 2111 format.substring(head, j), 2112 index++)); 2113 sb.setLength(0); 2114 i = j; 2115 break loop; 2116 2117 case 'i': case 'u': 2118 ch = 'd'; 2119 case 'd': case 'x': case 'o': case 'X': 2120 if (isLeft) 2121 sb.append('-'); 2122 if (isAlt) 2123 sb.append('#'); 2124 sb.append(flags); 2125 sb.append(format, head, j); 2126 sb.append(ch); 2127 2128 segments.add(new LongPrintfSegment(sb.toString(), index++)); 2129 sb.setLength(0); 2130 i = j; 2131 break loop; 2132 2133 case 'e': case 'E': case 'f': case 'g': case 'G': 2134 if (isLeft) 2135 sb.append('-'); 2136 if (isAlt) 2137 sb.append('#'); 2138 sb.append(flags); 2139 sb.append(format, head, j); 2140 sb.append(ch); 2141 2142 segments.add(new DoublePrintfSegment(sb.toString(), index++)); 2143 sb.setLength(0); 2144 i = j; 2145 break loop; 2146 2147 default: 2148 if (isLeft) 2149 sb.append('-'); 2150 if (isAlt) 2151 sb.append('#'); 2152 sb.append(flags); 2153 sb.append(format, head, j); 2154 sb.append(ch); 2155 i = j; 2156 break loop; 2157 } 2158 } 2159 } else 2160 sb.append(ch); 2161 } 2162 2163 if (sb.length() > 0) 2164 segments.add(new TextPrintfSegment(sb)); 2165 2166 return segments; 2167 } 2168 2169 2177 public static Value str_ireplace(Env env, 2178 Value search, 2179 Value replace, 2180 Value subject, 2181 @Reference @Optional Value count) 2182 { 2183 return strReplace(env, search, replace, subject, count, true); 2184 } 2185 2186 2194 public static StringValue str_pad(StringValue string, 2195 int length, 2196 @Optional("' '") String pad, 2197 @Optional("STR_PAD_RIGHT") int type) 2198 { 2199 int strLen = string.length(); 2200 int padLen = length - strLen; 2201 2202 if (padLen <= 0) 2203 return string; 2204 2205 if (pad == null || pad.length() == 0) 2206 pad = " "; 2207 2208 int leftPad = 0; 2209 int rightPad = 0; 2210 2211 switch (type) { 2212 case STR_PAD_LEFT: 2213 leftPad = padLen; 2214 break; 2215 case STR_PAD_RIGHT: 2216 default: 2217 rightPad = padLen; 2218 break; 2219 case STR_PAD_BOTH: 2220 leftPad = padLen / 2; 2221 rightPad = padLen - leftPad; 2222 break; 2223 } 2224 2225 StringBuilderValue sb = new StringBuilderValue(); 2226 2227 int padStringLen = pad.length(); 2228 2229 for (int i = 0; i < leftPad; i++) 2230 sb.append(pad.charAt(i % padStringLen)); 2231 2232 sb.append(string); 2233 2234 for (int i = 0; i < rightPad; i++) 2235 sb.append(pad.charAt(i % padStringLen)); 2236 2237 return sb; 2238 } 2239 2240 2246 public static Value str_repeat(StringValue string, int count) 2247 { 2248 StringBuilderValue sb = new StringBuilderValue(); 2249 2250 for (int i = 0; i < count; i++) 2251 sb.append(string); 2252 2253 return sb; 2254 } 2255 2256 2264 public static Value str_replace(Env env, 2265 Value search, 2266 Value replace, 2267 Value subject, 2268 @Reference @Optional Value count) 2269 { 2270 return strReplace(env, search, replace, subject, count, false); 2271 } 2272 2273 2281 private static Value strReplace(Env env, 2282 Value search, 2283 Value replace, 2284 Value subject, 2285 @Reference @Optional Value count, 2286 boolean isInsensitive) 2287 { 2288 count.set(LongValue.ZERO); 2289 2290 if (subject.isNull()) 2291 return StringValue.EMPTY; 2292 2293 if (search.isNull()) 2294 return subject; 2295 2296 if (subject instanceof ArrayValue) { 2297 ArrayValue subjectArray = (ArrayValue) subject; 2298 ArrayValue resultArray = new ArrayValueImpl(); 2299 2300 for (Value value : subjectArray.values()) { 2301 Value result = strReplaceImpl(env, 2302 search, 2303 replace, 2304 value.toStringValue(), 2305 count, 2306 isInsensitive); 2307 2308 resultArray.append(result); 2309 } 2310 2311 return resultArray; 2312 } 2313 else { 2314 StringValue subjectString = subject.toStringValue(); 2315 2316 if (subjectString.length() == 0) 2317 return StringValue.EMPTY; 2318 2319 return strReplaceImpl(env, 2320 search, 2321 replace, 2322 subjectString, 2323 count, 2324 isInsensitive); 2325 } 2326 } 2327 2328 2336 private static Value strReplaceImpl(Env env, 2337 Value search, 2338 Value replace, 2339 StringValue subject, 2340 Value count, 2341 boolean isInsensitive) 2342 { 2343 if (! search.isArray()) { 2344 StringValue searchString = search.toStringValue(); 2345 2346 if (searchString.length() == 0) 2347 return subject; 2348 2349 if (replace instanceof ArrayValue) { 2350 env.warning(L.l("Array to string conversion")); 2351 } 2352 2353 subject = strReplaceImpl(env, 2354 searchString, 2355 replace.toStringValue(), 2356 subject, 2357 count, 2358 isInsensitive); 2359 } 2360 else if (replace instanceof ArrayValue) { 2361 ArrayValue searchArray = (ArrayValue) search; 2362 ArrayValue replaceArray = (ArrayValue) replace; 2363 2364 Iterator <Value> searchIter = searchArray.values().iterator(); 2365 Iterator <Value> replaceIter = replaceArray.values().iterator(); 2366 2367 while (searchIter.hasNext()) { 2368 Value searchItem = searchIter.next(); 2369 Value replaceItem = replaceIter.next(); 2370 2371 if (replaceItem == null) 2372 replaceItem = NullValue.NULL; 2373 2374 subject = strReplaceImpl(env, 2375 searchItem.toStringValue(), 2376 replaceItem.toStringValue(), 2377 subject, 2378 count, 2379 isInsensitive); 2380 } 2381 } 2382 else { 2383 ArrayValue searchArray = (ArrayValue) search; 2384 2385 Iterator <Value> searchIter = searchArray.values().iterator(); 2386 2387 while (searchIter.hasNext()) { 2388 Value searchItem = searchIter.next(); 2389 2390 subject = strReplaceImpl(env, 2391 searchItem.toStringValue(), 2392 replace.toStringValue(), 2393 subject, 2394 count, 2395 isInsensitive); 2396 } 2397 } 2398 2399 return subject; 2400 } 2401 2402 2410 private static StringValue strReplaceImpl(Env env, 2411 StringValue search, 2412 StringValue replace, 2413 StringValue subject, 2414 Value countV, 2415 boolean isInsensitive) 2416 { 2417 long count = countV.toLong(); 2418 2419 int head = 0; 2420 int next; 2421 2422 int searchLen = search.length(); 2423 2424 StringBuilderValue result = null; 2425 2426 while ((next = indexOf(subject, search, head, isInsensitive)) >= head) { 2427 if (result == null) 2428 result = new StringBuilderValue(); 2429 2430 result.append(subject, head, next); 2431 result.append(replace); 2432 2433 if (head < next + searchLen) 2434 head = next + searchLen; 2435 else 2436 head += 1; 2437 2438 count++; 2439 } 2440 2441 if (count != 0) { 2442 countV.set(LongValue.create(count)); 2443 2444 if (head > 0 && head < subject.length()) 2445 result.append(subject, head, subject.length()); 2446 2447 return result; 2448 } 2449 else 2450 return subject; 2451 } 2452 2453 2456 private static int indexOf(StringValue subject, 2457 StringValue match, 2458 int head, 2459 boolean isInsensitive) 2460 { 2461 if (! isInsensitive) 2462 return subject.indexOf(match, head); 2463 else { 2464 int length = subject.length(); 2465 int matchLen = match.length(); 2466 2467 if (matchLen <= 0) 2468 return -1; 2469 2470 char ch = Character.toLowerCase(match.charAt(0)); 2471 loop: 2472 for (; head + matchLen <= length; head++) { 2473 if (ch == Character.toLowerCase(subject.charAt(head))) { 2474 for (int i = 1; i < matchLen; i++) { 2475 if (Character.toLowerCase(subject.charAt(head + i)) != 2476 Character.toLowerCase(match.charAt(i))) 2477 continue loop; 2478 } 2479 2480 return head; 2481 } 2482 } 2483 2484 return -1; 2485 } 2486 } 2487 2488 2493 public static Value str_rot13(String string) 2494 { 2495 if (string == null) 2496 string = ""; 2497 2498 StringBuilder sb = new StringBuilder (); 2499 2500 int len = string.length(); 2501 for (int i = 0; i < len; i++) { 2502 char ch = string.charAt(i); 2503 2504 if ('a' <= ch && ch <= 'z') { 2505 int off = ch - 'a'; 2506 2507 sb.append((char) ('a' + (off + 13) % 26)); 2508 } 2509 else if ('A' <= ch && ch <= 'Z') { 2510 int off = ch - 'A'; 2511 2512 sb.append((char) ('A' + (off + 13) % 26)); 2513 } 2514 else { 2515 sb.append(ch); 2516 } 2517 } 2518 2519 return new StringValueImpl(sb.toString()); 2520 } 2521 2522 2525 public static String str_shuffle(String string) 2526 { 2527 if (string == null) 2528 string = ""; 2529 2530 char []chars = string.toCharArray(); 2531 2532 int length = chars.length; 2533 2534 for (int i = 0; i < length; i++) { 2535 int rand = RandomUtil.nextInt(length); 2536 2537 char temp = chars[rand]; 2538 chars[rand] = chars[i]; 2539 chars[i] = temp; 2540 } 2541 2542 return new String (chars); 2543 } 2544 2545 2551 public static Value str_split(String string, 2552 @Optional("1") int chunk) 2553 { 2554 if (string == null) 2555 string = ""; 2556 2557 ArrayValue array = new ArrayValueImpl(); 2558 2559 int strLen = string.length(); 2560 2561 for (int i = 0; i < strLen; i += chunk) { 2562 Value value; 2563 2564 if (i + chunk <= strLen) { 2565 value = new StringValueImpl(string.substring(i, i + chunk)); 2566 } else { 2567 value = new StringValueImpl(string.substring(i)); 2568 } 2569 2570 array.put(new LongValue(i), value); 2571 } 2572 2573 return array; 2574 } 2575 2576 public static Value str_word_count(String string, 2577 @Optional int format, 2578 @Optional String additionalWordCharacters) 2579 { 2580 if (string == null) 2581 string = ""; 2582 2583 if (format < 0 || format > 2) 2584 return NullValue.NULL; 2585 2586 int strlen = string.length(); 2587 boolean isAdditionalWordCharacters = additionalWordCharacters.length() > 0; 2588 2589 ArrayValueImpl resultArray = null; 2590 2591 if (format > 0) 2592 resultArray = new ArrayValueImpl(); 2593 2594 boolean isBetweenWords = true; 2595 2596 int wordCount = 0; 2597 2598 int lastWordStart = 0; 2599 2600 for (int i = 0; i <= strlen; i++) { 2601 boolean isWordCharacter; 2602 2603 if (i < strlen) { 2604 int ch = string.charAt(i); 2605 2606 isWordCharacter = Character.isLetter(ch) 2607 || ch == '-' 2608 || ch == '\'' 2609 || (isAdditionalWordCharacters 2610 && additionalWordCharacters.indexOf(ch) > -1); 2611 } 2612 else 2613 isWordCharacter = false; 2614 2615 if (isWordCharacter) { 2616 if (isBetweenWords) { 2617 isBetweenWords = false; 2619 2620 lastWordStart = i; 2621 wordCount++; 2622 } 2623 } 2624 else { 2625 if (!isBetweenWords) { 2626 isBetweenWords = true; 2628 2629 if (format > 0) { 2630 StringValue word = new StringValueImpl(string.substring(lastWordStart, i)); 2631 2632 if (format == 1) 2633 resultArray.append(word); 2634 else if (format == 2) 2635 resultArray.put(new LongValue(lastWordStart), word); 2636 } 2637 } 2638 } 2639 } 2640 2641 if (resultArray == null) 2642 return LongValue.create(wordCount); 2643 else 2644 return resultArray; 2645 } 2646 2647 2654 public static int strcasecmp(StringValue a, StringValue b) 2655 { 2656 int aLen = a.length(); 2657 int bLen = b.length(); 2658 2659 for (int i = 0; i < aLen && i < bLen; i++) { 2660 char chA = a.charAt(i); 2661 char chB = b.charAt(i); 2662 2663 if (chA == chB) 2664 continue; 2665 2666 if (Character.isUpperCase(chA)) 2667 chA = Character.toLowerCase(chA); 2668 2669 if (Character.isUpperCase(chB)) 2670 chB = Character.toLowerCase(chB); 2671 2672 if (chA == chB) 2673 continue; 2674 else if (chA < chB) 2675 return -1; 2676 else 2677 return 1; 2678 } 2679 2680 if (aLen == bLen) 2681 return 0; 2682 else if (aLen < bLen) 2683 return -1; 2684 else 2685 return 1; 2686 } 2687 2688 2695 public static int strcmp(String a, String b) 2696 { 2697 if (a == null) 2698 a = ""; 2699 2700 if (b == null) 2701 b = ""; 2702 2703 int cmp = a.compareTo(b); 2704 2705 if (cmp == 0) 2706 return 0; 2707 else if (cmp < 0) 2708 return -1; 2709 else 2710 return 1; 2711 } 2712 2713 2718 public static Value strchr(Env env, String haystack, Value needle) 2719 { 2720 return strstr(env, haystack, needle); 2721 } 2722 2723 2731 public static Value strcoll(String a, String b) 2732 { 2733 if (a == null) 2734 a = ""; 2735 2736 if (b == null) 2737 b = ""; 2738 2739 int cmp = a.compareTo(b); 2740 2741 if (cmp == 0) 2742 return LongValue.ZERO; 2743 else if (cmp < 0) 2744 return LongValue.MINUS_ONE; 2745 else 2746 return LongValue.ONE; 2747 } 2748 2749 2760 public static Value strcspn(StringValue string, 2761 StringValue characters, 2762 @Optional("0") int offset, 2763 @Optional("-2147483648") int length) 2764 { 2765 return strspnImpl(string, characters, offset, length, false); 2766 } 2767 2768 2769 2775 public static StringValue strip_tags(StringValue string, 2776 @Optional String allowTags) 2777 { 2778 2780 StringBuilderValue result = new StringBuilderValue(); 2781 2782 int len = string.length(); 2783 2784 for (int i = 0; i < len; i++) { 2785 char ch = string.charAt(i); 2786 2787 if (ch != '<') { 2788 result.append(ch); 2789 continue; 2790 } 2791 2792 for (i++; i < len; i++) { 2793 ch = string.charAt(i); 2794 2795 if (ch == '>') 2796 break; 2797 } 2798 } 2799 2800 return result; 2801 } 2802 2803 2808 public static long strlen(Value value) 2809 { 2810 return value.length(); 2811 } 2812 2813 2820 public static int strnatcasecmp(UnicodeValue a, UnicodeValue b) 2821 { 2822 return naturalOrderCompare(a, b, true); 2823 } 2824 2825 2832 public static int strnatcmp(UnicodeValue a, UnicodeValue b) 2833 { 2834 return naturalOrderCompare(a, b, false); 2835 } 2836 2837 2840 private static int naturalOrderCompare(UnicodeValue a, 2841 UnicodeValue b, 2842 boolean ignoreCase) 2843 { 2844 SimpleStringReader aIn = new SimpleStringReader(a); 2845 SimpleStringReader bIn = new SimpleStringReader(b); 2846 2847 int aChar = aIn.read(); 2848 int bChar = bIn.read(); 2849 2850 if (aChar == -1 && bChar >= 0) 2851 return -1; 2852 else if (aChar >= 0 && bChar == -1) 2853 return 1; 2854 2855 while (true) { 2856 while (Character.isWhitespace(aChar)) { 2857 aChar = aIn.read(); 2858 } 2859 2860 while (Character.isWhitespace(bChar)) { 2861 bChar = bIn.read(); 2862 } 2863 2864 if (aChar == -1 && bChar == -1) { 2865 return 0; 2866 } 2867 2868 if (aChar == '0' && bChar == '0') { 2872 while (true) { 2873 aChar = aIn.read(); 2874 bChar = bIn.read(); 2875 2876 if (aChar == '0' && bChar == '0') { 2877 continue; 2878 } 2879 else if (aChar == '0') { 2880 if ('1' <= bChar && bChar <= '9') 2881 return -1; 2882 else 2883 return 1; 2884 } 2885 else if (bChar == 0) { 2886 if ('1' <= aChar && aChar <= '9') 2887 return 1; 2888 else 2889 return -1; 2890 } 2891 else { 2892 break; 2893 } 2894 } 2895 } 2896 else if ('0' < aChar && aChar <= '9' && 2897 '0' < bChar && bChar <= '9') 2898 { 2899 int aInteger = aIn.readInt(aChar); 2900 int bInteger = bIn.readInt(bChar); 2901 2902 if (aInteger > bInteger) 2903 return 1; 2904 else if (aInteger < bInteger) 2905 return -1; 2906 else { 2907 aChar = aIn.read(); 2908 bChar = bIn.read(); 2909 } 2910 } 2911 2912 if (ignoreCase) { 2913 aChar = Character.toUpperCase(aChar); 2914 bChar = Character.toUpperCase(bChar); 2915 } 2916 2917 if (aChar > bChar) 2918 return 1; 2919 else if (aChar < bChar) 2920 return -1; 2921 2922 aChar = aIn.read(); 2923 bChar = bIn.read(); 2924 2925 if (aChar >= 0 && bChar == -1) 2928 return 1; 2929 else if (aChar == -1 && bChar >= 0) 2930 return -1; 2931 } 2932 } 2933 2934 2941 public static int strncasecmp(StringValue a, StringValue b, int length) 2942 { 2943 int aLen = a.length(); 2944 int bLen = b.length(); 2945 2946 for (int i = 0; i < length; i++) { 2947 if (aLen <= i) 2948 return -1; 2949 else if (bLen <= i) 2950 return 1; 2951 2952 char aChar = Character.toUpperCase(a.charAt(i)); 2953 char bChar = Character.toUpperCase(b.charAt(i)); 2954 2955 if (aChar < bChar) 2956 return -1; 2957 else if (bChar < aChar) 2958 return 1; 2959 } 2960 2961 return 0; 2962 } 2963 2964 2971 public static int strncmp(String a, String b, int length) 2972 { 2973 if (a == null) 2974 a = ""; 2975 2976 if (b == null) 2977 b = ""; 2978 2979 if (length < a.length()) 2980 a = a.substring(0, length); 2981 2982 if (length < b.length()) 2983 b = b.substring(0, length); 2984 2985 int cmp = a.compareTo(b); 2986 2987 if (cmp == 0) 2988 return 0; 2989 else if (cmp < 0) 2990 return -1; 2991 else 2992 return 1; 2993 } 2994 2995 3003 public static Value strpbrk(StringValue haystack, 3004 StringValue charList) 3005 { 3006 int len = haystack.length(); 3007 int sublen = charList.length(); 3008 3009 for (int i = 0; i < len; i++) { 3010 for (int j = 0; j < sublen; j++) { 3011 if (haystack.charAt(i) == charList.charAt(j)) 3012 return haystack.substring(i); 3013 } 3014 } 3015 3016 return BooleanValue.FALSE; 3017 } 3018 3019 3025 public static Value strpos(StringValue haystack, 3026 Value needleV, 3027 @Optional int offset) 3028 { 3029 StringValue needle; 3030 3031 if (needleV instanceof StringValue) 3032 needle = (StringValue) needleV; 3033 else 3034 needle = StringValue.create((char) needleV.toInt()); 3035 3036 int pos = haystack.indexOf(needle, offset); 3037 3038 if (pos < 0) 3039 return BooleanValue.FALSE; 3040 else 3041 return LongValue.create(pos); 3042 } 3043 3044 3051 public static Value stripos(StringValue haystack, 3052 Value needleV, 3053 @Optional int offset) 3054 { 3055 StringValue needle; 3056 3057 if (needleV instanceof StringValue) 3058 needle = (StringValue) needleV; 3059 else 3060 needle = StringValue.create((char) needleV.toInt()); 3061 3062 haystack = haystack.toLowerCase(); 3063 needle = needle.toLowerCase(); 3064 3065 int pos = haystack.indexOf(needle, offset); 3066 3067 if (pos < 0) 3068 return BooleanValue.FALSE; 3069 else 3070 return LongValue.create(pos); 3071 } 3072 3073 3080 public static String stripcslashes(String source) 3081 { 3082 if (source == null) 3083 source = ""; 3084 3085 StringBuilder result = new StringBuilder (source.length()); 3086 3087 int length = source.length(); 3088 3089 for (int i = 0; i < length; i++) { 3090 int ch = source.charAt(i); 3091 3092 if (ch == '\\') { 3093 i++; 3094 3095 if (i == length) 3096 ch = '\\'; 3097 else { 3098 ch = source.charAt(i); 3099 3100 switch (ch) { 3101 case 'a': 3102 ch = 0x07; 3103 break; 3104 case 'b': 3105 ch = '\b'; 3106 break; 3107 case 't': 3108 ch = '\t'; 3109 break; 3110 case 'n': 3111 ch = '\n'; 3112 break; 3113 case 'v': 3114 ch = 0xb; 3115 break; 3116 case 'f': 3117 ch = '\f'; 3118 break; 3119 case 'r': 3120 ch = '\r'; 3121 break; 3122 case 'x': 3123 if (i + 1 == length) 3125 break; 3126 3127 int digitValue = hexToDigit(source.charAt(i + 1)); 3128 3129 if (digitValue < 0) 3130 break; 3131 3132 ch = digitValue; 3133 i++; 3134 3135 if (i + 1 == length) 3136 break; 3137 3138 digitValue = hexToDigit(source.charAt(i + 1)); 3139 3140 if (digitValue < 0) 3141 break; 3142 3143 ch = ((ch << 4) | digitValue); 3144 i++; 3145 3146 break; 3147 default: 3148 digitValue = octToDigit((char) ch); 3150 3151 if (digitValue < 0) 3152 break; 3153 3154 ch = digitValue; 3155 3156 if (i + 1 == length) 3157 break; 3158 3159 digitValue = octToDigit(source.charAt(i + 1)); 3160 3161 if (digitValue < 0) 3162 break; 3163 3164 ch = ((ch << 3) | digitValue); 3165 i++; 3166 3167 if (i + 1 == length) 3168 break; 3169 3170 digitValue = octToDigit(source.charAt(i + 1)); 3171 3172 if (digitValue < 0) 3173 break; 3174 3175 ch = ((ch << 3) | digitValue); 3176 i++; 3177 } 3178 } 3179 } 3181 result.append((char) ch); 3182 } 3183 3184 return result.toString(); 3185 } 3186 3187 3192 public static StringValue stripslashes(StringValue string) 3193 { 3194 StringBuilderValue sb = new StringBuilderValue(); 3195 int len = string.length(); 3196 3197 for (int i = 0; i < len; i++) { 3198 char ch = string.charAt(i); 3199 3200 if (ch == '\\' && i + 1 < len) { 3201 sb.append(string.charAt(i + 1)); 3202 i++; 3203 } 3204 else 3205 sb.append(ch); 3206 } 3207 3208 return sb; 3209 } 3210 3211 3218 public static Value stristr(String haystack, 3219 Value needleV) 3220 { 3221 if (haystack == null) 3222 haystack = ""; 3223 3224 String needle; 3225 3226 if (needleV instanceof StringValue) { 3227 needle = needleV.toString(); 3228 } 3229 else { 3230 needle = String.valueOf((char) needleV.toLong()); 3231 } 3232 3233 String haystackLower = haystack.toLowerCase(); 3234 String needleLower = needle.toLowerCase(); 3235 3236 int i = haystackLower.indexOf(needleLower); 3237 3238 if (i >= 0) 3239 return new StringValueImpl(haystack.substring(i)); 3240 else 3241 return BooleanValue.FALSE; 3242 } 3243 3244 3251 public static Value strrchr(String haystack, 3252 Value needleV) 3253 { 3254 if (haystack == null) 3255 haystack = ""; 3256 3257 String needle; 3258 3259 if (needleV instanceof StringValue) { 3260 needle = needleV.toString(); 3261 } 3262 else { 3263 needle = String.valueOf((char) needleV.toLong()); 3264 } 3265 3266 int i = haystack.lastIndexOf(needle); 3267 3268 if (i > 0) 3269 return new StringValueImpl(haystack.substring(i)); 3270 else 3271 return BooleanValue.FALSE; 3272 } 3273 3274 3278 public static Value strrev(StringValue string) 3279 { 3280 StringBuilderValue sb = new StringBuilderValue(); 3281 3282 for (int i = string.length() - 1; i >= 0; i--) { 3283 sb.append(string.charAt(i)); 3284 } 3285 3286 return sb; 3287 } 3288 3289 3295 public static Value strrpos(StringValue haystack, 3296 Value needleV, 3297 @Optional Value offsetV) 3298 { 3299 StringValue needle; 3300 3301 if (needleV instanceof StringValue) 3302 needle = needleV.toStringValue(); 3303 else 3304 needle = StringValue.create((char) needleV.toInt()); 3305 3306 int offset; 3307 3308 if (offsetV instanceof DefaultValue) 3309 offset = haystack.length(); 3310 else 3311 offset = offsetV.toInt(); 3312 3313 int pos = haystack.lastIndexOf(needle, offset); 3314 3315 if (pos < 0) 3316 return BooleanValue.FALSE; 3317 else 3318 return new LongValue(pos); 3319 } 3320 3321 3328 public static Value strripos(String haystack, 3329 Value needleV, 3330 @Optional Value offsetV) 3331 { 3332 if (haystack == null) 3333 haystack = ""; 3334 3335 String needle; 3336 3337 if (needleV instanceof StringValue) 3338 needle = needleV.toString(); 3339 else 3340 needle = String.valueOf((char) needleV.toInt()); 3341 3342 int offset; 3343 3344 if (offsetV instanceof DefaultValue) 3345 offset = haystack.length(); 3346 else 3347 offset = offsetV.toInt(); 3348 3349 haystack = haystack.toLowerCase(); 3350 needle = needle.toLowerCase(); 3351 3352 int pos = haystack.lastIndexOf(needle, offset); 3353 3354 if (pos < 0) 3355 return BooleanValue.FALSE; 3356 else 3357 return new LongValue(pos); 3358 } 3359 3360 3371 public static Value strspn(StringValue string, 3372 StringValue characters, 3373 @Optional int offset, 3374 @Optional("-2147483648") int length) 3375 { 3376 return strspnImpl(string, characters, offset, length, true); 3377 } 3378 3379 private static Value strspnImpl(StringValue string, 3380 StringValue characters, 3381 int offset, 3382 int length, 3383 boolean isMatch) 3384 { 3385 int strlen = string.length(); 3386 3387 if (offset < 0) { 3390 offset += strlen; 3391 3392 if (offset < 0) 3393 offset = 0; 3394 } 3395 3396 if (offset > strlen) 3397 return BooleanValue.FALSE; 3398 3399 if (length == -2147483648) 3400 length = strlen; 3401 else if (length < 0) { 3402 length += (strlen - offset); 3403 3404 if (length < 0) 3405 length = 0; 3406 } 3407 3408 int end = offset + length; 3409 3410 if (strlen < end) 3411 end = strlen; 3412 3413 int count = 0; 3414 3415 for (; offset < end; offset++) { 3416 char ch = string.charAt(offset); 3417 3418 boolean isPresent = characters.indexOf(ch) > -1; 3419 3420 if (isPresent == isMatch) 3421 count++; 3422 else 3423 return LongValue.create(count); 3424 } 3425 3426 return LongValue.create(count); 3427 } 3428 3429 3438 public static Value strstr(Env env, 3439 String haystack, 3440 Value needleV) 3441 { 3442 if (haystack == null) 3443 haystack = ""; 3444 3445 String needle; 3446 3447 if (needleV instanceof StringValue) { 3448 needle = needleV.toString(); 3449 } 3450 else { 3451 needle = String.valueOf((char) needleV.toLong()); 3452 } 3453 3454 if (needle.length() == 0) { 3455 env.warning("empty needle"); 3456 return BooleanValue.FALSE; 3457 } 3458 3459 int i = haystack.indexOf(needle); 3460 3461 if (i >= 0) 3462 return new StringValueImpl(haystack.substring(i)); 3463 else 3464 return BooleanValue.FALSE; 3465 } 3466 3467 3507 public static Value strtok(Env env, String string1, @Optional Value string2) 3508 { 3509 if (string1 == null) 3510 string1 = ""; 3511 3512 String string; 3513 String characters; 3514 int offset; 3515 3516 if (string2.isNull()) { 3517 String savedString = (String ) env.getSpecialValue("caucho.strtok_string"); 3518 Integer savedOffset = (Integer ) env.getSpecialValue("caucho.strtok_offset"); 3519 3520 string = savedString == null ? "" : savedString; 3521 offset = savedOffset == null ? 0 : savedOffset; 3522 characters = string1; 3523 } 3524 else { 3525 string = string1; 3526 offset = 0; 3527 characters = string2.toString(); 3528 3529 env.setSpecialValue("caucho.strtok_string", string); 3530 } 3531 3532 int strlen = string.length(); 3533 3534 for (; offset < strlen; offset++) { 3536 char ch = string.charAt(offset); 3537 3538 if (characters.indexOf(ch) < 0) 3539 break; 3540 } 3541 3542 Value result; 3543 3544 if (offset == strlen) 3545 result = BooleanValue.FALSE; 3546 else { 3547 int start = offset; 3548 3549 offset++; 3550 3551 for (; offset < strlen; offset++) { 3553 char ch = string.charAt(offset); 3554 3555 if (characters.indexOf(ch) > -1) 3556 break; 3557 } 3558 3559 result = new StringValueImpl(string.substring(start, offset)); 3560 } 3561 3562 env.setSpecialValue("caucho.strtok_offset", offset); 3563 3564 return result; 3565 } 3566 3567 3572 public static StringValue strtolower(StringValue string) 3573 { 3574 return string.toLowerCase(); 3575 } 3576 3577 3582 public static StringValue strtoupper(StringValue string) 3583 { 3584 return string.toUpperCase(); 3585 } 3586 3587 3594 public static StringValue strtr(Env env, 3595 StringValue string, 3596 Value fromV, 3597 @Optional StringValue to) 3598 { 3599 if (fromV instanceof ArrayValue) 3600 return strtrArray(string, (ArrayValue) fromV); 3601 3602 StringValue from = fromV.toStringValue(); 3603 3604 int len = from.length(); 3605 3606 if (to.length() < len) 3607 len = to.length(); 3608 3609 char []map = new char[256]; 3610 for (int i = len - 1; i >= 0; i--) 3611 map[from.charAt(i)] = to.charAt(i); 3612 3613 StringBuilderValue sb = new StringBuilderValue(); 3614 3615 len = string.length(); 3616 for (int i = 0; i < len; i++) { 3617 char ch = string.charAt(i); 3618 3619 if (map[ch] != 0) 3620 sb.append(map[ch]); 3621 else 3622 sb.append(ch); 3623 } 3624 3625 return sb; 3626 } 3627 3628 3634 private static StringValue strtrArray(StringValue string, ArrayValue map) 3635 { 3636 int size = map.getSize(); 3637 3638 StringValue []from = new StringValue[size]; 3639 StringValue []to = new StringValue[size]; 3640 int k = 0; 3641 3642 for (Map.Entry <Value,Value> entry : map.entrySet()) { 3643 from[k] = entry.getKey().toStringValue(); 3644 to[k] = entry.getValue().toStringValue(); 3645 3646 k++; 3647 } 3648 3649 StringBuilderValue result = new StringBuilderValue(); 3650 int len = string.length(); 3651 int head = 0; 3652 3653 while (head < len) { 3654 int bestHead = len; 3655 int bestI = -1; 3656 int bestLength = 0; 3657 3658 for (int i = 0; i < from.length; i++) { 3659 int p = string.indexOf(from[i], head); 3660 3661 if (p >= 0 && (p < bestHead || 3662 p == bestHead && bestLength < from[i].length())) { 3663 bestHead = p; 3664 bestI = i; 3665 bestLength = from[i].length(); 3666 } 3667 } 3668 3669 if (head != bestHead) 3670 result.append(string.substring(head, bestHead)); 3671 3672 if (bestI >= 0) 3673 result.append(to[bestI]); 3674 3675 head = bestHead + bestLength; 3676 } 3677 3678 return result; 3679 } 3680 3681 3689 public static Value substr(Env env, 3690 StringValue string, 3691 int start, 3692 @Optional Value lenV) 3693 { 3694 int strLen = string.length(); 3695 if (start < 0) 3696 start = strLen + start; 3697 3698 if (start < 0 || strLen < start) 3699 return BooleanValue.FALSE; 3700 3701 if (lenV instanceof DefaultValue) { 3702 return string.substring(start); 3703 } 3704 else { 3705 int len = lenV.toInt(); 3706 int end; 3707 3708 if (len < 0) 3709 end = strLen + len; 3710 else 3711 end = start + len; 3712 3713 if (end <= start) 3714 return StringValue.EMPTY; 3715 else if (strLen <= end) 3716 return string.substring(start); 3717 else 3718 return string.substring(start, end); 3719 } 3720 } 3721 3722 public static Value substr_count(Env env, 3723 StringValue haystackV, 3724 StringValue needleV, 3725 @Optional("0") int offset, 3726 @Optional("-1") int length) 3727 { 3728 String haystack = haystackV.toString(); 3729 3730 String needle = needleV.toString(); 3731 3732 if (needle.length() == 0) { 3733 env.warning(L.l("empty substr")); 3734 return BooleanValue.FALSE; 3735 } 3736 3737 int haystackLength = haystack.length(); 3738 3739 if (offset < 0 || offset > haystackLength) { 3740 env.warning(L.l("offset `{0}' out of range", offset)); 3741 return BooleanValue.FALSE; 3742 } 3743 3744 if (length > -1) { 3745 if (offset + length > haystackLength) { 3746 env.warning(L.l("length `{0}' out of range", length)); 3747 return BooleanValue.FALSE; 3748 } 3749 else 3750 haystackLength = offset + length; 3751 } 3752 3753 int needleLength = needle.length(); 3754 3755 int count = 0; 3756 3757 int end = haystackLength - needleLength + 1; 3758 3759 for (int i = offset; i < end; i++) { 3760 if (haystack.startsWith(needle, i)) { 3761 count++; 3762 i += needleLength; 3763 } 3764 } 3765 3766 return new LongValue(count); 3767 } 3768 3769 3777 public static Value substr_replace(Value subjectV, 3778 StringValue replacement, 3779 Value startV, 3780 @Optional Value lengthV) 3781 { 3782 int start = 0; 3783 int length = Integer.MAX_VALUE / 2; 3784 3785 if ( !(lengthV.isNull() || lengthV.isArray()) ) 3786 length = lengthV.toInt(); 3787 3788 if ( !(startV.isNull() || startV.isArray()) ) 3789 start = startV.toInt(); 3790 3791 Iterator <Value> startIterator = 3792 startV.isArray() 3793 ? ((ArrayValue) startV).values().iterator() 3794 : null; 3795 3796 Iterator <Value> lengthIterator = 3797 lengthV.isArray() 3798 ? ((ArrayValue) lengthV).values().iterator() 3799 : null; 3800 3801 if (subjectV.isArray()) { 3802 ArrayValue resultArray = new ArrayValueImpl(); 3803 3804 ArrayValue subjectArray = (ArrayValue) subjectV; 3805 3806 for (Value value : subjectArray.values()) { 3807 3808 if (lengthIterator != null && lengthIterator.hasNext()) 3809 length = lengthIterator.next().toInt(); 3810 3811 if (startIterator != null && startIterator.hasNext()) 3812 start = startIterator.next().toInt(); 3813 3814 Value result = substrReplaceImpl(value.toStringValue(), replacement, start, length); 3815 3816 resultArray.append(result); 3817 } 3818 3819 return resultArray; 3820 } 3821 else { 3822 if (lengthIterator != null && lengthIterator.hasNext()) 3823 length = lengthIterator.next().toInt(); 3824 3825 if (startIterator != null && startIterator.hasNext()) 3826 start = startIterator.next().toInt(); 3827 3828 return substrReplaceImpl(subjectV.toStringValue(), replacement, start, length); 3829 } 3830 } 3831 3832 private static Value substrReplaceImpl(StringValue string, 3833 StringValue replacement, 3834 int start, 3835 int len) 3836 { 3837 int strLen = string.length(); 3838 3839 if (start > strLen) 3840 start = strLen; 3841 else if (start < 0) 3842 start = Math.max(strLen + start, 0); 3843 3844 int end; 3845 3846 if (len < 0) 3847 end = Math.max(strLen + len, start); 3848 else 3849 end = Math.min(start + len, strLen); 3850 3851 StringBuilderValue result = new StringBuilderValue(); 3852 3853 result.append(string.substring(0, start)); 3854 result.append(replacement); 3855 result.append(string.substring(end)); 3856 3857 return result; 3858 } 3859 3860 3867 public static Value trim(StringValue string, @Optional String characters) 3868 { 3869 boolean []trim; 3870 3871 if (characters == null || characters.equals("")) 3872 trim = TRIM_WHITESPACE; 3873 else 3874 trim = parseCharsetBitmap(characters.toString()); 3875 3876 int len = string.length(); 3877 3878 int head = 0; 3879 for (; head < len; head++) { 3880 char ch = string.charAt(head); 3881 3882 if (ch >= 256 || ! trim[ch]) { 3883 break; 3884 } 3885 } 3886 3887 int tail = len - 1; 3888 for (; tail >= 0; tail--) { 3889 char ch = string.charAt(tail); 3890 3891 if (ch >= 256 || ! trim[ch]) { 3892 break; 3893 } 3894 } 3895 3896 if (tail < head) 3897 return StringValue.EMPTY; 3898 else { 3899 return (StringValue) string.subSequence(head, tail + 1); 3900 } 3901 } 3902 3903 3908 public static String ucfirst(String string) 3909 { 3910 if (string == null) 3911 string = ""; 3912 3913 if (string.length() == 0) 3914 return string; 3915 3916 return Character.toUpperCase(string.charAt(0)) + string.substring(1); 3917 } 3918 3919 3924 public static String ucwords(String string) 3925 { 3926 if (string == null) 3927 string = ""; 3928 3929 int strLen = string.length(); 3930 3931 boolean isStart = true; 3932 StringBuilder sb = new StringBuilder (); 3933 3934 for (int i = 0; i < strLen; i++) { 3935 char ch = string.charAt(i); 3936 3937 switch (ch) { 3938 case ' ': case '\t': case '\r': case '\n': 3939 isStart = true; 3940 sb.append(ch); 3941 break; 3942 default: 3943 if (isStart) 3944 sb.append(Character.toUpperCase(ch)); 3945 else 3946 sb.append(ch); 3947 isStart = false; 3948 break; 3949 } 3950 } 3951 3952 return sb.toString(); 3953 } 3954 3955 3961 public static int vprintf(Env env, 3962 String format, 3963 @NotNull ArrayValue array) 3964 { 3965 Value []args; 3966 3967 if (array != null) { 3968 args = new Value[array.getSize()]; 3969 int i = 0; 3970 for (Value value : array.values()) 3971 args[i++] = value; 3972 } 3973 else 3974 args = new Value[0]; 3975 3976 return printf(env, format, args); 3977 } 3978 3979 3985 public static Value vsprintf(String format, 3986 @NotNull ArrayValue array) 3987 { 3988 Value []args; 3989 3990 if (array != null) { 3991 args = new Value[array.getSize()]; 3992 int i = 0; 3993 for (Value value : array.values()) 3994 args[i++] = value; 3995 } 3996 else 3997 args = new Value[0]; 3998 3999 return sprintf(format, args); 4000 } 4001 4002 4010 public static String wordwrap(String string, 4011 @Optional("75") int width, 4012 @Optional("'\n'") String breakString, 4013 @Optional boolean cut) 4014 { 4015 if (string == null) 4016 string = ""; 4017 4018 if (breakString == null) 4019 breakString = ""; 4020 4021 int len = string.length(); 4022 int head = 0; 4023 4024 StringBuilder sb = new StringBuilder (); 4025 while (head + width < len) { 4026 int tail = head + width; 4027 4028 if (! cut) { 4029 for (; 4030 head < tail && ! Character.isWhitespace(string.charAt(tail)); 4031 tail--) { 4032 } 4033 4034 if (head == tail) 4035 tail = head + width; 4036 } 4037 4038 if (sb.length() > 0) 4039 sb.append(breakString); 4040 4041 sb.append(string.substring(head, tail)); 4042 4043 head = tail; 4044 4045 if (! cut && head < len && Character.isWhitespace(string.charAt(head))) 4046 head++; 4047 } 4048 4049 if (head < len) { 4050 if (sb.length() > 0) 4051 sb.append(breakString); 4052 4053 sb.append(string.substring(head)); 4054 } 4055 4056 return sb.toString(); 4057 } 4058 4059 4062 private static boolean isWhitespace(char ch) 4063 { 4064 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; 4065 } 4066 4067 4070 private static char toUpperCase(char ch) 4071 { 4072 if (ch >= 'a' && ch <= 'z') 4073 return (char) ('A' + (ch - 'a')); 4074 else 4075 return ch; 4076 } 4077 4078 4081 private static char toUUChar(int d) 4082 { 4083 if (d == 0) 4084 return (char) 0x60; 4085 else 4086 return (char) (0x20 + (d & 0x3f)); 4087 } 4088 4089 private static char toHexChar(int d) 4090 { 4091 d &= 0xf; 4092 4093 if (d < 10) 4094 return (char) (d + '0'); 4095 else 4096 return (char) (d - 10 + 'a'); 4097 } 4098 4099 private static char toUpperHexChar(int d) 4100 { 4101 d &= 0xf; 4102 4103 if (d < 10) 4104 return (char) (d + '0'); 4105 else 4106 return (char) (d - 10 + 'A'); 4107 } 4108 4109 private static int hexToDigit(char ch) 4110 { 4111 if ('0' <= ch && ch <= '9') 4112 return ch - '0'; 4113 else if ('a' <= ch && ch <= 'f') 4114 return ch - 'a' + 10; 4115 else if ('A' <= ch && ch <= 'F') 4116 return ch - 'A' + 10; 4117 else 4118 return -1; 4119 } 4120 4121 private static int octToDigit(char ch) 4122 { 4123 if ('0' <= ch && ch <= '7') 4124 return ch - '0'; 4125 else 4126 return -1; 4127 } 4128 4129 abstract static class PrintfSegment { 4130 abstract public void apply(StringBuilderValue sb, Value []args); 4131 4132 static boolean hasIndex(String format) 4133 { 4134 return format.indexOf('$') >= 0; 4135 } 4136 4137 static int getIndex(String format) 4138 { 4139 int value = 0; 4140 4141 for (int i = 0; i < format.length(); i++) { 4142 char ch; 4143 4144 if ('0' <= (ch = format.charAt(i)) && ch <= '9') 4145 value = 10 * value + ch - '0'; 4146 else 4147 break; 4148 } 4149 4150 return value - 1; 4151 } 4152 4153 static String getIndexFormat(String format) 4154 { 4155 int p = format.indexOf('$'); 4156 4157 return format.substring(p + 1); 4158 } 4159 } 4160 4161 static class TextPrintfSegment extends PrintfSegment { 4162 private final char []_text; 4163 4164 TextPrintfSegment(StringBuilder text) 4165 { 4166 _text = new char[text.length()]; 4167 4168 text.getChars(0, _text.length, _text, 0); 4169 } 4170 4171 public void apply(StringBuilderValue sb, Value []args) 4172 { 4173 sb.append(_text, 0, _text.length); 4174 } 4175 } 4176 4177 static class LongPrintfSegment extends PrintfSegment { 4178 private final String _format; 4179 private final int _index; 4180 4181 LongPrintfSegment(String format, int index) 4182 { 4183 if (hasIndex(format)) { 4184 _index = getIndex(format); 4185 _format = getIndexFormat(format); 4186 } 4187 else { 4188 _format = format; 4189 _index = index; 4190 } 4191 } 4192 4193 public void apply(StringBuilderValue sb, Value []args) 4194 { 4195 long value; 4196 4197 if (_index < args.length) 4198 value = args[_index].toLong(); 4199 else 4200 value = 0; 4201 4202 sb.append(String.format(_format, value)); 4203 } 4204 } 4205 4206 static class DoublePrintfSegment extends PrintfSegment { 4207 private final String _format; 4208 private final int _index; 4209 4210 DoublePrintfSegment(String format, int index) 4211 { 4212 if (hasIndex(format)) { 4213 _index = getIndex(format); 4214 _format = getIndexFormat(format); 4215 } 4216 else { 4217 _format = format; 4218 _index = index; 4219 } 4220 } 4221 4222 public void apply(StringBuilderValue sb, Value []args) 4223 { 4224 double value; 4225 4226 if (_index < args.length) 4227 value = args[_index].toDouble(); 4228 else 4229 value = 0; 4230 4231 sb.append(String.format(_format, value)); 4232 } 4233 } 4234 4235 static class StringPrintfSegment extends PrintfSegment { 4236 private final char []_prefix; 4237 private final int _min; 4238 private final int _max; 4239 private final boolean _isLeft; 4240 private final boolean _isUpper; 4241 private final char _pad; 4242 protected final int _index; 4243 4244 StringPrintfSegment(StringBuilder prefix, 4245 boolean isLeft, boolean isZero, boolean isUpper, 4246 String format, int index) 4247 { 4248 _prefix = new char[prefix.length()]; 4249 4250 _isLeft = isLeft; 4251 _isUpper = isUpper; 4252 4253 _pad = isZero ? '0' : ' '; 4254 4255 prefix.getChars(0, _prefix.length, _prefix, 0); 4256 4257 if (hasIndex(format)) { 4258 index = getIndex(format); 4259 format = getIndexFormat(format); 4260 } 4261 4262 int i = 0; 4263 int len = format.length(); 4264 4265 int min = 0; 4266 int max = Integer.MAX_VALUE; 4267 char ch = ' '; 4268 4269 for (; i < len && '0' <= (ch = format.charAt(i)) && ch <= '9'; i++) { 4270 min = 10 * min + ch - '0'; 4271 } 4272 4273 if (ch == '.') { 4274 max = 0; 4275 4276 for (i++; i < len && '0' <= (ch = format.charAt(i)) && ch <= '9'; i++) { 4277 max = 10 * max + ch - '0'; 4278 } 4279 } 4280 4281 _min = min; 4282 _max = max; 4283 4284 _index = index; 4285 } 4286 4287 public void apply(StringBuilderValue sb, Value []args) 4288 { 4289 sb.append(_prefix, 0, _prefix.length); 4290 4291 String value = toValue(args); 4292 4293 int len = value.length(); 4294 4295 if (_max < len) { 4296 value = value.substring(0, _max); 4297 len = _max; 4298 } 4299 4300 if (_isUpper) 4301 value = value.toUpperCase(); 4302 4303 if (! _isLeft) { 4304 for (int i = len; i < _min; i++) { 4305 sb.append(_pad); 4306 } 4307 } 4308 4309 sb.append(value); 4310 4311 if (_isLeft) { 4312 for (int i = len; i < _min; i++) { 4313 sb.append(_pad); 4314 } 4315 } 4316 } 4317 4318 String toValue(Value []args) 4319 { 4320 if (_index < args.length) 4321 return args[_index].toString(); 4322 else 4323 return ""; 4324 } 4325 } 4326 4327 static class CharPrintfSegment extends StringPrintfSegment { 4328 CharPrintfSegment(StringBuilder prefix, 4329 boolean isLeft, boolean isZero, boolean isUpper, 4330 String format, int index) 4331 { 4332 super(prefix, isLeft, isZero, isUpper, format, index); 4333 } 4334 4335 String toValue(Value []args) 4336 { 4337 if (args.length <= _index) 4338 return ""; 4339 4340 Value v = args[_index]; 4341 4342 if (v.isLongConvertible()) 4343 return String.valueOf((char) v.toLong()); 4344 else 4345 return v.charValueAt(0).toString(); 4346 } 4347 } 4348 4349 static class SimpleStringReader { 4350 UnicodeValue _str; 4351 4352 int _length; 4353 int _index; 4354 4355 SimpleStringReader(UnicodeValue str) 4356 { 4357 _str = str; 4358 _length = str.length(); 4359 _index = 0; 4360 } 4361 4362 int read() 4363 { 4364 if (_index < _length) 4365 return _str.charAt(_index++); 4366 else 4367 return -1; 4368 } 4369 4370 int peek() 4371 { 4372 if (_index < _length) 4373 return _str.charAt(_index); 4374 else 4375 return -1; 4376 4377 } 4378 4379 int readInt(int currChar) 4380 { 4381 int number = currChar - '0'; 4382 4383 while (true) { 4384 currChar = peek(); 4385 4386 if ('0' <= currChar && currChar <= '9') { 4387 number = number * 10 + currChar - '0'; 4388 _index++; 4389 } 4390 else { 4391 break; 4392 } 4393 } 4394 4395 return number; 4396 } 4397 } 4398 4399 static { 4400 DEFAULT_DECIMAL_FORMAT_SYMBOLS = new DecimalFormatSymbols (); 4401 DEFAULT_DECIMAL_FORMAT_SYMBOLS.setDecimalSeparator('.'); 4402 DEFAULT_DECIMAL_FORMAT_SYMBOLS.setGroupingSeparator(','); 4403 DEFAULT_DECIMAL_FORMAT_SYMBOLS.setZeroDigit('0'); 4404 } 4405 4406} 4407 4408 | Popular Tags |