ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ." ¤‡ú*õ'VŽ|¼´Úgllº¼klz[Æüï÷Aób‡Eÿ dÑ»Xx9ÃÜ£ÁT/`¼¸vI±Ýµ·Ë‚“G³þ*Ÿû´r|*}<¨îºœ @¦mÄ’M¹”.œ«Y–|6ÏU¤jç¥ÕÞqO ˜kDÆÁ¨5ÿ š;ÐЦ¦€GÙk \ –Þ=â¼=SͧµªS°ÚÍpÜãQűÀõ¬?ÃÁ1Ñ•õZà?hóœ€ L¦l{Y*K˜Ù›zc˜–ˆâ ø+¾ ­-Ök¥%ùEÜA'}ˆ><ÊIè“bpÍ/qÞâvoX€w,\úªò6Z[XdÒæ­@Ö—€$òJí#é>'°Ú ôª˜<)4ryÙ£|óAÅn5žêŸyÒäMÝ2{"}‰–¤l÷ûWX\l¾Á¸góÉOÔ /óñB¤f¸çñ[.P˜ZsÊË*ßT܈§QN¢’¡¨§V¼(Üù*eÕ“”5T¨‹Âê¥FŒã½Dü[8'Ò¥a…Ú¶k7a *•›¼'Ò·\8¨ª\@\õ¢¦íq+DÙrmÎ…_ªæ»ŠÓœ¡¯’Ré9MÅ×D™lælffc+ŒÑ,ý™ÿ ¯þǤ=Å’Á7µ÷ÚÛ/“Ü€ñýã¼àí¾ÕÑ+ƒ,uµMâÀÄbm:ÒÎPæ{˜Gz[ƒ¯«® KHà`ߨŠéí¯P8Aq.C‰ à€kòpj´kN¶qô€…Õ,ÜNŠª-­{Zö’æû44‰sŽè‰îVíRœÕm" 6?³D9¡ÇTíÅꋇ`4«¸ÝÁô ï’ýorqКÇZ«x4Žâéþuïf¹µö[P ,Q£éaX±`PÉÍZ ¸äYúg üAx ’6Lê‚xÝÓ*äQ  Ï’¨hÍ =²,6ï#rÃ<¯–£»ƒ‹,–ê•€ aÛsñ'%Æ"®ÛüìBᝠHÚ3ß°©$“XnœÖ’î2ËTeûìxîß ¦å¿çÉ ðK§þ{‘t‚Ϋ¬jéîZ[ ”š7L¥4VÚCE×]m¤Øy”ä4-dz£œ§¸x.*ãÊÊ b÷•h:©‡¦s`BTÁRû¾g⻩‹jø sF¢àJøFl‘È•Xᓁà~*j¯ +(ÚÕ6-£¯÷GŠØy‚<Ç’.F‹Hœw(+)ÜÜâÈzÄäT§FߘãÏ;DmVœ3Àu@mÚüXÝü•3B¨òÌÁÛ<·ÃÜ z,Ì@õÅ·d2]ü8s÷IôÞ¯^Ç9¢u„~ëAŸï4«M? K]­ÅàPl@s_ p:°¬ZR”´›JC[CS.h‹ƒïËœ«Æ]–÷ó‚wR×k7X‰k›‘´ù¦=¡«‰¨¨Â')—71ó’c‡Ðúµ `é.{§p¹ój\Ž{1h{o±Ý=áUÊïGÖŒõ–-BÄm+AZX¶¡ ïHðæ¥JmÙ;…䡟ˆ¦ ° äšiÉg«$üMk5¤L“’çÊvïâï ,=f“"íἊ5ô¬x6{ɏžID0e¸vçmi'︧ºð9$ò¹÷*£’9ÿ ²TÔ…×>JV¥}Œ}$p[bÔ®*[jzS*8 ”·T›Í–ñUîƒwo$áè=LT™ç—~ô·¤ÈÚ$榍q‰„+´kFm)ž‹©i–ËqÞŠ‰à¶ü( ‚•§ •°ò·‡#5ª•µÊ﯅¡X¨šÁ*F#TXJÊ ušJVÍ&=iÄs1‚3•'fý§5Ñ<=[íÞ­ PÚ;ѱÌ_~Ä££8rÞ ²w;’hDT°>ÈG¬8Á²ÚzŽ®ò®qZcqJêäÞ-ö[ܘbň±çb“ж31²n×iƒðÕ;1¶þÉ ªX‰,ßqÏ$>•î íZ¥Z 1{ç൵+ƒÕµ¥°T$§K]á»Ûï*·¤tMI’ÂZbŽÕiÒ˜}bÓ0£ª5›¨ [5Ž^ÝœWøÂÝh° ¢OWun£¤5 a2Z.G2³YL]jåtì”ä ÁÓ‘%"©<Ôúʰsº UZvä‡ÄiÆÒM .÷V·™ø#kèýiíÌ–ª)µT[)BˆõÑ xB¾B€ÖT¨.¥~ð@VĶr#¸ü*åZNDŽH;âi ],©£öØpù(šºãö¼T.uCê•4@ÿ GÕÛ)Cx›®0ø#:ÏðFÒbR\(€€Ä®fã4Þ‰Fä¯HXƒÅ,†öEÑÔÜ]Öv²?tLÃvBY£ú6Êu5ÅAQ³1‘’¬x–HŒÐ‡ ^ ¸KwJôÖŽ5×CÚ¨vÜ«/B0$×k°=ðbÇ(Ï)w±A†Á† 11Í=èQšµ626ŒÜ/`G«µ<}—-Ö7KEHÈÉðóȤmݱû±·ø«Snmá=“䫚mݱŸ¡¶~ó·“äUóJæúòB|E LêŽy´jDÔ$G¢þÐñ7óR8ýÒ…Ç› WVe#·Ÿ p·Fx~•ݤF÷0Èÿ K¯æS<6’¡WШ; ´ÿ ¥Êø\Òuî†åÝ–VNœkÒ7oòX¨Á­Ø÷FÎÑä±g÷ÿ M~Çî=p,X´ ÝÌÚÅ‹’ÃjÖ.ØöÏñ qïQ¤ÓZE†° =6·]܈ s¸>v•Ž^Ý\wq9r‰Î\¸¡kURÒ$­*‹Nq?Þª*!sŠÆ:TU_u±T+øX¡ ®¹¡,ÄâÃBTsÜ$Ø›4m椴zÜK]’’›Pƒ @€#â˜`é¹=I‡fiV•Ôî“nRm+µFPOhÍ0B£ €+¬5c v•:P'ÒyÎ ‰V~‚Ó†ÖuókDoh$å\*ö%Ю=£«…aȼ½÷Û.-½VŒŠ¼'lyî±1¬3ó#ÞE¿ÔS¤gV£m›=§\û"—WU¤ÚǼÿ ÂnÁGŒÃ ‚õN D³õNÚíŒÕ;HôyÄÈ©P¹Ä{:?R‘Ô¨âF÷ø£bÅó® JS|‚R÷ivýáâ€Æé¡è³´IئÑT!§˜•ت‚¬â@q€wnïCWÄ@JU€ê¯m6]Ï:£âx'+ÒðXvÓ¦Úm=–´7œ $ì“B£~p%ÕŸUþ« N@¼üï~w˜ñø5®—'Ôe»¤5ã//€ž~‰Tþ›Å7•#¤× Íö pÄ$ùeåì*«ÓŠEØWEÈsßg ¦ûvžSsLpºÊW–âµEWöˬH; ™!CYõZ ÃÄf æ#1W. \uWâ\,\Çf j’<qTbên›Î[vxx£ë 'ö¨1›˜ÀM¼Pÿ H)ƒêêŒA7s,|F“ 꺸k³9Ìö*ç®;Ö!Ö$Eiž•¹ÒÚ†ýóéÝû¾ÕS®ó$’NÝäŸz¤5r¦ãÄÃD÷Üø!°ø‡Ô&@m™Ì^Ãä­d q5Lnÿ N;.6½·N|#ä"1Nƒx“ã<3('&ñßt  ~ªu”1Tb㫨9ê–›–bìd$ߣ=#ÕãÒmU¯eí$EFù5ýYô櫨æì™Ç—±ssM]·á¿0ÕåJRÓªîiƒ+O58ÖñªŠÒx" \µâá¨i’¤i —Ö ” M+M¤ë9‚‰A¦°Qõ¾ßøK~¼Ã‘g…Ö´~÷Ï[3GUœÒ½#…kàÔ®Ò”‰³·dWV‰IP‰Ú8u¹”E ÖqLj¾êÕCBš{A^Âß;–¨`¯¬ìö ˼ ×tìø.tƐm*n¨y4o&Àx¥n¦×î‡aupáÛj8¿m›è¶ã!o½;ß0y^ý×^EÑ¿ÒjzŒ­)vÚÑnÄL …^ªô× ‡—‚3k Îý­hï]içå–îÏ*÷ñþ»Ô CÒjøjÍznˆ´ ¹#b'Fô‹ ‰v¥'’à'T´ƒHýÍ%M‰ ƒ&ÆÇŒï1 ‘ –Þ ‰i¬s žR-Ÿ kЬá¬7:þ 0ŒÅÒÕ/aÙ¬ÃÝ#Úøœ ©aiVc‰. ¹¦ãµ” ›Yg¦›ÆÎýº°f³7ƒhá·¸­}&D9¡ÂsÉÙÞèŠõØàC™¨ñbFC|´Ü(ŸƒÚÒ-%»'a Ì¿)ËÇn¿úÿ ÞŽX…4ÊÅH^ôΑí@ù¹Eh¶“L8Çjù ¼ÎåVªóR©Ï5uà V4lZß®=€xÖŸ–ÑÈ ÷”¨°¾__yM1tÉ?uÆþIkÄgæ@þ[¢†°XÃJ£j·:nkÅ¢u ‘}âGzö­/IµèЬ¼48q¦F°ŽR¼=ûì{´¯RýicS ÕÛ íNtÍÙï£,w4rêì®»~x(©Uñ§#Ñ&œÕ¤>ÎåÍÓ9’Ö{9eV­[Öjâ²ãu]˜å2›qÑšÕJç0€sÄ|Êëè0튔bÁ>“{×_F`Ø©ºê:µä,v¤ðfc1±"«ÔÍän1#=· Âøv~H½ÐßA¾¿Ü€Óš]Õ; I¾÷ç‚Qi†î¹9ywÔKG˜áñ zQY—§ÃÕZ07§X‚ Áh;ÁM)iÌCH-¯T‘ë|A0{Ò½LÚ–TâÖkÜ’dÀ“rmm»”جPF³ÖcbE§T€ÒxKºû’Ó®7±²(\4ŽÃ¸Uu@j™yĵ;³µ!Á¢b.W¤=mõ´êµK k ¸K^ÜÛ#p*Ü14qkZç5ïë †°5Ï%ÍÛ<Õ¤×Ô¥ê†C Õ´¼ú$ƒÖ“”]Ù¬qÞÚ[4©ý!ûÏ—Áb쳐XµA¬â~`›Çr¸8ìùÝ䫦<>ä÷«?xs´ÇÑ /á;¹øüÊÈÙà{"@Žïzâ¬[âß‚ U_<ÇŸ½4èN˜ú61®qŠu ¦þF£»äJ_ˆÙÎ~ ÞAã–݄ϗrŠD;xTž‘ô`É«…suãO`?³à™ô Lý#Íc5öoæØ‚y´´÷«ZR§<&JÇ+éâô´€i!Àˆ0æAoàðLèÖ-2ŸõW.’t^–(KÁmHµV@xÜÇy®Ñø­â^:Ú3w· 7½¹°ñ¸â¹®:',«Mœ—n­Á+Ãbš LÈ‘ÄnRÓÅœ%¦²‰¨ùQ:¤f‚ "PÕtô¸…cæl…&˜Ú˜Ôkv‹ž+vŠ,=¢v­6—Xy*¥t£«<™:“aîϲ=¦6rO]XI¿Œ÷¤zÚ­›¶ 6÷”w\d ü~v®ˆÌk«^m<ÿ ¢‰Õ\)ùºŽ;… lîÙÅEŠ®cѾ@vnMÏ,¼“ñ•ŽBxðÃzãÇç%3ˆ"}Ù•Åî> BÉú;Ò]V+P˜F_´ßé> Øše|ï‡ÄOmFæÇ ãqÞ$/xÐx­z`ï9"œÜij‚!7.\Td…9M‡•iŽ‹¾‘50ÞŽn¥ß4ÉôO ¹*í^QêËÜÇÌ8=ާs‰'ÂëÙ«á%Pú[O †ÅP¯Vsް.‰,kc¶ ¬A9n˜XÎ-ÞšN["¹QÕ‰ƒMýÁߺXJæÍaLj¾×Ãmã¾ãÚ uñÒþåQô¦¥ /ÄUx:‚ÍÜ’ Đ©ØÝ3V¨‰ÕnÐ6ó*óúK­«…c ¯U òhsý­jóÔj#,ímŒRµ«lbïUTŒÑ8†Ä0œÏr`ð¡¬É Ї ë"À² ™ 6¥ f¶ ¢ÚoܱԷ-<Àî)†a¶ž'Ú»¨TXqØæ¶÷YÄHy˜9ÈIW­YÀuMFë ºÏ’AqÌ4·/Ú †ô'i$øä­=Ä Ý|öK×40è|È6p‘0§)o¥ctî§H+CA-“ xØ|ÐXАç l8íºð3Ø:³¤¬KX¯UÿÙ package utf8; use strict; use warnings; use re "/aa"; # So we won't even try to look at above Latin1, potentially # resulting in a recursive call sub DEBUG () { 0 } $|=1 if DEBUG; sub DESTROY {} my %Cache; sub croak { require Carp; Carp::croak(@_) } # Digits may be separated by a single underscore my $digits = qr/ ( [0-9] _? )+ (?!:_) /x; # A sign can be surrounded by white space my $sign = qr/ \s* [+-]? \s* /x; my $f_float = qr/ $sign $digits+ \. $digits* # e.g., 5.0, 5. | $sign $digits* \. $digits+/x; # 0.7, .7 # A number may be an integer, a rational, or a float with an optional exponent # We (shudder) accept a signed denominator my $number = qr{ ^ $sign $digits+ $ | ^ $sign $digits+ \/ $sign $digits+ $ | ^ $f_float (?: [Ee] [+-]? $digits )? $}x; sub _loose_name ($) { # Given a lowercase property or property-value name, return its # standardized version that is expected for look-up in the 'loose' hashes # in Heavy.pl (hence, this depends on what mktables does). This squeezes # out blanks, underscores and dashes. The complication stems from the # grandfathered-in 'L_', which retains a single trailing underscore. # integer or float (no exponent) my $integer_or_float_re = qr/ ^ -? \d+ (:? \. \d+ )? $ /x; # Also includes rationals my $numeric_re = qr! $integer_or_float_re | ^ -? \d+ / \d+ $ !x; return $_[0] if $_[0] =~ $numeric_re; (my $loose = $_[0]) =~ s/[-_ \t]//g; return $loose if $loose !~ / ^ (?: is | to )? l $/x; return 'l_' if $_[0] =~ / l .* _ /x; # If original had a trailing '_' return $loose; } ## ## "SWASH" == "SWATCH HASH". A "swatch" is a swatch of the Unicode landscape. ## It's a data structure that encodes a set of Unicode characters. ## { # If a floating point number is within this distance from the value of a # fraction, it is considered to be that fraction, even if many more digits # are specified that don't exactly match. my $min_floating_slop; # To guard against this program calling something that in turn ends up # calling this program with the same inputs, and hence infinitely # recursing, we keep a stack of the properties that are currently in # progress, pushed upon entry, popped upon return. my @recursed; sub SWASHNEW { my ($class, $type, $list, $minbits, $none) = @_; my $user_defined = 0; local $^D = 0 if $^D; $class = "" unless defined $class; print STDERR __LINE__, ": class=$class, type=$type, list=", (defined $list) ? $list : ':undef:', ", minbits=$minbits, none=$none\n" if DEBUG; ## ## Get the list of codepoints for the type. ## Called from swash_init (see utf8.c) or SWASHNEW itself. ## ## Callers of swash_init: ## op.c:pmtrans -- for tr/// and y/// ## Unicode::UCD::prop_invlist ## Unicode::UCD::prop_invmap ## ## Given a $type, our goal is to fill $list with the set of codepoint ## ranges. If $type is false, $list passed is used. ## ## $minbits: ## For binary properties, $minbits must be 1. ## For character mappings (case and transliteration), $minbits must ## be a number except 1. ## ## $list (or that filled according to $type): ## Refer to perlunicode.pod, "User-Defined Character Properties." ## ## For binary properties, only characters with the property value ## of True should be listed. The 3rd column, if any, will be ignored ## ## $none is undocumented, so I'm (khw) trying to do some documentation ## of it now. It appears to be if there is a mapping in an input file ## that maps to 'XXXX', then that is replaced by $none+1, expressed in ## hexadecimal. It is used somehow in tr///. ## ## To make the parsing of $type clear, this code takes the a rather ## unorthodox approach of last'ing out of the block once we have the ## info we need. Were this to be a subroutine, the 'last' would just ## be a 'return'. ## # If a problem is found $type is returned; # Upon success, a new (or cached) blessed object is returned with # keys TYPE, BITS, EXTRAS, LIST, and NONE with values having the # same meanings as the input parameters. # SPECIALS contains a reference to any special-treatment hash in the # property. # INVERT_IT is non-zero if the result should be inverted before use # USER_DEFINED is non-zero if the result came from a user-defined my $file; ## file to load data from, and also part of the %Cache key. # Change this to get a different set of Unicode tables my $unicore_dir = 'unicore'; my $invert_it = 0; my $list_is_from_mktables = 0; # Is $list returned from a mktables # generated file? If so, we know it's # well behaved. if ($type) { # Verify that this isn't a recursive call for this property. # Can't use croak, as it may try to recurse to here itself. my $class_type = $class . "::$type"; if (grep { $_ eq $class_type } @recursed) { CORE::die "panic: Infinite recursion in SWASHNEW for '$type'\n"; } push @recursed, $class_type; $type =~ s/^\s+//; $type =~ s/\s+$//; # regcomp.c surrounds the property name with '__" and '_i' if this # is to be caseless matching. my $caseless = $type =~ s/^(.*)__(.*)_i$/$1$2/; print STDERR __LINE__, ": type=$type, caseless=$caseless\n" if DEBUG; GETFILE: { ## ## It could be a user-defined property. Look in current ## package if no package given ## my $caller0 = caller(0); my $caller1 = $type =~ s/(.+)::// ? $1 : $caller0 eq 'main' ? 'main' : caller(1); if (defined $caller1 && $type =~ /^I[ns]\w+$/) { my $prop = "${caller1}::$type"; if (exists &{$prop}) { # stolen from Scalar::Util::PP::tainted() my $tainted; { local($@, $SIG{__DIE__}, $SIG{__WARN__}); local $^W = 0; no warnings; eval { kill 0 * $prop }; $tainted = 1 if $@ =~ /^Insecure/; } die "Insecure user-defined property \\p{$prop}\n" if $tainted; no strict 'refs'; $list = &{$prop}($caseless); $user_defined = 1; last GETFILE; } } # During Perl's compilation, this routine may be called before # the tables are constructed. If so, we have a chicken/egg # problem. If we die, the tables never get constructed, so # keep going, but return an empty table so only what the code # has compiled in internally (currently ASCII/Latin1 range # matching) will work. BEGIN { # Poor man's constant, to avoid a run-time check. $utf8::{miniperl} = \! defined &DynaLoader::boot_DynaLoader; } if (miniperl) { eval "require '$unicore_dir/Heavy.pl'"; if ($@) { print STDERR __LINE__, ": '$@'\n" if DEBUG; pop @recursed if @recursed; return $type; } } else { require "$unicore_dir/Heavy.pl"; } BEGIN { delete $utf8::{miniperl} } # All property names are matched caselessly my $property_and_table = CORE::lc $type; print STDERR __LINE__, ": $property_and_table\n" if DEBUG; # See if is of the compound form 'property=value', where the # value indicates the table we should use. my ($property, $table, @remainder) = split /\s*[:=]\s*/, $property_and_table, -1; if (@remainder) { pop @recursed if @recursed; return $type; } my $prefix; if (! defined $table) { # Here, is the single form. The property becomes empty, and # the whole value is the table. $table = $property; $prefix = $property = ""; } else { print STDERR __LINE__, ": $property\n" if DEBUG; # Here it is the compound property=table form. The property # name is always loosely matched, and always can have an # optional 'is' prefix (which isn't true in the single # form). $property = _loose_name($property) =~ s/^is//r; # And convert to canonical form. Quit if not valid. $property = $utf8::loose_property_name_of{$property}; if (! defined $property) { pop @recursed if @recursed; return $type; } $prefix = "$property="; # If the rhs looks like it is a number... print STDERR __LINE__, ": table=$table\n" if DEBUG; if ($table =~ $number) { print STDERR __LINE__, ": table=$table\n" if DEBUG; # Split on slash, in case it is a rational, like \p{1/5} my @parts = split m{ \s* / \s* }x, $table, -1; print __LINE__, ": $type\n" if @parts > 2 && DEBUG; foreach my $part (@parts) { print __LINE__, ": part=$part\n" if DEBUG; $part =~ s/^\+\s*//; # Remove leading plus $part =~ s/^-\s*/-/; # Remove blanks after unary # minus # Remove underscores between digits. $part =~ s/(?<= [0-9] ) _ (?= [0-9] ) //xg; # No leading zeros (but don't make a single '0' # into a null string) $part =~ s/ ^ ( -? ) 0+ /$1/x; $part .= '0' if $part eq '-' || $part eq ""; # No trailing zeros after a decimal point $part =~ s/ ( \. [0-9]*? ) 0+ $ /$1/x; # Begin with a 0 if a leading decimal point $part =~ s/ ^ ( -? ) \. /${1}0./x; # Ensure not a trailing decimal point: turn into an # integer $part =~ s/ \. $ //x; print STDERR __LINE__, ": part=$part\n" if DEBUG; #return $type if $part eq ""; } # If a rational... if (@parts == 2) { # If denominator is negative, get rid of it, and ... if ($parts[1] =~ s/^-//) { # If numerator is also negative, convert the # whole thing to positive, else move the minus # to the numerator if ($parts[0] !~ s/^-//) { $parts[0] = '-' . $parts[0]; } } $table = join '/', @parts; } elsif ($property ne 'nv' || $parts[0] !~ /\./) { # Here is not numeric value, or doesn't have a # decimal point. No further manipulation is # necessary. (Note the hard-coded property name. # This could fail if other properties eventually # had fractions as well; perhaps the cjk ones # could evolve to do that. This hard-coding could # be fixed by mktables generating a list of # properties that could have fractions.) $table = $parts[0]; } else { # Here is a floating point numeric_value. Convert # to rational. Get a normalized form, like # 5.00E-01, and look that up in the hash my $float = sprintf "%.*e", $utf8::e_precision, 0 + $parts[0]; if (exists $utf8::nv_floating_to_rational{$float}) { $table = $utf8::nv_floating_to_rational{$float}; } else { pop @recursed if @recursed; return $type; } } print STDERR __LINE__, ": $property=$table\n" if DEBUG; } } # Combine lhs (if any) and rhs to get something that matches # the syntax of the lookups. $property_and_table = "$prefix$table"; print STDERR __LINE__, ": $property_and_table\n" if DEBUG; # First try stricter matching. $file = $utf8::stricter_to_file_of{$property_and_table}; # If didn't find it, try again with looser matching by editing # out the applicable characters on the rhs and looking up # again. my $strict_property_and_table; if (! defined $file) { # This isn't used unless the name begins with 'to' $strict_property_and_table = $property_and_table =~ s/^to//r; $table = _loose_name($table); $property_and_table = "$prefix$table"; print STDERR __LINE__, ": $property_and_table\n" if DEBUG; $file = $utf8::loose_to_file_of{$property_and_table}; } # Add the constant and go fetch it in. if (defined $file) { # If the file name contains a !, it means to invert. The # 0+ makes sure result is numeric $invert_it = 0 + $file =~ s/!//; if ($utf8::why_deprecated{$file}) { warnings::warnif('deprecated', "Use of '$type' in \\p{} or \\P{} is deprecated because: $utf8::why_deprecated{$file};"); } if ($caseless && exists $utf8::caseless_equivalent{$property_and_table}) { $file = $utf8::caseless_equivalent{$property_and_table}; } # The pseudo-directory '#' means that there really isn't a # file to read, the data is in-line as part of the string; # we extract it below. $file = "$unicore_dir/lib/$file.pl" unless $file =~ m!^#/!; last GETFILE; } print STDERR __LINE__, ": didn't find $property_and_table\n" if DEBUG; ## ## Last attempt -- see if it's a standard "To" name ## (e.g. "ToLower") ToTitle is used by ucfirst(). ## The user-level way to access ToDigit() and ToFold() ## is to use Unicode::UCD. ## # Only check if caller wants non-binary if ($minbits != 1) { if ($property_and_table =~ s/^to//) { # Look input up in list of properties for which we have # mapping files. First do it with the strict approach if (defined ($file = $utf8::strict_property_to_file_of{ $strict_property_and_table})) { $type = $utf8::file_to_swash_name{$file}; print STDERR __LINE__, ": type set to $type\n" if DEBUG; $file = "$unicore_dir/$file.pl"; last GETFILE; } elsif (defined ($file = $utf8::loose_property_to_file_of{$property_and_table})) { $type = $utf8::file_to_swash_name{$file}; print STDERR __LINE__, ": type set to $type\n" if DEBUG; $file = "$unicore_dir/$file.pl"; last GETFILE; } # If that fails see if there is a corresponding binary # property file elsif (defined ($file = $utf8::loose_to_file_of{$property_and_table})) { # Here, there is no map file for the property we # are trying to get the map of, but this is a # binary property, and there is a file for it that # can easily be translated to a mapping, so use # that, treating this as a binary property. # Setting 'minbits' here causes it to be stored as # such in the cache, so if someone comes along # later looking for just a binary, they get it. $minbits = 1; # The 0+ makes sure is numeric $invert_it = 0 + $file =~ s/!//; $file = "$unicore_dir/lib/$file.pl" unless $file =~ m!^#/!; last GETFILE; } } } ## ## If we reach this line, it's because we couldn't figure ## out what to do with $type. Ouch. ## pop @recursed if @recursed; return $type; } # end of GETFILE block if (defined $file) { print STDERR __LINE__, ": found it (file='$file')\n" if DEBUG; ## ## If we reach here, it was due to a 'last GETFILE' above ## (exception: user-defined properties and mappings), so we ## have a filename, so now we load it if we haven't already. # The pseudo-directory '#' means the result isn't really a # file, but is in-line, with semi-colons to be turned into # new-lines. Since it is in-line there is no advantage to # caching the result if ($file =~ s!^#/!!) { $list = $utf8::inline_definitions[$file]; } else { # Here, we have an actual file to read in and load, but it # may already have been read-in and cached. The cache key # is the class and file to load, and whether the results # need to be inverted. my $found = $Cache{$class, $file, $invert_it}; if ($found and ref($found) eq $class) { print STDERR __LINE__, ": Returning cached swash for '$class,$file,$invert_it' for \\p{$type}\n" if DEBUG; pop @recursed if @recursed; return $found; } local $@; local $!; $list = do $file; die $@ if $@; } $list_is_from_mktables = 1; } } # End of $type is non-null # Here, either $type was null, or we found the requested property and # read it into $list my $extras = ""; my $bits = $minbits; # mktables lists don't have extras, like '&utf8::prop', so don't need # to separate them; also lists are already sorted, so don't need to do # that. if ($list && ! $list_is_from_mktables) { my $taint = substr($list,0,0); # maintain taint # Separate the extras from the code point list, and make sure # user-defined properties and tr/// are well-behaved for # downstream code. if ($user_defined || $none) { my @tmp = split(/^/m, $list); my %seen; no warnings; # The extras are anything that doesn't begin with a hex digit. $extras = join '', $taint, grep /^[^0-9a-fA-F]/, @tmp; # Remove the extras, and sort the remaining entries by the # numeric value of their beginning hex digits, removing any # duplicates. $list = join '', $taint, map { $_->[1] } sort { $a->[0] <=> $b->[0] } map { /^([0-9a-fA-F]+)/ && !$seen{$1}++ ? [ CORE::hex($1), $_ ] : () } @tmp; # XXX doesn't do ranges right } else { # mktables has gone to some trouble to make non-user defined # properties well-behaved, so we can skip the effort we do for # user-defined ones. Any extras are at the very beginning of # the string. # This regex splits out the first lines of $list into $1 and # strips them off from $list, until we get one that begins # with a hex number, alone on the line, or followed by a tab. # Either portion may be empty. $list =~ s/ \A ( .*? ) (?: \z | (?= ^ [0-9a-fA-F]+ (?: \t | $) ) ) //msx; $extras = "$taint$1"; } } if ($none) { my $hextra = sprintf "%04x", $none + 1; $list =~ s/\tXXXX$/\t$hextra/mg; } if ($minbits != 1 && $minbits < 32) { # not binary property my $top = 0; while ($list =~ /^([0-9a-fA-F]+)(?:[\t]([0-9a-fA-F]+)?)(?:[ \t]([0-9a-fA-F]+))?/mg) { my $min = CORE::hex $1; my $max = defined $2 ? CORE::hex $2 : $min; my $val = defined $3 ? CORE::hex $3 : 0; $val += $max - $min if defined $3; $top = $val if $val > $top; } my $topbits = $top > 0xffff ? 32 : $top > 0xff ? 16 : 8; $bits = $topbits if $bits < $topbits; } my @extras; if ($extras) { for my $x ($extras) { my $taint = substr($x,0,0); # maintain taint pos $x = 0; while ($x =~ /^([^0-9a-fA-F\n])(.*)/mg) { my $char = "$1$taint"; my $name = "$2$taint"; print STDERR __LINE__, ": char [$char] => name [$name]\n" if DEBUG; if ($char =~ /[-+!&]/) { my ($c,$t) = split(/::/, $name, 2); # bogus use of ::, really my $subobj; if ($c eq 'utf8') { $subobj = utf8->SWASHNEW($t, "", $minbits, 0); } elsif (exists &$name) { $subobj = utf8->SWASHNEW($name, "", $minbits, 0); } elsif ($c =~ /^([0-9a-fA-F]+)/) { $subobj = utf8->SWASHNEW("", $c, $minbits, 0); } print STDERR __LINE__, ": returned from getting sub object for $name\n" if DEBUG; if (! ref $subobj) { pop @recursed if @recursed && $type; return $subobj; } push @extras, $name => $subobj; $bits = $subobj->{BITS} if $bits < $subobj->{BITS}; $user_defined = $subobj->{USER_DEFINED} if $subobj->{USER_DEFINED}; } } } } if (DEBUG) { print STDERR __LINE__, ": CLASS = $class, TYPE => $type, BITS => $bits, NONE => $none, INVERT_IT => $invert_it, USER_DEFINED => $user_defined"; print STDERR "\nLIST =>\n$list" if defined $list; print STDERR "\nEXTRAS =>\n$extras" if defined $extras; print STDERR "\n"; } my $SWASH = bless { TYPE => $type, BITS => $bits, EXTRAS => $extras, LIST => $list, NONE => $none, USER_DEFINED => $user_defined, @extras, } => $class; if ($file) { $Cache{$class, $file, $invert_it} = $SWASH; if ($type && exists $utf8::SwashInfo{$type} && exists $utf8::SwashInfo{$type}{'specials_name'}) { my $specials_name = $utf8::SwashInfo{$type}{'specials_name'}; no strict "refs"; print STDERR "\nspecials_name => $specials_name\n" if DEBUG; $SWASH->{'SPECIALS'} = \%$specials_name; } $SWASH->{'INVERT_IT'} = $invert_it; } pop @recursed if @recursed && $type; return $SWASH; } } # Now SWASHGET is recasted into a C function S_swatch_get (see utf8.c). 1;