Ruby
2.0.0p247(2013-06-27revision41674)
|
00001 /* 00002 $Id: strscan.c 37916 2012-11-28 00:17:33Z ryan $ 00003 00004 Copyright (c) 1999-2006 Minero Aoki 00005 00006 This program is free software. 00007 You can distribute/modify this program under the terms of 00008 the Ruby License. For details, see the file COPYING. 00009 */ 00010 00011 #include "ruby/ruby.h" 00012 #include "ruby/re.h" 00013 #include "ruby/encoding.h" 00014 #include "regint.h" 00015 00016 #define STRSCAN_VERSION "0.7.0" 00017 00018 /* ======================================================================= 00019 Data Type Definitions 00020 ======================================================================= */ 00021 00022 static VALUE StringScanner; 00023 static VALUE ScanError; 00024 static ID id_byteslice; 00025 00026 struct strscanner 00027 { 00028 /* multi-purpose flags */ 00029 unsigned long flags; 00030 #define FLAG_MATCHED (1 << 0) 00031 00032 /* the string to scan */ 00033 VALUE str; 00034 00035 /* scan pointers */ 00036 long prev; /* legal only when MATCHED_P(s) */ 00037 long curr; /* always legal */ 00038 00039 /* the regexp register; legal only when MATCHED_P(s) */ 00040 struct re_registers regs; 00041 }; 00042 00043 #define MATCHED_P(s) ((s)->flags & FLAG_MATCHED) 00044 #define MATCHED(s) (s)->flags |= FLAG_MATCHED 00045 #define CLEAR_MATCH_STATUS(s) (s)->flags &= ~FLAG_MATCHED 00046 00047 #define S_PBEG(s) (RSTRING_PTR((s)->str)) 00048 #define S_LEN(s) (RSTRING_LEN((s)->str)) 00049 #define S_PEND(s) (S_PBEG(s) + S_LEN(s)) 00050 #define CURPTR(s) (S_PBEG(s) + (s)->curr) 00051 #define S_RESTLEN(s) (S_LEN(s) - (s)->curr) 00052 00053 #define EOS_P(s) ((s)->curr >= RSTRING_LEN(p->str)) 00054 00055 #define GET_SCANNER(obj,var) do {\ 00056 (var) = check_strscan(obj);\ 00057 if (NIL_P((var)->str)) rb_raise(rb_eArgError, "uninitialized StringScanner object");\ 00058 } while (0) 00059 00060 /* ======================================================================= 00061 Function Prototypes 00062 ======================================================================= */ 00063 00064 static VALUE infect _((VALUE str, struct strscanner *p)); 00065 static VALUE extract_range _((struct strscanner *p, long beg_i, long end_i)); 00066 static VALUE extract_beg_len _((struct strscanner *p, long beg_i, long len)); 00067 00068 static struct strscanner *check_strscan _((VALUE obj)); 00069 static void strscan_mark _((void *p)); 00070 static void strscan_free _((void *p)); 00071 static size_t strscan_memsize _((const void *p)); 00072 static VALUE strscan_s_allocate _((VALUE klass)); 00073 static VALUE strscan_initialize _((int argc, VALUE *argv, VALUE self)); 00074 static VALUE strscan_init_copy _((VALUE vself, VALUE vorig)); 00075 00076 static VALUE strscan_s_mustc _((VALUE self)); 00077 static VALUE strscan_terminate _((VALUE self)); 00078 static VALUE strscan_clear _((VALUE self)); 00079 static VALUE strscan_get_string _((VALUE self)); 00080 static VALUE strscan_set_string _((VALUE self, VALUE str)); 00081 static VALUE strscan_concat _((VALUE self, VALUE str)); 00082 static VALUE strscan_get_pos _((VALUE self)); 00083 static VALUE strscan_set_pos _((VALUE self, VALUE pos)); 00084 static VALUE strscan_do_scan _((VALUE self, VALUE regex, 00085 int succptr, int getstr, int headonly)); 00086 static VALUE strscan_scan _((VALUE self, VALUE re)); 00087 static VALUE strscan_match_p _((VALUE self, VALUE re)); 00088 static VALUE strscan_skip _((VALUE self, VALUE re)); 00089 static VALUE strscan_check _((VALUE self, VALUE re)); 00090 static VALUE strscan_scan_full _((VALUE self, VALUE re, 00091 VALUE succp, VALUE getp)); 00092 static VALUE strscan_scan_until _((VALUE self, VALUE re)); 00093 static VALUE strscan_skip_until _((VALUE self, VALUE re)); 00094 static VALUE strscan_check_until _((VALUE self, VALUE re)); 00095 static VALUE strscan_search_full _((VALUE self, VALUE re, 00096 VALUE succp, VALUE getp)); 00097 static void adjust_registers_to_matched _((struct strscanner *p)); 00098 static VALUE strscan_getch _((VALUE self)); 00099 static VALUE strscan_get_byte _((VALUE self)); 00100 static VALUE strscan_getbyte _((VALUE self)); 00101 static VALUE strscan_peek _((VALUE self, VALUE len)); 00102 static VALUE strscan_peep _((VALUE self, VALUE len)); 00103 static VALUE strscan_unscan _((VALUE self)); 00104 static VALUE strscan_bol_p _((VALUE self)); 00105 static VALUE strscan_eos_p _((VALUE self)); 00106 static VALUE strscan_empty_p _((VALUE self)); 00107 static VALUE strscan_rest_p _((VALUE self)); 00108 static VALUE strscan_matched_p _((VALUE self)); 00109 static VALUE strscan_matched _((VALUE self)); 00110 static VALUE strscan_matched_size _((VALUE self)); 00111 static VALUE strscan_aref _((VALUE self, VALUE idx)); 00112 static VALUE strscan_pre_match _((VALUE self)); 00113 static VALUE strscan_post_match _((VALUE self)); 00114 static VALUE strscan_rest _((VALUE self)); 00115 static VALUE strscan_rest_size _((VALUE self)); 00116 00117 static VALUE strscan_inspect _((VALUE self)); 00118 static VALUE inspect1 _((struct strscanner *p)); 00119 static VALUE inspect2 _((struct strscanner *p)); 00120 00121 /* ======================================================================= 00122 Utils 00123 ======================================================================= */ 00124 00125 static VALUE 00126 infect(VALUE str, struct strscanner *p) 00127 { 00128 OBJ_INFECT(str, p->str); 00129 return str; 00130 } 00131 00132 static VALUE 00133 str_new(struct strscanner *p, const char *ptr, long len) 00134 { 00135 VALUE str = rb_str_new(ptr, len); 00136 rb_enc_copy(str, p->str); 00137 return str; 00138 } 00139 00140 static VALUE 00141 extract_range(struct strscanner *p, long beg_i, long end_i) 00142 { 00143 if (beg_i > S_LEN(p)) return Qnil; 00144 if (end_i > S_LEN(p)) 00145 end_i = S_LEN(p); 00146 return infect(str_new(p, S_PBEG(p) + beg_i, end_i - beg_i), p); 00147 } 00148 00149 static VALUE 00150 extract_beg_len(struct strscanner *p, long beg_i, long len) 00151 { 00152 if (beg_i > S_LEN(p)) return Qnil; 00153 if (beg_i + len > S_LEN(p)) 00154 len = S_LEN(p) - beg_i; 00155 return infect(str_new(p, S_PBEG(p) + beg_i, len), p); 00156 } 00157 00158 /* ======================================================================= 00159 Constructor 00160 ======================================================================= */ 00161 00162 static void 00163 strscan_mark(void *ptr) 00164 { 00165 struct strscanner *p = ptr; 00166 rb_gc_mark(p->str); 00167 } 00168 00169 static void 00170 strscan_free(void *ptr) 00171 { 00172 struct strscanner *p = ptr; 00173 onig_region_free(&(p->regs), 0); 00174 ruby_xfree(p); 00175 } 00176 00177 static size_t 00178 strscan_memsize(const void *ptr) 00179 { 00180 const struct strscanner *p = ptr; 00181 size_t size = 0; 00182 if (p) { 00183 size = sizeof(*p) - sizeof(p->regs) + onig_region_memsize(&p->regs); 00184 } 00185 return size; 00186 } 00187 00188 static const rb_data_type_t strscanner_type = { 00189 "StringScanner", 00190 {strscan_mark, strscan_free, strscan_memsize} 00191 }; 00192 00193 static VALUE 00194 strscan_s_allocate(VALUE klass) 00195 { 00196 struct strscanner *p; 00197 00198 p = ALLOC(struct strscanner); 00199 MEMZERO(p, struct strscanner, 1); 00200 CLEAR_MATCH_STATUS(p); 00201 onig_region_init(&(p->regs)); 00202 p->str = Qnil; 00203 return TypedData_Wrap_Struct(klass, &strscanner_type, p); 00204 } 00205 00206 /* 00207 * call-seq: StringScanner.new(string, dup = false) 00208 * 00209 * Creates a new StringScanner object to scan over the given +string+. 00210 * +dup+ argument is obsolete and not used now. 00211 */ 00212 static VALUE 00213 strscan_initialize(int argc, VALUE *argv, VALUE self) 00214 { 00215 struct strscanner *p; 00216 VALUE str, need_dup; 00217 00218 p = check_strscan(self); 00219 rb_scan_args(argc, argv, "11", &str, &need_dup); 00220 StringValue(str); 00221 p->str = str; 00222 00223 return self; 00224 } 00225 00226 static struct strscanner * 00227 check_strscan(VALUE obj) 00228 { 00229 return rb_check_typeddata(obj, &strscanner_type); 00230 } 00231 00232 /* 00233 * call-seq: 00234 * dup 00235 * clone 00236 * 00237 * Duplicates a StringScanner object. 00238 */ 00239 static VALUE 00240 strscan_init_copy(VALUE vself, VALUE vorig) 00241 { 00242 struct strscanner *self, *orig; 00243 00244 self = check_strscan(vself); 00245 orig = check_strscan(vorig); 00246 if (self != orig) { 00247 self->flags = orig->flags; 00248 self->str = orig->str; 00249 self->prev = orig->prev; 00250 self->curr = orig->curr; 00251 onig_region_copy(&self->regs, &orig->regs); 00252 } 00253 00254 return vself; 00255 } 00256 00257 /* ======================================================================= 00258 Instance Methods 00259 ======================================================================= */ 00260 00261 /* 00262 * call-seq: StringScanner.must_C_version 00263 * 00264 * This method is defined for backward compatibility. 00265 */ 00266 static VALUE 00267 strscan_s_mustc(VALUE self) 00268 { 00269 return self; 00270 } 00271 00272 /* 00273 * Reset the scan pointer (index 0) and clear matching data. 00274 */ 00275 static VALUE 00276 strscan_reset(VALUE self) 00277 { 00278 struct strscanner *p; 00279 00280 GET_SCANNER(self, p); 00281 p->curr = 0; 00282 CLEAR_MATCH_STATUS(p); 00283 return self; 00284 } 00285 00286 /* 00287 * call-seq: 00288 * terminate 00289 * clear 00290 * 00291 * Set the scan pointer to the end of the string and clear matching data. 00292 */ 00293 static VALUE 00294 strscan_terminate(VALUE self) 00295 { 00296 struct strscanner *p; 00297 00298 GET_SCANNER(self, p); 00299 p->curr = S_LEN(p); 00300 CLEAR_MATCH_STATUS(p); 00301 return self; 00302 } 00303 00304 /* 00305 * Equivalent to #terminate. 00306 * This method is obsolete; use #terminate instead. 00307 */ 00308 static VALUE 00309 strscan_clear(VALUE self) 00310 { 00311 rb_warning("StringScanner#clear is obsolete; use #terminate instead"); 00312 return strscan_terminate(self); 00313 } 00314 00315 /* 00316 * Returns the string being scanned. 00317 */ 00318 static VALUE 00319 strscan_get_string(VALUE self) 00320 { 00321 struct strscanner *p; 00322 00323 GET_SCANNER(self, p); 00324 return p->str; 00325 } 00326 00327 /* 00328 * call-seq: string=(str) 00329 * 00330 * Changes the string being scanned to +str+ and resets the scanner. 00331 * Returns +str+. 00332 */ 00333 static VALUE 00334 strscan_set_string(VALUE self, VALUE str) 00335 { 00336 struct strscanner *p = check_strscan(self); 00337 00338 StringValue(str); 00339 p->str = str; 00340 p->curr = 0; 00341 CLEAR_MATCH_STATUS(p); 00342 return str; 00343 } 00344 00345 /* 00346 * call-seq: 00347 * concat(str) 00348 * <<(str) 00349 * 00350 * Appends +str+ to the string being scanned. 00351 * This method does not affect scan pointer. 00352 * 00353 * s = StringScanner.new("Fri Dec 12 1975 14:39") 00354 * s.scan(/Fri /) 00355 * s << " +1000 GMT" 00356 * s.string # -> "Fri Dec 12 1975 14:39 +1000 GMT" 00357 * s.scan(/Dec/) # -> "Dec" 00358 */ 00359 static VALUE 00360 strscan_concat(VALUE self, VALUE str) 00361 { 00362 struct strscanner *p; 00363 00364 GET_SCANNER(self, p); 00365 StringValue(str); 00366 rb_str_append(p->str, str); 00367 return self; 00368 } 00369 00370 /* 00371 * Returns the byte position of the scan pointer. In the 'reset' position, this 00372 * value is zero. In the 'terminated' position (i.e. the string is exhausted), 00373 * this value is the bytesize of the string. 00374 * 00375 * In short, it's a 0-based index into bytes of the string. 00376 * 00377 * s = StringScanner.new('test string') 00378 * s.pos # -> 0 00379 * s.scan_until /str/ # -> "test str" 00380 * s.pos # -> 8 00381 * s.terminate # -> #<StringScanner fin> 00382 * s.pos # -> 11 00383 */ 00384 static VALUE 00385 strscan_get_pos(VALUE self) 00386 { 00387 struct strscanner *p; 00388 00389 GET_SCANNER(self, p); 00390 return INT2FIX(p->curr); 00391 } 00392 00393 /* 00394 * Returns the character position of the scan pointer. In the 'reset' position, this 00395 * value is zero. In the 'terminated' position (i.e. the string is exhausted), 00396 * this value is the size of the string. 00397 * 00398 * In short, it's a 0-based index into the string. 00399 * 00400 * s = StringScanner.new("abcädeföghi") 00401 * s.charpos # -> 0 00402 * s.scan_until(/ä/) # -> "abcä" 00403 * s.pos # -> 5 00404 * s.charpos # -> 4 00405 */ 00406 static VALUE 00407 strscan_get_charpos(VALUE self) 00408 { 00409 struct strscanner *p; 00410 VALUE substr; 00411 00412 GET_SCANNER(self, p); 00413 00414 substr = rb_funcall(p->str, id_byteslice, 2, INT2FIX(0), INT2NUM(p->curr)); 00415 00416 return rb_str_length(substr); 00417 } 00418 00419 /* 00420 * call-seq: pos=(n) 00421 * 00422 * Set the byte position of the scan pointer. 00423 * 00424 * s = StringScanner.new('test string') 00425 * s.pos = 7 # -> 7 00426 * s.rest # -> "ring" 00427 */ 00428 static VALUE 00429 strscan_set_pos(VALUE self, VALUE v) 00430 { 00431 struct strscanner *p; 00432 long i; 00433 00434 GET_SCANNER(self, p); 00435 i = NUM2INT(v); 00436 if (i < 0) i += S_LEN(p); 00437 if (i < 0) rb_raise(rb_eRangeError, "index out of range"); 00438 if (i > S_LEN(p)) rb_raise(rb_eRangeError, "index out of range"); 00439 p->curr = i; 00440 return INT2NUM(i); 00441 } 00442 00443 static VALUE 00444 strscan_do_scan(VALUE self, VALUE regex, int succptr, int getstr, int headonly) 00445 { 00446 regex_t *rb_reg_prepare_re(VALUE re, VALUE str); 00447 struct strscanner *p; 00448 regex_t *re; 00449 long ret; 00450 int tmpreg; 00451 00452 Check_Type(regex, T_REGEXP); 00453 GET_SCANNER(self, p); 00454 00455 CLEAR_MATCH_STATUS(p); 00456 if (S_RESTLEN(p) < 0) { 00457 return Qnil; 00458 } 00459 re = rb_reg_prepare_re(regex, p->str); 00460 tmpreg = re != RREGEXP(regex)->ptr; 00461 if (!tmpreg) RREGEXP(regex)->usecnt++; 00462 00463 if (headonly) { 00464 ret = onig_match(re, (UChar* )CURPTR(p), 00465 (UChar* )(CURPTR(p) + S_RESTLEN(p)), 00466 (UChar* )CURPTR(p), &(p->regs), ONIG_OPTION_NONE); 00467 } 00468 else { 00469 ret = onig_search(re, 00470 (UChar* )CURPTR(p), (UChar* )(CURPTR(p) + S_RESTLEN(p)), 00471 (UChar* )CURPTR(p), (UChar* )(CURPTR(p) + S_RESTLEN(p)), 00472 &(p->regs), ONIG_OPTION_NONE); 00473 } 00474 if (!tmpreg) RREGEXP(regex)->usecnt--; 00475 if (tmpreg) { 00476 if (RREGEXP(regex)->usecnt) { 00477 onig_free(re); 00478 } 00479 else { 00480 onig_free(RREGEXP(regex)->ptr); 00481 RREGEXP(regex)->ptr = re; 00482 } 00483 } 00484 00485 if (ret == -2) rb_raise(ScanError, "regexp buffer overflow"); 00486 if (ret < 0) { 00487 /* not matched */ 00488 return Qnil; 00489 } 00490 00491 MATCHED(p); 00492 p->prev = p->curr; 00493 if (succptr) { 00494 p->curr += p->regs.end[0]; 00495 } 00496 if (getstr) { 00497 return extract_beg_len(p, p->prev, p->regs.end[0]); 00498 } 00499 else { 00500 return INT2FIX(p->regs.end[0]); 00501 } 00502 } 00503 00504 /* 00505 * call-seq: scan(pattern) => String 00506 * 00507 * Tries to match with +pattern+ at the current position. If there's a match, 00508 * the scanner advances the "scan pointer" and returns the matched string. 00509 * Otherwise, the scanner returns +nil+. 00510 * 00511 * s = StringScanner.new('test string') 00512 * p s.scan(/\w+/) # -> "test" 00513 * p s.scan(/\w+/) # -> nil 00514 * p s.scan(/\s+/) # -> " " 00515 * p s.scan(/\w+/) # -> "string" 00516 * p s.scan(/./) # -> nil 00517 * 00518 */ 00519 static VALUE 00520 strscan_scan(VALUE self, VALUE re) 00521 { 00522 return strscan_do_scan(self, re, 1, 1, 1); 00523 } 00524 00525 /* 00526 * call-seq: match?(pattern) 00527 * 00528 * Tests whether the given +pattern+ is matched from the current scan pointer. 00529 * Returns the length of the match, or +nil+. The scan pointer is not advanced. 00530 * 00531 * s = StringScanner.new('test string') 00532 * p s.match?(/\w+/) # -> 4 00533 * p s.match?(/\w+/) # -> 4 00534 * p s.match?(/\s+/) # -> nil 00535 */ 00536 static VALUE 00537 strscan_match_p(VALUE self, VALUE re) 00538 { 00539 return strscan_do_scan(self, re, 0, 0, 1); 00540 } 00541 00542 /* 00543 * call-seq: skip(pattern) 00544 * 00545 * Attempts to skip over the given +pattern+ beginning with the scan pointer. 00546 * If it matches, the scan pointer is advanced to the end of the match, and the 00547 * length of the match is returned. Otherwise, +nil+ is returned. 00548 * 00549 * It's similar to #scan, but without returning the matched string. 00550 * 00551 * s = StringScanner.new('test string') 00552 * p s.skip(/\w+/) # -> 4 00553 * p s.skip(/\w+/) # -> nil 00554 * p s.skip(/\s+/) # -> 1 00555 * p s.skip(/\w+/) # -> 6 00556 * p s.skip(/./) # -> nil 00557 * 00558 */ 00559 static VALUE 00560 strscan_skip(VALUE self, VALUE re) 00561 { 00562 return strscan_do_scan(self, re, 1, 0, 1); 00563 } 00564 00565 /* 00566 * call-seq: check(pattern) 00567 * 00568 * This returns the value that #scan would return, without advancing the scan 00569 * pointer. The match register is affected, though. 00570 * 00571 * s = StringScanner.new("Fri Dec 12 1975 14:39") 00572 * s.check /Fri/ # -> "Fri" 00573 * s.pos # -> 0 00574 * s.matched # -> "Fri" 00575 * s.check /12/ # -> nil 00576 * s.matched # -> nil 00577 * 00578 * Mnemonic: it "checks" to see whether a #scan will return a value. 00579 */ 00580 static VALUE 00581 strscan_check(VALUE self, VALUE re) 00582 { 00583 return strscan_do_scan(self, re, 0, 1, 1); 00584 } 00585 00586 /* 00587 * call-seq: scan_full(pattern, advance_pointer_p, return_string_p) 00588 * 00589 * Tests whether the given +pattern+ is matched from the current scan pointer. 00590 * Advances the scan pointer if +advance_pointer_p+ is true. 00591 * Returns the matched string if +return_string_p+ is true. 00592 * The match register is affected. 00593 * 00594 * "full" means "#scan with full parameters". 00595 */ 00596 static VALUE 00597 strscan_scan_full(VALUE self, VALUE re, VALUE s, VALUE f) 00598 { 00599 return strscan_do_scan(self, re, RTEST(s), RTEST(f), 1); 00600 } 00601 00602 /* 00603 * call-seq: scan_until(pattern) 00604 * 00605 * Scans the string _until_ the +pattern+ is matched. Returns the substring up 00606 * to and including the end of the match, advancing the scan pointer to that 00607 * location. If there is no match, +nil+ is returned. 00608 * 00609 * s = StringScanner.new("Fri Dec 12 1975 14:39") 00610 * s.scan_until(/1/) # -> "Fri Dec 1" 00611 * s.pre_match # -> "Fri Dec " 00612 * s.scan_until(/XYZ/) # -> nil 00613 */ 00614 static VALUE 00615 strscan_scan_until(VALUE self, VALUE re) 00616 { 00617 return strscan_do_scan(self, re, 1, 1, 0); 00618 } 00619 00620 /* 00621 * call-seq: exist?(pattern) 00622 * 00623 * Looks _ahead_ to see if the +pattern+ exists _anywhere_ in the string, 00624 * without advancing the scan pointer. This predicates whether a #scan_until 00625 * will return a value. 00626 * 00627 * s = StringScanner.new('test string') 00628 * s.exist? /s/ # -> 3 00629 * s.scan /test/ # -> "test" 00630 * s.exist? /s/ # -> 2 00631 * s.exist? /e/ # -> nil 00632 */ 00633 static VALUE 00634 strscan_exist_p(VALUE self, VALUE re) 00635 { 00636 return strscan_do_scan(self, re, 0, 0, 0); 00637 } 00638 00639 /* 00640 * call-seq: skip_until(pattern) 00641 * 00642 * Advances the scan pointer until +pattern+ is matched and consumed. Returns 00643 * the number of bytes advanced, or +nil+ if no match was found. 00644 * 00645 * Look ahead to match +pattern+, and advance the scan pointer to the _end_ 00646 * of the match. Return the number of characters advanced, or +nil+ if the 00647 * match was unsuccessful. 00648 * 00649 * It's similar to #scan_until, but without returning the intervening string. 00650 * 00651 * s = StringScanner.new("Fri Dec 12 1975 14:39") 00652 * s.skip_until /12/ # -> 10 00653 * s # 00654 */ 00655 static VALUE 00656 strscan_skip_until(VALUE self, VALUE re) 00657 { 00658 return strscan_do_scan(self, re, 1, 0, 0); 00659 } 00660 00661 /* 00662 * call-seq: check_until(pattern) 00663 * 00664 * This returns the value that #scan_until would return, without advancing the 00665 * scan pointer. The match register is affected, though. 00666 * 00667 * s = StringScanner.new("Fri Dec 12 1975 14:39") 00668 * s.check_until /12/ # -> "Fri Dec 12" 00669 * s.pos # -> 0 00670 * s.matched # -> 12 00671 * 00672 * Mnemonic: it "checks" to see whether a #scan_until will return a value. 00673 */ 00674 static VALUE 00675 strscan_check_until(VALUE self, VALUE re) 00676 { 00677 return strscan_do_scan(self, re, 0, 1, 0); 00678 } 00679 00680 /* 00681 * call-seq: search_full(pattern, advance_pointer_p, return_string_p) 00682 * 00683 * Scans the string _until_ the +pattern+ is matched. 00684 * Advances the scan pointer if +advance_pointer_p+, otherwise not. 00685 * Returns the matched string if +return_string_p+ is true, otherwise 00686 * returns the number of bytes advanced. 00687 * This method does affect the match register. 00688 */ 00689 static VALUE 00690 strscan_search_full(VALUE self, VALUE re, VALUE s, VALUE f) 00691 { 00692 return strscan_do_scan(self, re, RTEST(s), RTEST(f), 0); 00693 } 00694 00695 static void 00696 adjust_registers_to_matched(struct strscanner *p) 00697 { 00698 onig_region_clear(&(p->regs)); 00699 onig_region_set(&(p->regs), 0, 0, (int)(p->curr - p->prev)); 00700 } 00701 00702 /* 00703 * Scans one character and returns it. 00704 * This method is multibyte character sensitive. 00705 * 00706 * s = StringScanner.new("ab") 00707 * s.getch # => "a" 00708 * s.getch # => "b" 00709 * s.getch # => nil 00710 * 00711 * $KCODE = 'EUC' 00712 * s = StringScanner.new("\244\242") 00713 * s.getch # => "\244\242" # Japanese hira-kana "A" in EUC-JP 00714 * s.getch # => nil 00715 */ 00716 static VALUE 00717 strscan_getch(VALUE self) 00718 { 00719 struct strscanner *p; 00720 long len; 00721 00722 GET_SCANNER(self, p); 00723 CLEAR_MATCH_STATUS(p); 00724 if (EOS_P(p)) 00725 return Qnil; 00726 00727 len = rb_enc_mbclen(CURPTR(p), S_PEND(p), rb_enc_get(p->str)); 00728 if (p->curr + len > S_LEN(p)) { 00729 len = S_LEN(p) - p->curr; 00730 } 00731 p->prev = p->curr; 00732 p->curr += len; 00733 MATCHED(p); 00734 adjust_registers_to_matched(p); 00735 return extract_range(p, p->prev + p->regs.beg[0], 00736 p->prev + p->regs.end[0]); 00737 } 00738 00739 /* 00740 * Scans one byte and returns it. 00741 * This method is not multibyte character sensitive. 00742 * See also: #getch. 00743 * 00744 * s = StringScanner.new('ab') 00745 * s.get_byte # => "a" 00746 * s.get_byte # => "b" 00747 * s.get_byte # => nil 00748 * 00749 * $KCODE = 'EUC' 00750 * s = StringScanner.new("\244\242") 00751 * s.get_byte # => "\244" 00752 * s.get_byte # => "\242" 00753 * s.get_byte # => nil 00754 */ 00755 static VALUE 00756 strscan_get_byte(VALUE self) 00757 { 00758 struct strscanner *p; 00759 00760 GET_SCANNER(self, p); 00761 CLEAR_MATCH_STATUS(p); 00762 if (EOS_P(p)) 00763 return Qnil; 00764 00765 p->prev = p->curr; 00766 p->curr++; 00767 MATCHED(p); 00768 adjust_registers_to_matched(p); 00769 return extract_range(p, p->prev + p->regs.beg[0], 00770 p->prev + p->regs.end[0]); 00771 } 00772 00773 /* 00774 * Equivalent to #get_byte. 00775 * This method is obsolete; use #get_byte instead. 00776 */ 00777 static VALUE 00778 strscan_getbyte(VALUE self) 00779 { 00780 rb_warning("StringScanner#getbyte is obsolete; use #get_byte instead"); 00781 return strscan_get_byte(self); 00782 } 00783 00784 /* 00785 * call-seq: peek(len) 00786 * 00787 * Extracts a string corresponding to <tt>string[pos,len]</tt>, without 00788 * advancing the scan pointer. 00789 * 00790 * s = StringScanner.new('test string') 00791 * s.peek(7) # => "test st" 00792 * s.peek(7) # => "test st" 00793 * 00794 */ 00795 static VALUE 00796 strscan_peek(VALUE self, VALUE vlen) 00797 { 00798 struct strscanner *p; 00799 long len; 00800 00801 GET_SCANNER(self, p); 00802 00803 len = NUM2LONG(vlen); 00804 if (EOS_P(p)) 00805 return infect(str_new(p, "", 0), p); 00806 00807 if (p->curr + len > S_LEN(p)) 00808 len = S_LEN(p) - p->curr; 00809 return extract_beg_len(p, p->curr, len); 00810 } 00811 00812 /* 00813 * Equivalent to #peek. 00814 * This method is obsolete; use #peek instead. 00815 */ 00816 static VALUE 00817 strscan_peep(VALUE self, VALUE vlen) 00818 { 00819 rb_warning("StringScanner#peep is obsolete; use #peek instead"); 00820 return strscan_peek(self, vlen); 00821 } 00822 00823 /* 00824 * Set the scan pointer to the previous position. Only one previous position is 00825 * remembered, and it changes with each scanning operation. 00826 * 00827 * s = StringScanner.new('test string') 00828 * s.scan(/\w+/) # => "test" 00829 * s.unscan 00830 * s.scan(/../) # => "te" 00831 * s.scan(/\d/) # => nil 00832 * s.unscan # ScanError: unscan failed: previous match record not exist 00833 */ 00834 static VALUE 00835 strscan_unscan(VALUE self) 00836 { 00837 struct strscanner *p; 00838 00839 GET_SCANNER(self, p); 00840 if (! MATCHED_P(p)) 00841 rb_raise(ScanError, "unscan failed: previous match record not exist"); 00842 p->curr = p->prev; 00843 CLEAR_MATCH_STATUS(p); 00844 return self; 00845 } 00846 00847 /* 00848 * Returns +true+ iff the scan pointer is at the beginning of the line. 00849 * 00850 * s = StringScanner.new("test\ntest\n") 00851 * s.bol? # => true 00852 * s.scan(/te/) 00853 * s.bol? # => false 00854 * s.scan(/st\n/) 00855 * s.bol? # => true 00856 * s.terminate 00857 * s.bol? # => true 00858 */ 00859 static VALUE 00860 strscan_bol_p(VALUE self) 00861 { 00862 struct strscanner *p; 00863 00864 GET_SCANNER(self, p); 00865 if (CURPTR(p) > S_PEND(p)) return Qnil; 00866 if (p->curr == 0) return Qtrue; 00867 return (*(CURPTR(p) - 1) == '\n') ? Qtrue : Qfalse; 00868 } 00869 00870 /* 00871 * Returns +true+ if the scan pointer is at the end of the string. 00872 * 00873 * s = StringScanner.new('test string') 00874 * p s.eos? # => false 00875 * s.scan(/test/) 00876 * p s.eos? # => false 00877 * s.terminate 00878 * p s.eos? # => true 00879 */ 00880 static VALUE 00881 strscan_eos_p(VALUE self) 00882 { 00883 struct strscanner *p; 00884 00885 GET_SCANNER(self, p); 00886 return EOS_P(p) ? Qtrue : Qfalse; 00887 } 00888 00889 /* 00890 * Equivalent to #eos?. 00891 * This method is obsolete, use #eos? instead. 00892 */ 00893 static VALUE 00894 strscan_empty_p(VALUE self) 00895 { 00896 rb_warning("StringScanner#empty? is obsolete; use #eos? instead"); 00897 return strscan_eos_p(self); 00898 } 00899 00900 /* 00901 * Returns true iff there is more data in the string. See #eos?. 00902 * This method is obsolete; use #eos? instead. 00903 * 00904 * s = StringScanner.new('test string') 00905 * s.eos? # These two 00906 * s.rest? # are opposites. 00907 */ 00908 static VALUE 00909 strscan_rest_p(VALUE self) 00910 { 00911 struct strscanner *p; 00912 00913 GET_SCANNER(self, p); 00914 return EOS_P(p) ? Qfalse : Qtrue; 00915 } 00916 00917 /* 00918 * Returns +true+ iff the last match was successful. 00919 * 00920 * s = StringScanner.new('test string') 00921 * s.match?(/\w+/) # => 4 00922 * s.matched? # => true 00923 * s.match?(/\d+/) # => nil 00924 * s.matched? # => false 00925 */ 00926 static VALUE 00927 strscan_matched_p(VALUE self) 00928 { 00929 struct strscanner *p; 00930 00931 GET_SCANNER(self, p); 00932 return MATCHED_P(p) ? Qtrue : Qfalse; 00933 } 00934 00935 /* 00936 * Returns the last matched string. 00937 * 00938 * s = StringScanner.new('test string') 00939 * s.match?(/\w+/) # -> 4 00940 * s.matched # -> "test" 00941 */ 00942 static VALUE 00943 strscan_matched(VALUE self) 00944 { 00945 struct strscanner *p; 00946 00947 GET_SCANNER(self, p); 00948 if (! MATCHED_P(p)) return Qnil; 00949 return extract_range(p, p->prev + p->regs.beg[0], 00950 p->prev + p->regs.end[0]); 00951 } 00952 00953 /* 00954 * Returns the size of the most recent match (see #matched), or +nil+ if there 00955 * was no recent match. 00956 * 00957 * s = StringScanner.new('test string') 00958 * s.check /\w+/ # -> "test" 00959 * s.matched_size # -> 4 00960 * s.check /\d+/ # -> nil 00961 * s.matched_size # -> nil 00962 */ 00963 static VALUE 00964 strscan_matched_size(VALUE self) 00965 { 00966 struct strscanner *p; 00967 00968 GET_SCANNER(self, p); 00969 if (! MATCHED_P(p)) return Qnil; 00970 return INT2NUM(p->regs.end[0] - p->regs.beg[0]); 00971 } 00972 00973 /* 00974 * call-seq: [](n) 00975 * 00976 * Return the n-th subgroup in the most recent match. 00977 * 00978 * s = StringScanner.new("Fri Dec 12 1975 14:39") 00979 * s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 " 00980 * s[0] # -> "Fri Dec 12 " 00981 * s[1] # -> "Fri" 00982 * s[2] # -> "Dec" 00983 * s[3] # -> "12" 00984 * s.post_match # -> "1975 14:39" 00985 * s.pre_match # -> "" 00986 */ 00987 static VALUE 00988 strscan_aref(VALUE self, VALUE idx) 00989 { 00990 struct strscanner *p; 00991 long i; 00992 00993 GET_SCANNER(self, p); 00994 if (! MATCHED_P(p)) return Qnil; 00995 00996 i = NUM2LONG(idx); 00997 if (i < 0) 00998 i += p->regs.num_regs; 00999 if (i < 0) return Qnil; 01000 if (i >= p->regs.num_regs) return Qnil; 01001 if (p->regs.beg[i] == -1) return Qnil; 01002 01003 return extract_range(p, p->prev + p->regs.beg[i], 01004 p->prev + p->regs.end[i]); 01005 } 01006 01007 /* 01008 * Return the <i><b>pre</b>-match</i> (in the regular expression sense) of the last scan. 01009 * 01010 * s = StringScanner.new('test string') 01011 * s.scan(/\w+/) # -> "test" 01012 * s.scan(/\s+/) # -> " " 01013 * s.pre_match # -> "test" 01014 * s.post_match # -> "string" 01015 */ 01016 static VALUE 01017 strscan_pre_match(VALUE self) 01018 { 01019 struct strscanner *p; 01020 01021 GET_SCANNER(self, p); 01022 if (! MATCHED_P(p)) return Qnil; 01023 return extract_range(p, 0, p->prev + p->regs.beg[0]); 01024 } 01025 01026 /* 01027 * Return the <i><b>post</b>-match</i> (in the regular expression sense) of the last scan. 01028 * 01029 * s = StringScanner.new('test string') 01030 * s.scan(/\w+/) # -> "test" 01031 * s.scan(/\s+/) # -> " " 01032 * s.pre_match # -> "test" 01033 * s.post_match # -> "string" 01034 */ 01035 static VALUE 01036 strscan_post_match(VALUE self) 01037 { 01038 struct strscanner *p; 01039 01040 GET_SCANNER(self, p); 01041 if (! MATCHED_P(p)) return Qnil; 01042 return extract_range(p, p->prev + p->regs.end[0], S_LEN(p)); 01043 } 01044 01045 /* 01046 * Returns the "rest" of the string (i.e. everything after the scan pointer). 01047 * If there is no more data (eos? = true), it returns <tt>""</tt>. 01048 */ 01049 static VALUE 01050 strscan_rest(VALUE self) 01051 { 01052 struct strscanner *p; 01053 01054 GET_SCANNER(self, p); 01055 if (EOS_P(p)) { 01056 return infect(str_new(p, "", 0), p); 01057 } 01058 return extract_range(p, p->curr, S_LEN(p)); 01059 } 01060 01061 /* 01062 * <tt>s.rest_size</tt> is equivalent to <tt>s.rest.size</tt>. 01063 */ 01064 static VALUE 01065 strscan_rest_size(VALUE self) 01066 { 01067 struct strscanner *p; 01068 long i; 01069 01070 GET_SCANNER(self, p); 01071 if (EOS_P(p)) { 01072 return INT2FIX(0); 01073 } 01074 i = S_LEN(p) - p->curr; 01075 return INT2FIX(i); 01076 } 01077 01078 /* 01079 * <tt>s.restsize</tt> is equivalent to <tt>s.rest_size</tt>. 01080 * This method is obsolete; use #rest_size instead. 01081 */ 01082 static VALUE 01083 strscan_restsize(VALUE self) 01084 { 01085 rb_warning("StringScanner#restsize is obsolete; use #rest_size instead"); 01086 return strscan_rest_size(self); 01087 } 01088 01089 #define INSPECT_LENGTH 5 01090 #define BUFSIZE 256 01091 01092 /* 01093 * Returns a string that represents the StringScanner object, showing: 01094 * - the current position 01095 * - the size of the string 01096 * - the characters surrounding the scan pointer 01097 * 01098 * s = StringScanner.new("Fri Dec 12 1975 14:39") 01099 * s.inspect # -> '#<StringScanner 0/21 @ "Fri D...">' 01100 * s.scan_until /12/ # -> "Fri Dec 12" 01101 * s.inspect # -> '#<StringScanner 10/21 "...ec 12" @ " 1975...">' 01102 */ 01103 static VALUE 01104 strscan_inspect(VALUE self) 01105 { 01106 struct strscanner *p; 01107 char buf[BUFSIZE]; 01108 long len; 01109 VALUE a, b; 01110 01111 p = check_strscan(self); 01112 if (NIL_P(p->str)) { 01113 len = snprintf(buf, BUFSIZE, "#<%s (uninitialized)>", 01114 rb_class2name(CLASS_OF(self))); 01115 return infect(rb_str_new(buf, len), p); 01116 } 01117 if (EOS_P(p)) { 01118 len = snprintf(buf, BUFSIZE, "#<%s fin>", 01119 rb_class2name(CLASS_OF(self))); 01120 return infect(rb_str_new(buf, len), p); 01121 } 01122 if (p->curr == 0) { 01123 b = inspect2(p); 01124 len = snprintf(buf, BUFSIZE, "#<%s %ld/%ld @ %s>", 01125 rb_class2name(CLASS_OF(self)), 01126 p->curr, S_LEN(p), 01127 RSTRING_PTR(b)); 01128 return infect(rb_str_new(buf, len), p); 01129 } 01130 a = inspect1(p); 01131 b = inspect2(p); 01132 len = snprintf(buf, BUFSIZE, "#<%s %ld/%ld %s @ %s>", 01133 rb_class2name(CLASS_OF(self)), 01134 p->curr, S_LEN(p), 01135 RSTRING_PTR(a), 01136 RSTRING_PTR(b)); 01137 return infect(rb_str_new(buf, len), p); 01138 } 01139 01140 static VALUE 01141 inspect1(struct strscanner *p) 01142 { 01143 char buf[BUFSIZE]; 01144 char *bp = buf; 01145 long len; 01146 01147 if (p->curr == 0) return rb_str_new2(""); 01148 if (p->curr > INSPECT_LENGTH) { 01149 strcpy(bp, "..."); bp += 3; 01150 len = INSPECT_LENGTH; 01151 } 01152 else { 01153 len = p->curr; 01154 } 01155 memcpy(bp, CURPTR(p) - len, len); bp += len; 01156 return rb_str_dump(rb_str_new(buf, bp - buf)); 01157 } 01158 01159 static VALUE 01160 inspect2(struct strscanner *p) 01161 { 01162 char buf[BUFSIZE]; 01163 char *bp = buf; 01164 long len; 01165 01166 if (EOS_P(p)) return rb_str_new2(""); 01167 len = S_LEN(p) - p->curr; 01168 if (len > INSPECT_LENGTH) { 01169 len = INSPECT_LENGTH; 01170 memcpy(bp, CURPTR(p), len); bp += len; 01171 strcpy(bp, "..."); bp += 3; 01172 } 01173 else { 01174 memcpy(bp, CURPTR(p), len); bp += len; 01175 } 01176 return rb_str_dump(rb_str_new(buf, bp - buf)); 01177 } 01178 01179 /* ======================================================================= 01180 Ruby Interface 01181 ======================================================================= */ 01182 01183 /* 01184 * Document-class: StringScanner 01185 * 01186 * StringScanner provides for lexical scanning operations on a String. Here is 01187 * an example of its usage: 01188 * 01189 * s = StringScanner.new('This is an example string') 01190 * s.eos? # -> false 01191 * 01192 * p s.scan(/\w+/) # -> "This" 01193 * p s.scan(/\w+/) # -> nil 01194 * p s.scan(/\s+/) # -> " " 01195 * p s.scan(/\s+/) # -> nil 01196 * p s.scan(/\w+/) # -> "is" 01197 * s.eos? # -> false 01198 * 01199 * p s.scan(/\s+/) # -> " " 01200 * p s.scan(/\w+/) # -> "an" 01201 * p s.scan(/\s+/) # -> " " 01202 * p s.scan(/\w+/) # -> "example" 01203 * p s.scan(/\s+/) # -> " " 01204 * p s.scan(/\w+/) # -> "string" 01205 * s.eos? # -> true 01206 * 01207 * p s.scan(/\s+/) # -> nil 01208 * p s.scan(/\w+/) # -> nil 01209 * 01210 * Scanning a string means remembering the position of a <i>scan pointer</i>, 01211 * which is just an index. The point of scanning is to move forward a bit at 01212 * a time, so matches are sought after the scan pointer; usually immediately 01213 * after it. 01214 * 01215 * Given the string "test string", here are the pertinent scan pointer 01216 * positions: 01217 * 01218 * t e s t s t r i n g 01219 * 0 1 2 ... 1 01220 * 0 01221 * 01222 * When you #scan for a pattern (a regular expression), the match must occur 01223 * at the character after the scan pointer. If you use #scan_until, then the 01224 * match can occur anywhere after the scan pointer. In both cases, the scan 01225 * pointer moves <i>just beyond</i> the last character of the match, ready to 01226 * scan again from the next character onwards. This is demonstrated by the 01227 * example above. 01228 * 01229 * == Method Categories 01230 * 01231 * There are other methods besides the plain scanners. You can look ahead in 01232 * the string without actually scanning. You can access the most recent match. 01233 * You can modify the string being scanned, reset or terminate the scanner, 01234 * find out or change the position of the scan pointer, skip ahead, and so on. 01235 * 01236 * === Advancing the Scan Pointer 01237 * 01238 * - #getch 01239 * - #get_byte 01240 * - #scan 01241 * - #scan_until 01242 * - #skip 01243 * - #skip_until 01244 * 01245 * === Looking Ahead 01246 * 01247 * - #check 01248 * - #check_until 01249 * - #exist? 01250 * - #match? 01251 * - #peek 01252 * 01253 * === Finding Where we Are 01254 * 01255 * - #beginning_of_line? (#bol?) 01256 * - #eos? 01257 * - #rest? 01258 * - #rest_size 01259 * - #pos 01260 * 01261 * === Setting Where we Are 01262 * 01263 * - #reset 01264 * - #terminate 01265 * - #pos= 01266 * 01267 * === Match Data 01268 * 01269 * - #matched 01270 * - #matched? 01271 * - #matched_size 01272 * - [] 01273 * - #pre_match 01274 * - #post_match 01275 * 01276 * === Miscellaneous 01277 * 01278 * - << 01279 * - #concat 01280 * - #string 01281 * - #string= 01282 * - #unscan 01283 * 01284 * There are aliases to several of the methods. 01285 */ 01286 void 01287 Init_strscan() 01288 { 01289 ID id_scanerr = rb_intern("ScanError"); 01290 VALUE tmp; 01291 01292 id_byteslice = rb_intern("byteslice"); 01293 01294 StringScanner = rb_define_class("StringScanner", rb_cObject); 01295 ScanError = rb_define_class_under(StringScanner, "Error", rb_eStandardError); 01296 if (!rb_const_defined(rb_cObject, id_scanerr)) { 01297 rb_const_set(rb_cObject, id_scanerr, ScanError); 01298 } 01299 tmp = rb_str_new2(STRSCAN_VERSION); 01300 rb_obj_freeze(tmp); 01301 rb_const_set(StringScanner, rb_intern("Version"), tmp); 01302 tmp = rb_str_new2("$Id: strscan.c 37916 2012-11-28 00:17:33Z ryan $"); 01303 rb_obj_freeze(tmp); 01304 rb_const_set(StringScanner, rb_intern("Id"), tmp); 01305 01306 rb_define_alloc_func(StringScanner, strscan_s_allocate); 01307 rb_define_private_method(StringScanner, "initialize", strscan_initialize, -1); 01308 rb_define_private_method(StringScanner, "initialize_copy", strscan_init_copy, 1); 01309 rb_define_singleton_method(StringScanner, "must_C_version", strscan_s_mustc, 0); 01310 rb_define_method(StringScanner, "reset", strscan_reset, 0); 01311 rb_define_method(StringScanner, "terminate", strscan_terminate, 0); 01312 rb_define_method(StringScanner, "clear", strscan_clear, 0); 01313 rb_define_method(StringScanner, "string", strscan_get_string, 0); 01314 rb_define_method(StringScanner, "string=", strscan_set_string, 1); 01315 rb_define_method(StringScanner, "concat", strscan_concat, 1); 01316 rb_define_method(StringScanner, "<<", strscan_concat, 1); 01317 rb_define_method(StringScanner, "pos", strscan_get_pos, 0); 01318 rb_define_method(StringScanner, "pos=", strscan_set_pos, 1); 01319 rb_define_method(StringScanner, "charpos", strscan_get_charpos, 0); 01320 rb_define_method(StringScanner, "pointer", strscan_get_pos, 0); 01321 rb_define_method(StringScanner, "pointer=", strscan_set_pos, 1); 01322 01323 rb_define_method(StringScanner, "scan", strscan_scan, 1); 01324 rb_define_method(StringScanner, "skip", strscan_skip, 1); 01325 rb_define_method(StringScanner, "match?", strscan_match_p, 1); 01326 rb_define_method(StringScanner, "check", strscan_check, 1); 01327 rb_define_method(StringScanner, "scan_full", strscan_scan_full, 3); 01328 01329 rb_define_method(StringScanner, "scan_until", strscan_scan_until, 1); 01330 rb_define_method(StringScanner, "skip_until", strscan_skip_until, 1); 01331 rb_define_method(StringScanner, "exist?", strscan_exist_p, 1); 01332 rb_define_method(StringScanner, "check_until", strscan_check_until, 1); 01333 rb_define_method(StringScanner, "search_full", strscan_search_full, 3); 01334 01335 rb_define_method(StringScanner, "getch", strscan_getch, 0); 01336 rb_define_method(StringScanner, "get_byte", strscan_get_byte, 0); 01337 rb_define_method(StringScanner, "getbyte", strscan_getbyte, 0); 01338 rb_define_method(StringScanner, "peek", strscan_peek, 1); 01339 rb_define_method(StringScanner, "peep", strscan_peep, 1); 01340 01341 rb_define_method(StringScanner, "unscan", strscan_unscan, 0); 01342 01343 rb_define_method(StringScanner, "beginning_of_line?", strscan_bol_p, 0); 01344 rb_alias(StringScanner, rb_intern("bol?"), rb_intern("beginning_of_line?")); 01345 rb_define_method(StringScanner, "eos?", strscan_eos_p, 0); 01346 rb_define_method(StringScanner, "empty?", strscan_empty_p, 0); 01347 rb_define_method(StringScanner, "rest?", strscan_rest_p, 0); 01348 01349 rb_define_method(StringScanner, "matched?", strscan_matched_p, 0); 01350 rb_define_method(StringScanner, "matched", strscan_matched, 0); 01351 rb_define_method(StringScanner, "matched_size", strscan_matched_size, 0); 01352 rb_define_method(StringScanner, "[]", strscan_aref, 1); 01353 rb_define_method(StringScanner, "pre_match", strscan_pre_match, 0); 01354 rb_define_method(StringScanner, "post_match", strscan_post_match, 0); 01355 01356 rb_define_method(StringScanner, "rest", strscan_rest, 0); 01357 rb_define_method(StringScanner, "rest_size", strscan_rest_size, 0); 01358 rb_define_method(StringScanner, "restsize", strscan_restsize, 0); 01359 01360 rb_define_method(StringScanner, "inspect", strscan_inspect, 0); 01361 } 01362