Index: trunk/library/jquery/jquery-1.6.4.min.js
===================================================================
--- trunk/library/jquery/jquery-1.6.4.min.js	(revision 5146)
+++ trunk/library/jquery/jquery-1.6.4.min.js	(revision 5146)
@@ -0,0 +1,4 @@
+/*! jQuery v1.6.4 http://jquery.com/ | http://jquery.org/license */
+(function(a,b){function cu(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cr(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cq(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cp(){cn=b}function co(){setTimeout(cp,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function bZ(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function bY(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bA.test(a)?d(a,e):bY(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)bY(a+"["+e+"]",b[e],c,d);else d(a,b)}function bX(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bW(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bP,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bW(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bW(a,c,d,e,"*",g));return l}function bV(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bL),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function by(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bt:bu;if(d>0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bv(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bl(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bd,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bk(a){f.nodeName(a,"input")?bj(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bj)}function bj(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bi(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bh(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bg(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bf(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function V(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(Q.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function U(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function M(a,b){return(a&&a!=="*"?a+".":"")+b.replace(y,"`").replace(z,"&")}function L(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;i<s.length;i++)g=s[i],g.origType.replace(w,"")===a.type?q.push(g.selector):s.splice(i--,1);e=f(a.target).closest(q,a.currentTarget);for(j=0,k=e.length;j<k;j++){m=e[j];for(i=0;i<s.length;i++){g=s[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,d=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,d=f(a.relatedTarget).closest(g.selector)[0],d&&f.contains(h,d)&&(d=h);(!d||d!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){e=p[j];if(c&&e.level>c)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function J(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function D(){return!0}function C(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function K(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(K,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,x=/^-ms-/,y=function(a,b){return(b+"").toUpperCase()},z=d.userAgent,A,B,C,D=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=Array.prototype.push,G=Array.prototype.slice,H=String.prototype.trim,I=Array.prototype.indexOf,J={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.4",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?F.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),B.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:F,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;B.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!B){B=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",C,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",C),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&K()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):J[D.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!E.call(a,"constructor")&&!E.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||E.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(x,"ms-").replace(w,y)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:H?function(a){return a==null?"":H.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?F.call(c,a):e.merge(c,a)}return c},inArray:function(a,b){if(!b)return-1;if(I)return I.call(b,a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=G.call(arguments,2),g=function(){return a.apply(c,f.concat(G.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=s.exec(a)||t.exec(a)||u.exec(a)||a.indexOf("compatible")<0&&v.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){J["[object "+b+"]"]=b.toLowerCase()}),A=e.uaMatch(z),A.browser&&(e.browser[A.browser]=!0,e.browser.version=A.version),e.browser.webkit&&(e.browser.safari=!0),j.test("Â ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?C=function(){c.removeEventListener("DOMContentLoaded",C,!1),e.ready()}:c.attachEvent&&(C=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",C),e.ready())});return e}(),g="done fail isResolved isRejected promise then always pipe".split(" "),h=[].slice;f.extend({_Deferred:function(){var a=[],b,c,d,e={done:function(){if(!d){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=f.type(i),j==="array"?e.done.apply(e,i):j==="function"&&a.push(i);k&&e.resolveWith(k[0],k[1])}return this},resolveWith:function(e,f){if(!d&&!b&&!c){f=f||[],c=1;try{while(a[0])a.shift().apply(e,f)}finally{b=[e,f],c=0}}return this},resolve:function(){e.resolveWith(this,arguments);return this},isResolved:function(){return!!c||!!b},cancel:function(){d=1,a=[];return this}};return e},Deferred:function(a){var b=f._Deferred(),c=f._Deferred(),d;f.extend(b,{then:function(a,c){b.done(a).fail(c);return this},always:function(){return b.done.apply(b,arguments).fail.apply(this,arguments)},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,pipe:function(a,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[c,"reject"]},function(a,c){var e=c[0],g=c[1],h;f.isFunction(e)?b[a](function(){h=e.apply(this,arguments),h&&f.isFunction(h.promise)?h.promise().then(d.resolve,d.reject):d[g+"With"](this===b?d:this,[h])}):b[a](d[g])})}).promise()},promise:function(a){if(a==null){if(d)return d;d=a={}}var c=g.length;while(c--)a[g[c]]=b[g[c]];return a}}),b.done(c.cancel).fail(b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){function i(a){return function(c){b[a]=arguments.length>1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c<d;c++)b[c]&&f.isFunction(b[c].promise)?b[c].promise().then(i(c),g.reject):--e;e||g.resolveWith(g,b)}else g!==a&&g.resolveWith(g,d?[a]:[]);return g.promise()}}),f.support=function(){var a=c.createElement("div"),b=c.documentElement,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;a.setAttribute("className","t"),a.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},m&&f.extend(p,{position:"absolute",left:"-1000px",top:"-1000px"});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i=f.expando,j=typeof c=="string",k=a.nodeType,l=k?f.cache:a,m=k?a[f.expando]:a[f.expando]&&f.expando;if((!m||e&&m&&l[m]&&!l[m][i])&&j&&d===b)return;m||(k?a[f.expando]=m=++f.uuid:m=f.expando),l[m]||(l[m]={},k||(l[m].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?l[m][i]=f.extend(l[m][i],c):l[m]=f.extend(l[m],c);g=l[m],e&&(g[i]||(g[i]={}),g=g[i]),d!==b&&(g[f.camelCase(c)]=d);if(c==="events"&&!g[c])return g[i]&&g[i].events;j?(h=g[c],h==null&&(h=g[f.camelCase(c)])):h=g;return h}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e=f.expando,g=a.nodeType,h=g?f.cache:a,i=g?a[f.expando]:f.expando;if(!h[i])return;if(b){d=c?h[i][e]:h[i];if(d){d[b]||(b=f.camelCase(b)),delete d[b];if(!l(d))return}}if(c){delete h[i][e];if(!l(h[i]))return}var j=h[i][e];f.support.deleteExpando||!h.setInterval?delete h[i]:h[i]=null,j?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=j):g&&(f.support.deleteExpando?delete a[f.expando]:a.removeAttribute?a.removeAttribute(f.expando):a[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h<i;h++)g=e[h].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),k(this[0],g,d[g]))}}return d}if(typeof a=="object")return this.each(function(){f.data(this,a)});var j=a.split(".");j[1]=j[1]?"."+j[1]:"";if(c===b){d=this.triggerHandler("getData"+j[1]+"!",[j[0]]),d===b&&this.length&&(d=f.data(this[0],a),d=k(this[0],a,d));return d===b&&j[1]?this.data(j[0]):d}return this.each(function(){var b=f(this),d=[j[0],c];b.triggerHandler("setData"+j[1]+"!",d),f.data(this,a,c),b.triggerHandler("changeData"+j[1]+"!",d)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,c){a&&(c=(c||"fx")+"mark",f.data(a,c,(f.data(a,c,b,!0)||0)+1,!0))},_unmark:function(a,c,d){a!==!0&&(d=c,c=a,a=!1);if(c){d=d||"fx";var e=d+"mark",g=a?0:(f.data(c,e,b,!0)||1)-1;g?f.data(c,e,g,!0):(f.removeData(c,e,!0),m(c,d,"mark"))}},queue:function(a,c,d){if(a){c=(c||"fx")+"queue";var e=f.data(a,c,b,!0);d&&(!e||f.isArray(d)?e=f.data(a,c,f.makeArray(d),!0):e.push(d));return e||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e;d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),d.call(a,function(){f.dequeue(a,b)})),c.length||(f.removeData(a,b+"queue",!0),m(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){f.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f._Deferred(),!0))h++,l.done(m);m();return d.promise()}});var n=/[\n\t\r]/g,o=/\s+/,p=/\r/g,q=/^(?:button|input)$/i,r=/^(?:button|input|object|select|textarea)$/i,s=/^a(?:rea)?$/i,t=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,u,v;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(o);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(o);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(n," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(o);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(n," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h<i;h++){var j=e[h];if(j.selected&&(f.support.optDisabled?!j.disabled:j.getAttribute("disabled")===null)&&(!j.parentNode.disabled||!f.nodeName(j.parentNode,"optgroup"))){b=f(j).val();if(g)return b;d.push(b)}}if(g&&!d.length&&e.length)return f(e[c]).val();return d},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=v:u&&(i=u)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.attr(a,b,""),a.removeAttribute(b),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(u&&f.nodeName(a,"button"))return u.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(u&&f.nodeName(a,"button"))return u.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==null?g:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabIndex=f.propHooks.tabIndex,v={get:function(a,c){var d;return f.prop(a,c)===!0||(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(u=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var w=/\.(.*)$/,x=/^(?:textarea|input|select)$/i,y=/\./g,z=/ /g,A=/[^\w\s.|`]/g,B=function(a){return a.replace(A,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=C;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=C);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),B).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))f.event.remove(a,r,q.handler,j),p.splice(j--,1)}continue}o=f.event.special[h]||{};for(j=e||0;j<p.length;j++){q=p[j];if(d.guid===q.guid){if(l||n.test(q.namespace))e==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(e!=null)break}}if(p.length===0||e!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&f.removeEvent(a,h,s.handle),g=null,delete 
+t[h]}if(f.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,f.isEmptyObject(s)&&f.removeData(a,b,!0)}}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){var h=c.type||c,i=[],j;h.indexOf("!")>=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,M(a.origType,a.selector),f.extend({},a,{handler:L,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,M(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?D:C):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=D;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=D;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=D,this.stopPropagation()},isDefaultPrevented:C,isPropagationStopped:C,isImmediatePropagationStopped:C};var E=function(a){var b=a.relatedTarget,c=!1,d=a.type;a.type=a.data,b!==this&&(b&&(c=f.contains(this,b)),c||(f.event.handle.apply(this,arguments),a.type=d))},F=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?F:E,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?F:E)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=f.nodeName(b,"input")||f.nodeName(b,"button")?b.type:"";(c==="submit"||c==="image")&&f(b).closest("form").length&&J("submit",this,arguments)}),f.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=f.nodeName(b,"input")||f.nodeName(b,"button")?b.type:"";(c==="text"||c==="password")&&f(b).closest("form").length&&a.keyCode===13&&J("submit",this,arguments)});else return!1},teardown:function(a){f.event.remove(this,".specialSubmit")}});if(!f.support.changeBubbles){var G,H=function(a){var b=f.nodeName(a,"input")?a.type:"",c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},I=function(c){var d=c.target,e,g;if(!!x.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=H(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:I,beforedeactivate:I,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&I.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&I.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",H(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in G)f.event.add(this,c+".specialChange",G[c]);return x.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return x.test(this.nodeName)}},G=f.event.special.change.filters,G.focus=G.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i<j;i++)f.event.add(this[i],a,g,d);return this}}),f.fn.extend({unbind:function(a,b){if(typeof a=="object"&&!a.preventDefault)for(var c in a)this.unbind(c,a[c]);else for(var d=0,e=this.length;d<e;d++)f.event.remove(this[d],a,b);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f.data(this,"lastToggle"+a.guid)||0)%d;f.data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var K={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};f.each(["live","die"],function(a,c){f.fn[c]=function(a,d,e,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:f(this.context);if(typeof a=="object"&&!a.preventDefault){for(var o in a)n[c](o,d,a[o],m);return this}if(c==="die"&&!a&&g&&g.charAt(0)==="."){n.unbind(g);return this}if(d===!1||f.isFunction(d))e=d||C,d=b;a=(a||"").split(" ");while((h=a[i++])!=null){j=w.exec(h),k="",j&&(k=j[0],h=h.replace(w,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}l=h,K[h]?(a.push(K[h]+k),h=h+k):h=(K[h]||h)+k;if(c==="live")for(var p=0,q=n.length;p<q;p++)f.event.add(n[p],"live."+M(h,m),{data:d,selector:m,handler:e,origType:h,origHandler:e,preType:l});else n.unbind("live."+M(h,m),e)}return this}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}if(i.nodeType===1){f||(i.sizcache=c,i.sizset=g);if(typeof b!="string"){if(i===b){j=!0;break}}else if(k.filter(b,[i]).length>0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}i=i[a]}d[g]=j}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},k.matches=function(a,b){return k(a,null,null,b)},k.matchesSelector=function(a,b){return k(b,null,null,[a]).length>0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e<f;e++){var g,h=l.order[e];if(g=l.leftMatch[h].exec(a)){var j=g[1];g.splice(1,1);if(j.substr(j.length-1)!=="\\"){g[1]=(g[1]||"").replace(i,""),d=l.find[h](g,b,c);if(d!=null){a=a.replace(l.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},k.filter=function(a,c,d,e){var f,g,h=a,i=[],j=c,m=c&&c[0]&&k.isXML(c[0]);while(a&&c.length){for(var n in l.filter)if((f=l.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=l.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;j===i&&(i=[]);if(l.preFilter[n]){f=l.preFilter[n](f,j,d,i,e,m);if(!f)g=o=!0;else if(f===!0)continue}if(f)for(var s=0;(p=j[s])!=null;s++)if(p){o=q(p,f,s,j);var t=e^!!o;d&&o!=null?t?g=!0:j[s]=!1:t&&(i.push(p),g=!0)}if(o!==b){d||(j=i),a=a.replace(l.match[n],"");if(!g)return[];break}}if(a===h)if(g==null)k.error(a);else break;h=a}return j},k.error=function(a){throw"Syntax error, unrecognized expression: "+a};var l=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!j.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&k.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&k.filter(b,a,!0)}},"":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("parentNode",b,f,a,e,c)},"~":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("previousSibling",b,f,a,e,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(i,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}k.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c<f;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var r,s;c.documentElement.compareDocumentPosition?r=function(a,b){if(a===b){g=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(r=function(a,b){if(a===b){g=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return s(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return s(e[k],f[k]);return k===c?s(a,f[k],-1):s(e[k],b,1)},s=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),k.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=k.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)k(a,f[g],d);return k.filter(e,d)};f.find=k,f.expr=k.selectors,f.expr[":"]=f.expr.filters,f.unique=k.uniqueSort,f.text=k.getText,f.isXMLDoc=k.isXML,f.contains=k.contains}();var N=/Until$/,O=/^(?:parents|prevUntil|prevAll)/,P=/,/,Q=/^.[^:#\[\.,]*$/,R=Array.prototype.slice,S=f.expr.match.POS,T={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(V(this,a,!1),"not",a)},filter:function(a){return this.pushStack(V(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d<e;d++)i=a[d],j[i]||(j[i]=S.test(i)?f(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=S.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(l?l.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(U(c[0])||U(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=R.call(arguments);N.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!T[a]?f.unique(e):e,(this.length>1||P.test(d))&&O.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|object|embed|option|style)/i,bb=/checked\s*(?:[^=]|=\s*.checked.)/i,bc=/\/(java|ecma)script/i,bd=/^\s*<!(?:\[CDATA\[|\-\-)/,be={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};be.optgroup=be.option,be.tbody=be.tfoot=be.colgroup=be.caption=be.thead,be.th=be.td,f.support.htmlSerialize||(be._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!be[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bb.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bf(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bl)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i;b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof a[0]=="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!ba.test(a[0])&&(f.support.checkClone||!bb.test(a[0]))&&(g=!0,h=f.fragments[a[0]],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean
+(a,i,e,d)),g&&(f.fragments[a[0]]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bh(a,d),e=bi(a),g=bi(d);for(h=0;e[h];++h)g[h]&&bh(e[h],g[h])}if(b){bg(a,d);if(c){e=bi(a),g=bi(d);for(h=0;e[h];++h)bg(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=be[l]||be._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bk(k[i]);else bk(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||bc.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bm=/alpha\([^)]*\)/i,bn=/opacity=([^)]*)/,bo=/([A-Z]|^ms)/g,bp=/^-?\d+(?:px)?$/i,bq=/^-?\d/,br=/^([\-+])=([\-+.\de]+)/,bs={position:"absolute",visibility:"hidden",display:"block"},bt=["Left","Right"],bu=["Top","Bottom"],bv,bw,bx;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bv(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=br.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bv)return bv(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return by(a,b,d);f.swap(a,bs,function(){e=by(a,b,d)});return e}},set:function(a,b){if(!bp.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bn.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bm,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bm.test(g)?g.replace(bm,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bv(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bw=function(a,c){var d,e,g;c=c.replace(bo,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bx=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bp.test(d)&&bq.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bv=bw||bx,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bz=/%20/g,bA=/\[\]$/,bB=/\r?\n/g,bC=/#.*$/,bD=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bE=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bF=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bG=/^(?:GET|HEAD)$/,bH=/^\/\//,bI=/\?/,bJ=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bK=/^(?:select|textarea)/i,bL=/\s+/,bM=/([?&])_=[^&]*/,bN=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bO=f.fn.load,bP={},bQ={},bR,bS,bT=["*/"]+["*"];try{bR=e.href}catch(bU){bR=c.createElement("a"),bR.href="",bR=bR.href}bS=bN.exec(bR.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bO)return bO.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bJ,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bK.test(this.nodeName)||bE.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bB,"\r\n")}}):{name:b.name,value:c.replace(bB,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?bX(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),bX(a,b);return a},ajaxSettings:{url:bR,isLocal:bF.test(bS[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bT},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bV(bP),ajaxTransport:bV(bQ),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?bZ(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=b$(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bD.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bC,"").replace(bH,bS[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bL),d.crossDomain==null&&(r=bN.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bS[1]&&r[2]==bS[2]&&(r[3]||(r[1]==="http:"?80:443))==(bS[3]||(bS[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bW(bP,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bG.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bI.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bM,"$1_="+x);d.url=y+(y===d.url?(bI.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bT+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bW(bQ,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){s<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bz,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cq("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cr(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cq("hide",3),a,b,c);for(var d=0,e=this.length;d<e;d++)if(this[d].style){var g=f.css(this[d],"display");g!=="none"&&!f._data(this[d],"olddisplay")&&f._data(this[d],"olddisplay",g)}for(d=0;d<e;d++)this[d].style&&(this[d].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cq("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return this[e.queue===!1?"each":"queue"](function(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(f.support.inlineBlockNeedsLayout?(j=cr(this.nodeName),j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)):this.style.display="inline-block"))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)k=new f.fx(this,b,i),h=a[i],cj.test(h)?k[h==="toggle"?d?"show":"hide":h]():(l=ck.exec(h),m=k.cur(),l?(n=parseFloat(l[2]),o=l[3]||(f.cssNumber[i]?"":"px"),o!=="px"&&(f.style(this,i,(n||1)+o),m=(n||1)/k.cur()*m,f.style(this,i,m+o)),l[1]&&(n=(l[1]==="-="?-1:1)*n+m),k.custom(m,n,o)):k.custom(m,h,""));return!0})},stop:function(a,b){a&&this.queue([]),this.each(function(){var a=f.timers,c=a.length;b||f._unmark(!0,this);while(c--)a[c].elem===this&&(b&&a[c](!0),a.splice(c,1))}),b||this.dequeue();return this}}),f.each({slideDown:cq("show",1),slideUp:cq("hide",1),slideToggle:cq("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default,d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue!==!1?f.dequeue(this):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return d.step(a)}var d=this,e=f.fx;this.startTime=cn||co(),this.start=a,this.end=b,this.unit=c||this.unit||(f.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&f.timers.push(g)&&!cl&&(cl=setInterval(e.tick,e.interval))},show:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=cn||co(),c=!0,d=this.elem,e=this.options,g,h;if(a||b>=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b<a.length;++b)a[b]()||a.splice(b--,1);a.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cl),cl=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cs=/^t(?:able|d|h)$/i,ct=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cu(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);f.offset.initialize();var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.offset.doesNotAddBorder&&(!f.offset.doesAddBorderForTableAndCells||!cs.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={initialize:function(){var a=c.body,b=c.createElement("div"),d,e,g,h,i=parseFloat(f.css(a,"marginTop"))||0,j="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=ct.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!ct.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cu(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cu(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNaN(j)?i:j}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window);
Index: trunk/library/jquery/jquery-ui-1.8.16.custom.min.js
===================================================================
--- trunk/library/jquery/jquery-ui-1.8.16.custom.min.js	(revision 5146)
+++ trunk/library/jquery/jquery-ui-1.8.16.custom.min.js	(revision 5146)
@@ -0,0 +1,791 @@
+/*!
+ * jQuery UI 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.16",
+keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({propAttr:c.fn.prop||c.fn.attr,_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=
+this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,
+"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":
+"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,
+outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,
+"tabindex"),d=isNaN(b);return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&
+a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&
+c.ui.isOverAxis(b,e,i)}})}})(jQuery);
+;/*!
+ * jQuery UI Widget 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)try{b(d).triggerHandler("remove")}catch(e){}k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){try{b(this).triggerHandler("remove")}catch(d){}});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=
+function(h){return!!b.data(h,a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):
+d;if(e&&d.charAt(0)==="_")return h;e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=
+b.extend(true,{},this.options,this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+
+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",
+c);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
+;/*!
+ * jQuery UI Mouse 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ *	jquery.ui.widget.js
+ */
+(function(b){var d=false;b(document).mouseup(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
+this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"&&a.target.nodeName?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=
+this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
+!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
+false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
+;/*
+ * jQuery UI Position 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY,
+left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+=
+k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-=
+m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left=
+d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+=
+a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b),
+g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery);
+;/*
+ * jQuery UI Draggable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
+"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
+this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;if(b.iframeFix)d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;
+this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});
+this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true},
+_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=
+false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,
+10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||
+!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&
+a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=
+this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),
+10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),
+10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,
+(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!=
+"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),
+10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+
+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&
+!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.left<g[0])e=g[0]+this.offset.click.left;
+if(a.pageY-this.offset.click.top<g[1])h=g[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>g[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.top<g[1]||h-this.offset.click.top>g[3])?h:!(h-this.offset.click.top<g[1])?h-b.grid[1]:h+b.grid[1]:h;e=b.grid[0]?this.originalPageX+Math.round((e-this.originalPageX)/
+b.grid[0])*b.grid[0]:this.originalPageX;e=g?!(e-this.offset.click.left<g[0]||e-this.offset.click.left>g[2])?e:!(e-this.offset.click.left<g[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<
+526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,
+c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.16"});d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var h=d.data(this,"sortable");if(h&&!h.options.disabled){c.sortables.push({instance:h,shouldRevert:h.options.revert});
+h.refreshPositions();h._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=
+false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",true);
+this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;
+c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&
+this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=
+a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!=
+"x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<
+c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-
+c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable","snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,
+width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,h=b.offset.left,g=h+c.helperProportions.width,n=b.offset.top,o=n+c.helperProportions.height,i=c.snapElements.length-1;i>=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e<h&&h<l+e&&k-e<n&&n<m+e||j-e<h&&h<l+e&&k-e<o&&o<m+e||j-e<g&&g<l+e&&k-e<n&&n<m+e||j-e<g&&g<l+e&&k-e<o&&
+o<m+e){if(f.snapMode!="inner"){var p=Math.abs(k-o)<=e,q=Math.abs(m-n)<=e,r=Math.abs(j-g)<=e,s=Math.abs(l-h)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l}).left-c.margins.left}var t=
+p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(k-n)<=e;q=Math.abs(m-o)<=e;r=Math.abs(j-h)<=e;s=Math.abs(l-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m-c.helperProportions.height,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[i].snapping&&
+(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=p||q||r||s||t}else{c.snapElements[i].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),
+10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery);
+;/*
+ * jQuery UI Droppable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.draggable.js
+ */
+(function(d){d.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"},_create:function(){var a=this.options,b=a.accept;this.isover=0;this.isout=1;this.accept=d.isFunction(b)?b:function(c){return c.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};d.ui.ddmanager.droppables[a.scope]=d.ui.ddmanager.droppables[a.scope]||[];d.ui.ddmanager.droppables[a.scope].push(this);
+a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var a=d.ui.ddmanager.droppables[this.options.scope],b=0;b<a.length;b++)a[b]==this&&a.splice(b,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(a,b){if(a=="accept")this.accept=d.isFunction(b)?b:function(c){return c.is(b)};d.Widget.prototype._setOption.apply(this,arguments)},_activate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&
+this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass);
+this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g=
+d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop",
+a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.16"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height;
+switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>=
+i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!=
+"none";if(c[f].visible){e=="mousedown"&&c[f]._activate.call(c[f],b);c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight}}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem||
+a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},dragStart:function(a,b){a.element.parents(":not(body,html)").bind("scroll.droppable",function(){a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)})},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);
+if(c=!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e=d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})},dragStop:function(a,b){a.element.parents(":not(body,html)").unbind("scroll.droppable");
+a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)}}})(jQuery);
+;/*
+ * jQuery UI Resizable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(e){e.widget("ui.resizable",e.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1E3},_create:function(){var b=this,a=this.options;this.element.addClass("ui-resizable");e.extend(this,{_aspectRatio:!!a.aspectRatio,aspectRatio:a.aspectRatio,originalElement:this.element,
+_proportionallyResizeElements:[],_helper:a.helper||a.ghost||a.animate?a.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){/relative/.test(this.element.css("position"))&&e.browser.opera&&this.element.css({position:"relative",top:"auto",left:"auto"});this.element.wrap(e('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),
+top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=
+this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",
+nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d<c.length;d++){var f=e.trim(c[d]),g=e('<div class="ui-resizable-handle '+("ui-resizable-"+f)+'"></div>');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor==
+String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection();
+this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){if(!a.disabled){e(this).removeClass("ui-resizable-autohide");b._handles.show()}},function(){if(!a.disabled)if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();
+var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=
+false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"});
+this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff=
+{width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis];
+if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},
+_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,
+{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateVirtualBoundaries:function(b){var a=this.options,c,d,f;a={minWidth:k(a.minWidth)?a.minWidth:0,maxWidth:k(a.maxWidth)?a.maxWidth:Infinity,minHeight:k(a.minHeight)?a.minHeight:0,maxHeight:k(a.maxHeight)?a.maxHeight:
+Infinity};if(this._aspectRatio||b){b=a.minHeight*this.aspectRatio;d=a.minWidth/this.aspectRatio;c=a.maxHeight*this.aspectRatio;f=a.maxWidth/this.aspectRatio;if(b>a.minWidth)a.minWidth=b;if(d>a.minHeight)a.minHeight=d;if(c<a.maxWidth)a.maxWidth=c;if(f<a.maxHeight)a.maxHeight=f}this._vBoundaries=a},_updateCache:function(b){this.offset=this.helper.offset();if(k(b.left))this.position.left=b.left;if(k(b.top))this.position.top=b.top;if(k(b.height))this.size.height=b.height;if(k(b.width))this.size.width=
+b.width},_updateRatio:function(b){var a=this.position,c=this.size,d=this.axis;if(k(b.height))b.width=b.height*this.aspectRatio;else if(k(b.width))b.height=b.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(d=="nw"){b.top=a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this._vBoundaries,c=this.axis,d=k(b.width)&&a.maxWidth&&a.maxWidth<b.width,f=k(b.height)&&a.maxHeight&&a.maxHeight<b.height,g=k(b.width)&&a.minWidth&&
+a.minWidth>b.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height,l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&l)b.left=i-a.minWidth;if(d&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=
+null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a<this._proportionallyResizeElements.length;a++){var c=this._proportionallyResizeElements[a];if(!this.borderDif){var d=[c.css("borderTopWidth"),c.css("borderRightWidth"),c.css("borderBottomWidth"),c.css("borderLeftWidth")],f=[c.css("paddingTop"),c.css("paddingRight"),c.css("paddingBottom"),c.css("paddingLeft")];this.borderDif=e.map(d,function(g,h){g=parseInt(g,10)||
+0;h=parseInt(f[h],10)||0;return g+h})}e.browser.msie&&(e(b).is(":hidden")||e(b).parents(":hidden").length)||c.css({height:b.height()-this.borderDif[0]-this.borderDif[2]||0,width:b.width()-this.borderDif[1]-this.borderDif[3]||0})}},_renderProxy:function(){var b=this.options;this.elementOffset=this.element.offset();if(this._helper){this.helper=this.helper||e('<div style="overflow:hidden;"></div>');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+
+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+
+c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);
+b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.16"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),
+10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-
+f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var l=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:l.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(l.css("position"))){c._revertToRelativePosition=true;l.css({position:"absolute",top:"auto",left:"auto"})}l.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?
+e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=
+e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,
+step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=
+e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;
+var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:
+a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-
+d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,
+f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,
+display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=
+e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=
+d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery);
+;/*
+ * jQuery UI Selectable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"),
+selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("<div class='ui-selectable-helper'></div>")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX,
+c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting",
+c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d=
+this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.right<b||a.top>i||a.bottom<g);else if(d.tolerance=="fit")k=a.left>b&&a.right<h&&a.top>g&&a.bottom<i;if(k){if(a.selected){a.$element.removeClass("ui-selected");a.selected=false}if(a.unselecting){a.$element.removeClass("ui-unselecting");
+a.unselecting=false}if(!a.selecting){a.$element.addClass("ui-selecting");a.selecting=true;f._trigger("selecting",c,{selecting:a.element})}}else{if(a.selecting)if(c.metaKey&&a.startselected){a.$element.removeClass("ui-selecting");a.selecting=false;a.$element.addClass("ui-selected");a.selected=true}else{a.$element.removeClass("ui-selecting");a.selecting=false;if(a.startselected){a.$element.addClass("ui-unselecting");a.unselecting=true}f._trigger("unselecting",c,{unselecting:a.element})}if(a.selected)if(!c.metaKey&&
+!a.startselected){a.$element.removeClass("ui-selected");a.selected=false;a.$element.addClass("ui-unselecting");a.unselecting=true;f._trigger("unselecting",c,{unselecting:a.element})}}}});return false}},_mouseStop:function(c){var f=this;this.dragged=false;e(".ui-unselecting",this.element[0]).each(function(){var d=e.data(this,"selectable-item");d.$element.removeClass("ui-unselecting");d.unselecting=false;d.startselected=false;f._trigger("unselected",c,{unselected:d.element})});e(".ui-selecting",this.element[0]).each(function(){var d=
+e.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected");d.selecting=false;d.selected=true;d.startselected=true;f._trigger("selected",c,{selected:d.element})});this._trigger("stop",c);this.helper.remove();return false}});e.extend(e.ui.selectable,{version:"1.8.16"})})(jQuery);
+;/*
+ * jQuery UI Sortable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable");
+this.refresh();this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a===
+"disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&
+!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,
+left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};
+this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=
+document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);
+return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop+b.scrollSpeed;else if(a.pageY-this.overflowOffset.top<
+b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop-b.scrollSpeed;if(this.overflowOffset.left+this.scrollParent[0].offsetWidth-a.pageX<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft+b.scrollSpeed;else if(a.pageX-this.overflowOffset.left<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft-b.scrollSpeed}else{if(a.pageY-d(document).scrollTop()<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()-
+b.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()+b.scrollSpeed);if(a.pageX-d(document).scrollLeft()<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()-b.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()+b.scrollSpeed)}c!==false&&d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,
+a)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(b=this.items.length-1;b>=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],
+e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();
+c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):
+this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,
+dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},
+toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+j<k&&b+l>g&&b+l<h;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||
+this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?j:g<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<h&&i<e+this.helperProportions.height/2&&f-this.helperProportions.height/2<k},_intersectsWithPointer:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left,a.width);b=b&&a;a=this._getDragVerticalDirection();
+var c=this._getDragHorizontalDirection();if(!b)return false;return this.floating?c&&c=="right"||a=="down"?2:1:a&&(a=="down"?2:1)},_intersectsWithSides:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top+a.height/2,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left+a.width/2,a.width);var c=this._getDragVerticalDirection(),e=this._getDragHorizontalDirection();return this.floating&&e?e=="right"&&a||e=="left"&&!a:c&&(c=="down"&&b||c=="up"&&!b)},
+_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();
+if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),
+this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(a){this.items=[];this.containers=[this];var b=this.items,c=[[d.isFunction(this.options.items)?this.options.items.call(this.element[0],a,{item:this.currentItem}):d(this.options.items,this.element),
+this]],e=this._connectWith();if(e)for(var f=e.length-1;f>=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h<g;h++){i=d(e[h]);i.data("sortable-item",a);b.push({item:i,instance:a,width:0,height:0,left:0,top:0})}}},refreshPositions:function(a){if(this.offsetParent&&
+this.helper)this.offset.parent=this._getParentOffset();for(var b=this.items.length-1;b>=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=
+this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=
+d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||
+0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",
+a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-
+f)<b){b=Math.abs(h-f);e=this.items[g]}}if(e||this.options.dropOnEmpty){this.currentContainer=this.containers[c];e?this._rearrange(a,e,null,true):this._rearrange(a,null,this.containers[c].element,true);this._trigger("change",a,this._uiHash());this.containers[c]._trigger("change",a,this._uiHash(this));this.options.placeholder.update(this.currentContainer,this.placeholder);this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}}},_createHelper:function(a){var b=
+this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a,this.currentItem])):b.helper=="clone"?this.currentItem.clone():this.currentItem;a.parents("body").length||d(b.appendTo!="parent"?b.appendTo:this.currentItem[0].parentNode)[0].appendChild(a[0]);if(a[0]==this.currentItem[0])this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")};if(a[0].style.width==
+""||b.forceHelperSize)a.width(this.currentItem.width());if(a[0].style.height==""||b.forceHelperSize)a.height(this.currentItem.height());return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=
+this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a=
+{top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),
+10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?
+document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)){var b=d(a.containment)[0];a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),
+10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(a,b){if(!b)b=
+this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&
+this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0]))this.offset.relative=this._getRelativeOffset();
+var f=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])f=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-
+this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;f=this.originalPageX+Math.round((f-this.originalPageX)/b.grid[0])*b.grid[0];f=this.containment?!(f-this.offset.click.left<this.containment[0]||f-this.offset.click.left>this.containment[2])?f:!(f-this.offset.click.left<this.containment[0])?f-b.grid[0]:f+b.grid[0]:f}}return{top:g-
+this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())}},_rearrange:function(a,b,c,e){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],
+this.direction=="down"?b.item[0]:b.item[0].nextSibling);this.counter=this.counter?++this.counter:1;var f=this,g=this.counter;window.setTimeout(function(){g==f.counter&&f.refreshPositions(!e)},0)},_clear:function(a,b){this.reverting=false;var c=[];!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem);this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var e in this._storedCSS)if(this._storedCSS[e]=="auto"||this._storedCSS[e]=="static")this._storedCSS[e]=
+"";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!b&&c.push(function(f){this._trigger("receive",f,this._uiHash(this.fromOutside))});if((this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!b)c.push(function(f){this._trigger("update",f,this._uiHash())});if(!d.ui.contains(this.element[0],this.currentItem[0])){b||c.push(function(f){this._trigger("remove",
+f,this._uiHash())});for(e=this.containers.length-1;e>=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,
+this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",
+a,this._uiHash());for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}return false}b||this._trigger("beforeStop",a,this._uiHash());this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.helper[0]!=this.currentItem[0]&&this.helper.remove();this.helper=null;if(!b){for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}this.fromOutside=false;return true},_trigger:function(){d.Widget.prototype._trigger.apply(this,arguments)===false&&this.cancel()},
+_uiHash:function(a){var b=a||this;return{helper:b.helper,placeholder:b.placeholder||d([]),position:b.position,originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,sender:a?a.element:null}}});d.extend(d.ui.sortable,{version:"1.8.16"})})(jQuery);
+;/*
+ * jQuery UI Accordion 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ */
+(function(c){c.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix");
+a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
+if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion",
+function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a=
+this.options;if(a.icons){c("<span></span>").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex");
+this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons();
+b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target);
+a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+
+c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options;
+if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);
+if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(),
+e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight||
+e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false",
+"aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.16",
+animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/);
+f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide",
+paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery);
+;/*
+ * jQuery UI Autocomplete 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ *	jquery.ui.position.js
+ */
+(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.propAttr("readOnly"))){g=
+false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!=
+a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)};
+this.menu=d("<ul></ul>").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&&
+a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");
+d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&&
+b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source=
+this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==false)return this._search(a)},_search:function(a){this.pending++;this.element.addClass("ui-autocomplete-loading");this.source({term:a},this.response)},_response:function(a){if(!this.options.disabled&&a&&a.length){a=this._normalize(a);this._suggest(a);this._trigger("open")}else this.close();
+this.pending--;this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing);if(this.menu.element.is(":visible")){this.menu.element.hide();this.menu.deactivate();this._trigger("close",a)}},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(a){if(a.length&&a[0].label&&a[0].value)return a;return d.map(a,function(b){if(typeof b==="string")return{label:b,value:b};return d.extend({label:b.label||
+b.value,value:b.value||b.label},b)})},_suggest:function(a){var b=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(b,a);this.menu.deactivate();this.menu.refresh();b.show();this._resizeMenu();b.position(d.extend({of:this.element},this.options.position));this.options.autoFocus&&this.menu.next(new d.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth(),this.element.outerWidth()))},_renderMenu:function(a,b){var g=this;
+d.each(b,function(c,f){g._renderItem(a,f)})},_renderItem:function(a,b){return d("<li></li>").data("item.autocomplete",b).append(d("<a></a>").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,
+"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery);
+(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex",
+-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.scrollTop(),c=this.element.height();if(b<0)this.element.scrollTop(g+b);else b>=c&&this.element.scrollTop(g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");
+this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b,
+this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
+this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
+this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[d.fn.prop?"prop":"attr"]("scrollHeight")},select:function(e){this._trigger("selected",e,{item:this.active})}})})(jQuery);
+;/*
+ * jQuery UI Button 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ */
+(function(b){var h,i,j,g,l=function(){var a=b(this).find(":ui-button");setTimeout(function(){a.button("refresh")},1)},k=function(a){var c=a.name,e=a.form,f=b([]);if(c)f=e?b(e).find("[name='"+c+"']"):b("[name='"+c+"']",a.ownerDocument).filter(function(){return!this.form});return f};b.widget("ui.button",{options:{disabled:null,text:true,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",l);if(typeof this.options.disabled!==
+"boolean")this.options.disabled=this.element.propAttr("disabled");this._determineButtonType();this.hasTitle=!!this.buttonElement.attr("title");var a=this,c=this.options,e=this.type==="checkbox"||this.type==="radio",f="ui-state-hover"+(!e?" ui-state-active":"");if(c.label===null)c.label=this.buttonElement.html();if(this.element.is(":disabled"))c.disabled=true;this.buttonElement.addClass("ui-button ui-widget ui-state-default ui-corner-all").attr("role","button").bind("mouseenter.button",function(){if(!c.disabled){b(this).addClass("ui-state-hover");
+this===h&&b(this).addClass("ui-state-active")}}).bind("mouseleave.button",function(){c.disabled||b(this).removeClass(f)}).bind("click.button",function(d){if(c.disabled){d.preventDefault();d.stopImmediatePropagation()}});this.element.bind("focus.button",function(){a.buttonElement.addClass("ui-state-focus")}).bind("blur.button",function(){a.buttonElement.removeClass("ui-state-focus")});if(e){this.element.bind("change.button",function(){g||a.refresh()});this.buttonElement.bind("mousedown.button",function(d){if(!c.disabled){g=
+false;i=d.pageX;j=d.pageY}}).bind("mouseup.button",function(d){if(!c.disabled)if(i!==d.pageX||j!==d.pageY)g=true})}if(this.type==="checkbox")this.buttonElement.bind("click.button",function(){if(c.disabled||g)return false;b(this).toggleClass("ui-state-active");a.buttonElement.attr("aria-pressed",a.element[0].checked)});else if(this.type==="radio")this.buttonElement.bind("click.button",function(){if(c.disabled||g)return false;b(this).addClass("ui-state-active");a.buttonElement.attr("aria-pressed","true");
+var d=a.element[0];k(d).not(d).map(function(){return b(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")});else{this.buttonElement.bind("mousedown.button",function(){if(c.disabled)return false;b(this).addClass("ui-state-active");h=this;b(document).one("mouseup",function(){h=null})}).bind("mouseup.button",function(){if(c.disabled)return false;b(this).removeClass("ui-state-active")}).bind("keydown.button",function(d){if(c.disabled)return false;if(d.keyCode==b.ui.keyCode.SPACE||
+d.keyCode==b.ui.keyCode.ENTER)b(this).addClass("ui-state-active")}).bind("keyup.button",function(){b(this).removeClass("ui-state-active")});this.buttonElement.is("a")&&this.buttonElement.keyup(function(d){d.keyCode===b.ui.keyCode.SPACE&&b(this).click()})}this._setOption("disabled",c.disabled);this._resetButton()},_determineButtonType:function(){this.type=this.element.is(":checkbox")?"checkbox":this.element.is(":radio")?"radio":this.element.is("input")?"input":"button";if(this.type==="checkbox"||this.type===
+"radio"){var a=this.element.parents().filter(":last"),c="label[for='"+this.element.attr("id")+"']";this.buttonElement=a.find(c);if(!this.buttonElement.length){a=a.length?a.siblings():this.element.siblings();this.buttonElement=a.filter(c);if(!this.buttonElement.length)this.buttonElement=a.find(c)}this.element.addClass("ui-helper-hidden-accessible");(a=this.element.is(":checked"))&&this.buttonElement.addClass("ui-state-active");this.buttonElement.attr("aria-pressed",a)}else this.buttonElement=this.element},
+widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible");this.buttonElement.removeClass("ui-button ui-widget ui-state-default ui-corner-all ui-state-hover ui-state-active  ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only").removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html());this.hasTitle||this.buttonElement.removeAttr("title");
+b.Widget.prototype.destroy.call(this)},_setOption:function(a,c){b.Widget.prototype._setOption.apply(this,arguments);if(a==="disabled")c?this.element.propAttr("disabled",true):this.element.propAttr("disabled",false);else this._resetButton()},refresh:function(){var a=this.element.is(":disabled");a!==this.options.disabled&&this._setOption("disabled",a);if(this.type==="radio")k(this.element[0]).each(function(){b(this).is(":checked")?b(this).button("widget").addClass("ui-state-active").attr("aria-pressed",
+"true"):b(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")});else if(this.type==="checkbox")this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false")},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var a=this.buttonElement.removeClass("ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only"),
+c=b("<span></span>").addClass("ui-button-text").html(this.options.label).appendTo(a.empty()).text(),e=this.options.icons,f=e.primary&&e.secondary,d=[];if(e.primary||e.secondary){if(this.options.text)d.push("ui-button-text-icon"+(f?"s":e.primary?"-primary":"-secondary"));e.primary&&a.prepend("<span class='ui-button-icon-primary ui-icon "+e.primary+"'></span>");e.secondary&&a.append("<span class='ui-button-icon-secondary ui-icon "+e.secondary+"'></span>");if(!this.options.text){d.push(f?"ui-button-icons-only":
+"ui-button-icon-only");this.hasTitle||a.attr("title",c)}}else d.push("ui-button-text-only");a.addClass(d.join(" "))}}});b.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(a,c){a==="disabled"&&this.buttons.button("option",a,c);b.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var a=this.element.css("direction")===
+"ltr";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(a?"ui-corner-left":"ui-corner-right").end().filter(":last").addClass(a?"ui-corner-right":"ui-corner-left").end().end()},destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");
+b.Widget.prototype.destroy.call(this)}})})(jQuery);
+;/*
+ * jQuery UI Dialog 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ *  jquery.ui.button.js
+ *	jquery.ui.draggable.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.position.js
+ *	jquery.ui.resizable.js
+ */
+(function(c,l){var m={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},n={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true},o=c.attrFn||{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true,click:true};c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,
+position:{my:"center",at:"center",collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||"&#160;",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("<div></div>")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+
+b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&!i.isDefaultPrevented()&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),
+h=c('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("<span></span>")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("<span></span>").addClass("ui-dialog-title").attr("id",
+e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");
+a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d,e;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!==
+b.uiDialog[0]){e=c(this).css("z-index");isNaN(e)||(d=Math.max(d,e))}});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()};c.ui.dialog.maxZ+=1;
+d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target===
+f[0]&&e.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false,e=c("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,
+function(){return!(d=true)});if(d){c.each(a,function(f,h){h=c.isFunction(h)?{click:h,text:f}:h;var i=c('<button type="button"></button>').click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.each(h,function(j,k){if(j!=="click")j in o?i[j](k):i.attr(j,k)});c.fn.button&&i.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",
+handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition,
+originalSize:f.originalSize,position:f.position,size:f.size}}a=a===l?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize",
+f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop",f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0],e;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):
+[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,f){if(+b[g]===b[g]){d[g]=b[g];b[g]=f}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(e=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(c.extend({of:window},a));e||this.uiDialog.hide()},_setOptions:function(a){var b=this,d={},e=false;c.each(a,function(g,f){b._setOption(g,f);
+if(g in m)e=true;if(g in n)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"):
+e.removeClass("ui-dialog-disabled");break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||"&#160;"));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a=
+this.options,b,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height-
+b,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.16",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),
+create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()<c.ui.dialog.overlay.maxZ)return false})},1);c(document).bind("keydown.dialog-overlay",function(d){if(a.options.closeOnEscape&&!d.isDefaultPrevented()&&d.keyCode&&d.keyCode===c.ui.keyCode.ESCAPE){a.close(d);d.preventDefault()}});c(window).bind("resize.dialog-overlay",c.ui.dialog.overlay.resize)}var b=(this.oldInstances.pop()||
+c("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&&
+c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a<b?c(window).height()+"px":a+"px"}else return c(document).height()+"px"},width:function(){var a,b;if(c.browser.msie){a=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth);b=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return a<b?c(window).width()+"px":a+"px"}else return c(document).width()+
+"px"},resize:function(){var a=c([]);c.each(c.ui.dialog.overlay.instances,function(){a=a.add(this)});a.css({width:0,height:0}).css({width:c.ui.dialog.overlay.width(),height:c.ui.dialog.overlay.height()})}});c.extend(c.ui.dialog.overlay.prototype,{destroy:function(){c.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);
+;/*
+ * jQuery UI Slider 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.slider",d.ui.mouse,{widgetEventPrefix:"slide",options:{animate:false,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null},_create:function(){var a=this,b=this.options,c=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f=b.values&&b.values.length||1,e=[];this._mouseSliding=this._keySliding=false;this._animateOff=true;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+
+this.orientation+" ui-widget ui-widget-content ui-corner-all"+(b.disabled?" ui-slider-disabled ui-disabled":""));this.range=d([]);if(b.range){if(b.range===true){if(!b.values)b.values=[this._valueMin(),this._valueMin()];if(b.values.length&&b.values.length!==2)b.values=[b.values[0],b.values[0]]}this.range=d("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(b.range==="min"||b.range==="max"?" ui-slider-range-"+b.range:""))}for(var j=c.length;j<f;j+=1)e.push("<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>");
+this.handles=c.add(d(e.join("")).appendTo(a.element));this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(g){g.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur();else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(g){d(this).data("index.ui-slider-handle",
+g)});this.handles.keydown(function(g){var k=true,l=d(this).data("index.ui-slider-handle"),i,h,m;if(!a.options.disabled){switch(g.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:k=false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");i=a._start(g,l);if(i===false)return}break}m=a.options.step;i=a.options.values&&a.options.values.length?
+(h=a.values(l)):(h=a.value());switch(g.keyCode){case d.ui.keyCode.HOME:h=a._valueMin();break;case d.ui.keyCode.END:h=a._valueMax();break;case d.ui.keyCode.PAGE_UP:h=a._trimAlignValue(i+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=a._trimAlignValue(i-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(i===a._valueMax())return;h=a._trimAlignValue(i+m);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(i===a._valueMin())return;h=a._trimAlignValue(i-
+m);break}a._slide(g,l,h);return k}}).keyup(function(g){var k=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(g,k);a._change(g,k);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy();
+return this},_mouseCapture:function(a){var b=this.options,c,f,e,j,g;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:a.pageX,y:a.pageY});f=this._valueMax()-this._valueMin()+1;j=this;this.handles.each(function(k){var l=Math.abs(c-j.values(k));if(f>l){f=l;e=d(this);g=k}});if(b.range===true&&this.values(1)===b.min){g+=1;e=d(this.handles[g])}if(this._start(a,g)===false)return false;
+this._mouseSliding=true;j._handleIndex=g;e.addClass("ui-state-active").focus();b=e.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-e.width()/2,top:a.pageY-b.top-e.height()/2-(parseInt(e.css("borderTopWidth"),10)||0)-(parseInt(e.css("borderBottomWidth"),10)||0)+(parseInt(e.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(a,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(a){var b=
+this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b;if(this.orientation==="horizontal"){b=
+this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);
+c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var f;if(this.options.values&&this.options.values.length){f=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>f||b===1&&c<f))c=f;if(c!==this.values(b)){f=this.values();f[b]=c;a=this._trigger("slide",a,{handle:this.handles[b],value:c,values:f});this.values(b?0:1);a!==false&&this.values(b,c,true)}}else if(c!==this.value()){a=this._trigger("slide",a,{handle:this.handles[b],value:c});
+a!==false&&this.value(c)}},_stop:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);c.values=this.values()}this._trigger("stop",a,c)},_change:function(a,b){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);c.values=this.values()}this._trigger("change",a,c)}},value:function(a){if(arguments.length){this.options.value=
+this._trimAlignValue(a);this._refreshValue();this._change(null,0)}else return this._value()},values:function(a,b){var c,f,e;if(arguments.length>1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}else if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;f=arguments[0];for(e=0;e<c.length;e+=1){c[e]=this._trimAlignValue(f[e]);this._change(null,e)}this._refreshValue()}else return this.options.values&&this.options.values.length?this._values(a):
+this.value();else return this._values()},_setOption:function(a,b){var c,f=0;if(d.isArray(this.options.values))f=this.options.values.length;d.Widget.prototype._setOption.apply(this,arguments);switch(a){case "disabled":if(b){this.handles.filter(".ui-state-focus").blur();this.handles.removeClass("ui-state-hover");this.handles.propAttr("disabled",true);this.element.addClass("ui-disabled")}else{this.handles.propAttr("disabled",false);this.element.removeClass("ui-disabled")}break;case "orientation":this._detectOrientation();
+this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue();break;case "value":this._animateOff=true;this._refreshValue();this._change(null,0);this._animateOff=false;break;case "values":this._animateOff=true;this._refreshValue();for(c=0;c<f;c+=1)this._change(null,c);this._animateOff=false;break}},_value:function(){var a=this.options.value;return a=this._trimAlignValue(a)},_values:function(a){var b,c;if(arguments.length){b=this.options.values[a];
+return b=this._trimAlignValue(b)}else{b=this.options.values.slice();for(c=0;c<b.length;c+=1)b[c]=this._trimAlignValue(b[c]);return b}},_trimAlignValue:function(a){if(a<=this._valueMin())return this._valueMin();if(a>=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a=
+this.options.range,b=this.options,c=this,f=!this._animateOff?b.animate:false,e,j={},g,k,l,i;if(this.options.values&&this.options.values.length)this.handles.each(function(h){e=(c.values(h)-c._valueMin())/(c._valueMax()-c._valueMin())*100;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";d(this).stop(1,1)[f?"animate":"css"](j,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(h===0)c.range.stop(1,1)[f?"animate":"css"]({left:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({width:e-
+g+"%"},{queue:false,duration:b.animate})}else{if(h===0)c.range.stop(1,1)[f?"animate":"css"]({bottom:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({height:e-g+"%"},{queue:false,duration:b.animate})}g=e});else{k=this.value();l=this._valueMin();i=this._valueMax();e=i!==l?(k-l)/(i-l)*100:0;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[f?"animate":"css"](j,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[f?"animate":"css"]({width:e+"%"},
+b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[f?"animate":"css"]({width:100-e+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[f?"animate":"css"]({height:e+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[f?"animate":"css"]({height:100-e+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.16"})})(jQuery);
+;/*
+ * jQuery UI Tabs 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ */
+(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&&
+e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=
+d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]||
+(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all");
+this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected=
+this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active");
+if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));
+this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+
+g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",
+function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")};
+this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected=
+-1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";
+d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=
+d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b,
+e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]);
+j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove();
+if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1<this.anchors.length?1:-1));e.disabled=d.map(d.grep(e.disabled,function(h){return h!=b}),function(h){return h>=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null,
+this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this},
+load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c,
+"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},
+url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.16"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k<a.anchors.length?k:0)},b);j&&j.stopPropagation()});e=a._unrotate||(a._unrotate=!e?function(j){j.clientX&&
+a.rotate(null)}:function(){t=c.selected;h()});if(b){this.element.bind("tabsshow",h);this.anchors.bind(c.event+".tabs",e);h()}else{clearTimeout(a.rotation);this.element.unbind("tabsshow",h);this.anchors.unbind(c.event+".tabs",e);delete this._rotate;delete this._unrotate}return this}})})(jQuery);
+;/*
+ * jQuery UI Datepicker 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ */
+(function(d,C){function M(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass=
+"ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su",
+"Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",
+minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false,disabled:false};d.extend(this._defaults,this.regional[""]);this.dpDiv=N(d('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}function N(a){return a.bind("mouseout",
+function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");b.length&&b.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");if(!(d.datepicker._isDisabledDatepicker(J.inline?a.parent()[0]:J.input[0])||!b.length)){b.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
+b.addClass("ui-state-hover");b.hasClass("ui-datepicker-prev")&&b.addClass("ui-datepicker-prev-hover");b.hasClass("ui-datepicker-next")&&b.addClass("ui-datepicker-next-hover")}})}function H(a,b){d.extend(a,b);for(var c in b)if(b[c]==null||b[c]==C)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.16"}});var B=(new Date).getTime(),J;d.extend(M.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},
+setDefaults:function(a){H(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,
+"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:N(d('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}},_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",
+function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b);b.settings.disabled&&this._disableDatepicker(a)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&b.append.remove();if(c){b.append=d('<span class="'+this._appendClass+'">'+c+"</span>");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c==
+"focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("<img/>").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('<button type="button"></button>').addClass(this._triggerClass).html(f==""?c:d("<img/>").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():
+d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;g<f.length;g++)if(f[g].length>h){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,
+b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),true);this._updateDatepicker(b);this._updateAlternate(b);b.settings.disabled&&this._disableDatepicker(a);b.dpDiv.css("display","block")}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=
+1;this._dialogInput=d('<input type="text" id="'+("dp"+this.uuid)+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}H(a.settings,e||{});b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/
+2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=
+d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=
+a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().removeClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a,
+"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().addClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==
+a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return true;return false},_getInst:function(a){try{return d.data(a,"datepicker")}catch(b){throw"Missing instance data for this datepicker";}},_optionDatepicker:function(a,b,c){var e=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?d.extend({},d.datepicker._defaults):e?b=="all"?
+d.extend({},e.settings):this._get(e,b):null;var f=b||{};if(typeof b=="string"){f={};f[b]=c}if(e){this._curInst==e&&this._hideDatepicker();var h=this._getDateDatepicker(a,true),i=this._getMinMaxDate(e,"min"),g=this._getMinMaxDate(e,"max");H(e.settings,f);if(i!==null&&f.dateFormat!==C&&f.minDate===C)e.settings.minDate=this._formatDate(e,i);if(g!==null&&f.dateFormat!==C&&f.maxDate===C)e.settings.maxDate=this._formatDate(e,g);this._attachments(d(a),e);this._autoSize(e);this._setDate(e,h);this._updateAlternate(e);
+this._updateDatepicker(e)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){(a=this._getInst(a))&&this._updateDatepicker(a)},_setDateDatepicker:function(a,b){if(a=this._getInst(a)){this._setDate(a,b);this._updateDatepicker(a);this._updateAlternate(a)}},_getDateDatepicker:function(a,b){(a=this._getInst(a))&&!a.inline&&this._setDateFromField(a,b);return a?this._getDate(a):null},_doKeyDown:function(a){var b=d.datepicker._getInst(a.target),c=true,e=b.dpDiv.is(".ui-datepicker-rtl");
+b._keyEvent=true;if(d.datepicker._datepickerShowing)switch(a.keyCode){case 9:d.datepicker._hideDatepicker();c=false;break;case 13:c=d("td."+d.datepicker._dayOverClass+":not(."+d.datepicker._currentClass+")",b.dpDiv);c[0]&&d.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,c[0]);if(a=d.datepicker._get(b,"onSelect")){c=d.datepicker._formatDate(b);a.apply(b.input?b.input[0]:null,[c,b])}else d.datepicker._hideDatepicker();return false;case 27:d.datepicker._hideDatepicker();break;case 33:d.datepicker._adjustDate(a.target,
+a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 34:d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 35:if(a.ctrlKey||a.metaKey)d.datepicker._clearDate(a.target);c=a.ctrlKey||a.metaKey;break;case 36:if(a.ctrlKey||a.metaKey)d.datepicker._gotoToday(a.target);c=a.ctrlKey||a.metaKey;break;case 37:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?+1:-1,"D");c=
+a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 38:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,-7,"D");c=a.ctrlKey||a.metaKey;break;case 39:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?-1:+1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,
+"stepMonths"),"M");break;case 40:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,+7,"D");c=a.ctrlKey||a.metaKey;break;default:c=false}else if(a.keyCode==36&&a.ctrlKey)d.datepicker._showDatepicker(this);else c=false;if(c){a.preventDefault();a.stopPropagation()}},_doKeyPress:function(a){var b=d.datepicker._getInst(a.target);if(d.datepicker._get(b,"constrainInput")){b=d.datepicker._possibleChars(d.datepicker._get(b,"dateFormat"));var c=String.fromCharCode(a.charCode==C?a.keyCode:a.charCode);
+return a.ctrlKey||a.metaKey||c<" "||!b||b.indexOf(c)>-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",
+a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);if(d.datepicker._curInst&&d.datepicker._curInst!=b){d.datepicker._datepickerShowing&&d.datepicker._triggerOnClose(d.datepicker._curInst);d.datepicker._curInst.dpDiv.stop(true,true)}var c=d.datepicker._get(b,"beforeShow");c=c?c.apply(a,[a,b]):{};if(c!==false){H(b.settings,c);b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value=
+"";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);
+c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.datepicker._datepickerShowing=
+true;d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}}},_updateDatepicker:function(a){this.maxRows=4;var b=d.datepicker._getBorders(a.dpDiv);J=a;a.dpDiv.empty().append(this._generateHTML(a));var c=a.dpDiv.find("iframe.ui-datepicker-cover");c.length&&c.css({left:-b[0],top:-b[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});
+a.dpDiv.find("."+this._dayOverClass+" a").mouseover();b=this._getNumberOfMonths(a);c=b[1];a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");c>1&&a.dpDiv.addClass("ui-datepicker-multi-"+c).css("width",17*c+"em");a.dpDiv[(b[0]!=1||b[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&
+!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var e=a.yearshtml;setTimeout(function(){e===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);e=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),
+h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=
+this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_triggerOnClose:function(a){var b=this._get(a,"onClose");if(b)b.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a])},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);
+this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();d.datepicker._triggerOnClose(b);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},
+_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):
+0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e["selected"+(c=="M"?
+"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);
+this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");
+if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?
+b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=A+1<a.length&&a.charAt(A+1)==p)&&A++;return p},m=function(p){var D=
+o(p);p=new RegExp("^\\d{1,"+(p=="@"?14:p=="!"?20:p=="y"&&D?4:p=="o"?3:2)+"}");p=b.substring(q).match(p);if(!p)throw"Missing number at position "+q;q+=p[0].length;return parseInt(p[0],10)},n=function(p,D,K){p=d.map(o(p)?K:D,function(w,x){return[[x,w]]}).sort(function(w,x){return-(w[1].length-x[1].length)});var E=-1;d.each(p,function(w,x){w=x[1];if(b.substr(q,w.length).toLowerCase()==w.toLowerCase()){E=x[0];q+=w.length;return false}});if(E!=-1)return E+1;else throw"Unknown name at position "+q;},s=
+function(){if(b.charAt(q)!=a.charAt(A))throw"Unexpected literal at position "+q;q++},q=0,A=0;A<a.length;A++)if(k)if(a.charAt(A)=="'"&&!o("'"))k=false;else s();else switch(a.charAt(A)){case "d":l=m("d");break;case "D":n("D",f,h);break;case "o":u=m("o");break;case "m":j=m("m");break;case "M":j=n("M",i,g);break;case "y":c=m("y");break;case "@":var v=new Date(m("@"));c=v.getFullYear();j=v.getMonth()+1;l=v.getDate();break;case "!":v=new Date((m("!")-this._ticksTo1970)/1E4);c=v.getFullYear();j=v.getMonth()+
+1;l=v.getDate();break;case "'":if(o("'"))s();else k=true;break;default:s()}if(q<b.length)throw"Extra/unparsed characters found in date: "+b.substring(q);if(c==-1)c=(new Date).getFullYear();else if(c<100)c+=(new Date).getFullYear()-(new Date).getFullYear()%100+(c<=e?0:-100);if(u>-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,j-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=j||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",
+COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:
+null)||this._defaults.monthNames;var i=function(o){(o=k+1<a.length&&a.charAt(k+1)==o)&&k++;return o},g=function(o,m,n){m=""+m;if(i(o))for(;m.length<n;)m="0"+m;return m},j=function(o,m,n,s){return i(o)?s[m]:n[m]},l="",u=false;if(b)for(var k=0;k<a.length;k++)if(u)if(a.charAt(k)=="'"&&!i("'"))u=false;else l+=a.charAt(k);else switch(a.charAt(k)){case "d":l+=g("d",b.getDate(),2);break;case "D":l+=j("D",b.getDay(),e,f);break;case "o":l+=g("o",Math.round(((new Date(b.getFullYear(),b.getMonth(),b.getDate())).getTime()-
+(new Date(b.getFullYear(),0,0)).getTime())/864E5),3);break;case "m":l+=g("m",b.getMonth()+1,2);break;case "M":l+=j("M",b.getMonth(),h,c);break;case "y":l+=i("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case "@":l+=b.getTime();break;case "!":l+=b.getTime()*1E4+this._ticksTo1970;break;case "'":if(i("'"))l+="'";else u=true;break;default:l+=a.charAt(k)}return l},_possibleChars:function(a){for(var b="",c=false,e=function(h){(h=f+1<a.length&&a.charAt(f+1)==h)&&f++;return h},f=
+0;f<a.length;f++)if(c)if(a.charAt(f)=="'"&&!e("'"))c=false;else b+=a.charAt(f);else switch(a.charAt(f)){case "d":case "m":case "y":case "@":b+="0123456789";break;case "D":case "M":return null;case "'":if(e("'"))b+="'";else c=true;break;default:b+=a.charAt(f)}return b},_get:function(a,b){return a.settings[b]!==C?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),e=a.lastVal=a.input?a.input.val():null,f,h;f=h=this._getDefaultDate(a);
+var i=this._getFormatConfig(a);try{f=this.parseDate(c,e,i)||h}catch(g){this.log(g);e=b?"":e}a.selectedDay=f.getDate();a.drawMonth=a.selectedMonth=f.getMonth();a.drawYear=a.selectedYear=f.getFullYear();a.currentDay=e?f.getDate():0;a.currentMonth=e?f.getMonth():0;a.currentYear=e?f.getFullYear():0;this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var e=function(h){var i=new Date;
+i.setDate(i.getDate()+h);return i},f=function(h){try{return d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),h,d.datepicker._getFormatConfig(a))}catch(i){}var g=(h.toLowerCase().match(/^c/)?d.datepicker._getDate(a):null)||new Date,j=g.getFullYear(),l=g.getMonth();g=g.getDate();for(var u=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,k=u.exec(h);k;){switch(k[2]||"d"){case "d":case "D":g+=parseInt(k[1],10);break;case "w":case "W":g+=parseInt(k[1],10)*7;break;case "m":case "M":l+=parseInt(k[1],10);g=
+Math.min(g,d.datepicker._getDaysInMonth(j,l));break;case "y":case "Y":j+=parseInt(k[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(j,l));break}k=u.exec(h)}return new Date(j,l,g)};if(b=(b=b==null||b===""?c:typeof b=="string"?f(b):typeof b=="number"?isNaN(b)?c:e(b):new Date(b.getTime()))&&b.toString()=="Invalid Date"?c:b){b.setHours(0);b.setMinutes(0);b.setSeconds(0);b.setMilliseconds(0)}return this._daylightSavingAdjust(b)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>
+12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&
+a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?
+new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&n<k?k:n;this._daylightSavingAdjust(new Date(m,g,1))>n;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a));
+n=this._canAdjustMonth(a,-1,m,g)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+B+".datepicker._adjustDate('#"+a.id+"', -"+j+", 'M');\" title=\""+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>":f?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>";var s=this._get(a,"nextText");s=!h?s:this.formatDate(s,this._daylightSavingAdjust(new Date(m,
+g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+B+".datepicker._adjustDate('#"+a.id+"', +"+j+", 'M');\" title=\""+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>":f?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>";j=this._get(a,"currentText");s=this._get(a,"gotoCurrent")&&
+a.currentDay?u:b;j=!h?j:this.formatDate(j,s,this._getFormatConfig(a));h=!a.inline?'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+B+'.datepicker._hideDatepicker();">'+this._get(a,"closeText")+"</button>":"";e=e?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?h:"")+(this._isInRange(a,s)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+
+B+".datepicker._gotoToday('#"+a.id+"');\">"+j+"</button>":"")+(c?"":h)+"</div>":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");s=this._get(a,"dayNames");this._get(a,"dayNamesShort");var q=this._get(a,"dayNamesMin"),A=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),D=this._get(a,"showOtherMonths"),K=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var E=this._getDefaultDate(a),w="",x=0;x<i[0];x++){var O=
+"";this.maxRows=4;for(var G=0;G<i[1];G++){var P=this._daylightSavingAdjust(new Date(m,g,a.selectedDay)),t=" ui-corner-all",y="";if(l){y+='<div class="ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:y+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:y+=" ui-datepicker-group-middle";t="";break}y+='">'}y+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+t+'">'+(/all|left/.test(t)&&
+x==0?c?f:n:"")+(/all|right/.test(t)&&x==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,x>0||G>0,A,v)+'</div><table class="ui-datepicker-calendar"><thead><tr>';var z=j?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(t=0;t<7;t++){var r=(t+h)%7;z+="<th"+((t+h+6)%7>=5?' class="ui-datepicker-week-end"':"")+'><span title="'+s[r]+'">'+q[r]+"</span></th>"}y+=z+"</tr></thead><tbody>";z=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,
+z);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;z=Math.ceil((t+z)/7);this.maxRows=z=l?this.maxRows>z?this.maxRows:z:z;r=this._daylightSavingAdjust(new Date(m,g,1-t));for(var Q=0;Q<z;Q++){y+="<tr>";var R=!j?"":'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(r)+"</td>";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[r]):[true,""],F=r.getMonth()!=g,L=F&&!K||!I[0]||k&&r<k||o&&r>o;R+='<td class="'+((t+h+6)%7>=5?" ui-datepicker-week-end":"")+(F?" ui-datepicker-other-month":"")+(r.getTime()==
+P.getTime()&&g==a.selectedMonth&&a._keyEvent||E.getTime()==r.getTime()&&E.getTime()==P.getTime()?" "+this._dayOverClass:"")+(L?" "+this._unselectableClass+" ui-state-disabled":"")+(F&&!D?"":" "+I[1]+(r.getTime()==u.getTime()?" "+this._currentClass:"")+(r.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!F||D)&&I[2]?' title="'+I[2]+'"':"")+(L?"":' onclick="DP_jQuery_'+B+".datepicker._selectDay('#"+a.id+"',"+r.getMonth()+","+r.getFullYear()+', this);return false;"')+">"+(F&&!D?"&#xa0;":L?'<span class="ui-state-default">'+
+r.getDate()+"</span>":'<a class="ui-state-default'+(r.getTime()==b.getTime()?" ui-state-highlight":"")+(r.getTime()==u.getTime()?" ui-state-active":"")+(F?" ui-priority-secondary":"")+'" href="#">'+r.getDate()+"</a>")+"</td>";r.setDate(r.getDate()+1);r=this._daylightSavingAdjust(r)}y+=R+"</tr>"}g++;if(g>11){g=0;m++}y+="</tbody></table>"+(l?"</div>"+(i[0]>0&&G==i[1]-1?'<div class="ui-datepicker-row-break"></div>':""):"");O+=y}w+=O}w+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':
+"");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='<div class="ui-datepicker-title">',o="";if(h||!j)o+='<span class="ui-datepicker-month">'+i[b]+"</span>";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+B+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" >";for(var n=0;n<12;n++)if((!i||n>=e.getMonth())&&
+(!m||n<=f.getMonth()))o+='<option value="'+n+'"'+(n==b?' selected="selected"':"")+">"+g[n]+"</option>";o+="</select>"}u||(k+=o+(h||!(j&&l)?"&#xa0;":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+='<span class="ui-datepicker-year">'+c+"</span>";else{g=this._get(a,"yearRange").split(":");var s=(new Date).getFullYear();i=function(q){q=q.match(/c[+-].*/)?c+parseInt(q.substring(1),10):q.match(/[+-].*/)?s+parseInt(q,10):parseInt(q,10);return isNaN(q)?s:q};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,
+e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+B+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" >";b<=g;b++)a.yearshtml+='<option value="'+b+'"'+(b==c?' selected="selected"':"")+">"+b+"</option>";a.yearshtml+="</select>";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?"&#xa0;":"")+o;k+="</div>";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c=="Y"?b:0),f=a.drawMonth+
+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&b<c?c:b;return b=a&&b>a?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");if(b)b.apply(a.input?
+a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);c=this._daylightSavingAdjust(new Date(c,
+e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,
+"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=function(a){if(!this.length)return this;
+if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));return this.each(function(){typeof a==
+"string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.16";window["DP_jQuery_"+B]=d})(jQuery);
+;/*
+ * jQuery UI Progressbar 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar
+ *
+ * Depends:
+ *   jquery.ui.core.js
+ *   jquery.ui.widget.js
+ */
+(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow");
+this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*
+this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.16"})})(jQuery);
+;/*
+ * jQuery UI Effects 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/
+ */
+jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1],
+16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,
+a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d=
+a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor",
+"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,
+0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,
+211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,
+d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})};
+f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,
+[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.16",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c,a){var b;switch(c[0]){case "top":b=
+0;break;case "middle":b=0.5;break;case "bottom":b=1;break;default:b=c[0]/a.height}switch(c[1]){case "left":c=0;break;case "center":c=0.5;break;case "right":c=1;break;default:c=c[1]/a.width}return{x:c,y:b}},createWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent();var a={width:c.outerWidth(true),height:c.outerHeight(true),"float":c.css("float")},b=f("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),
+d=document.activeElement;c.wrap(b);if(c[0]===d||f.contains(c[0],d))f(d).focus();b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(e,g){a[g]=c.css(g);if(isNaN(parseInt(a[g],10)))a[g]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){var a,b=document.activeElement;
+if(c.parent().is(".ui-effects-wrapper")){a=c.parent().replaceWith(c);if(c[0]===b||f.contains(c[0],b))f(b).focus();return a}return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});
+return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,
+arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/
+2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,
+d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,
+a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,
+d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g))+b},easeOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*a)*Math.sin((a*e-c)*2*Math.PI/g)+d+b},easeInOutElastic:function(c,a,b,d,e){c=1.70158;var g=
+0,h=d;if(a==0)return b;if((a/=e/2)==2)return b+d;g||(g=e*0.3*1.5);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);if(a<1)return-0.5*h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)+b;return h*Math.pow(2,-10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)*0.5+d+b},easeInBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*(a/=e)*a*((g+1)*a-g)+b},easeOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*((a=a/e-1)*a*((g+1)*a+g)+1)+b},easeInOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;
+if((a/=e/2)<1)return d/2*a*a*(((g*=1.525)+1)*a-g)+b;return d/2*((a-=2)*a*(((g*=1.525)+1)*a+g)+2)+b},easeInBounce:function(c,a,b,d,e){return d-f.easing.easeOutBounce(c,e-a,0,d,e)+b},easeOutBounce:function(c,a,b,d,e){return(a/=e)<1/2.75?d*7.5625*a*a+b:a<2/2.75?d*(7.5625*(a-=1.5/2.75)*a+0.75)+b:a<2.5/2.75?d*(7.5625*(a-=2.25/2.75)*a+0.9375)+b:d*(7.5625*(a-=2.625/2.75)*a+0.984375)+b},easeInOutBounce:function(c,a,b,d,e){if(a<e/2)return f.easing.easeInBounce(c,a*2,0,d,e)*0.5+b;return f.easing.easeOutBounce(c,
+a*2-e,0,d,e)*0.5+d*0.5+b}})}(jQuery);
+;/*
+ * jQuery UI Effects Blind 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Blind
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(b){b.effects.blind=function(c){return this.queue(function(){var a=b(this),g=["position","top","bottom","left","right"],f=b.effects.setMode(a,c.options.mode||"hide"),d=c.options.direction||"vertical";b.effects.save(a,g);a.show();var e=b.effects.createWrapper(a).css({overflow:"hidden"}),h=d=="vertical"?"height":"width";d=d=="vertical"?e.height():e.width();f=="show"&&e.css(h,0);var i={};i[h]=f=="show"?d:0;e.animate(i,c.duration,c.options.easing,function(){f=="hide"&&a.hide();b.effects.restore(a,
+g);b.effects.removeWrapper(a);c.callback&&c.callback.apply(a[0],arguments);a.dequeue()})})}})(jQuery);
+;/*
+ * jQuery UI Effects Bounce 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Bounce
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(e){e.effects.bounce=function(b){return this.queue(function(){var a=e(this),l=["position","top","bottom","left","right"],h=e.effects.setMode(a,b.options.mode||"effect"),d=b.options.direction||"up",c=b.options.distance||20,m=b.options.times||5,i=b.duration||250;/show|hide/.test(h)&&l.push("opacity");e.effects.save(a,l);a.show();e.effects.createWrapper(a);var f=d=="up"||d=="down"?"top":"left";d=d=="up"||d=="left"?"pos":"neg";c=b.options.distance||(f=="top"?a.outerHeight({margin:true})/3:a.outerWidth({margin:true})/
+3);if(h=="show")a.css("opacity",0).css(f,d=="pos"?-c:c);if(h=="hide")c/=m*2;h!="hide"&&m--;if(h=="show"){var g={opacity:1};g[f]=(d=="pos"?"+=":"-=")+c;a.animate(g,i/2,b.options.easing);c/=2;m--}for(g=0;g<m;g++){var j={},k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing);c=h=="hide"?c*2:c/2}if(h=="hide"){g={opacity:0};g[f]=(d=="pos"?"-=":"+=")+c;a.animate(g,i/2,b.options.easing,function(){a.hide();e.effects.restore(a,l);e.effects.removeWrapper(a);
+b.callback&&b.callback.apply(this,arguments)})}else{j={};k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing,function(){e.effects.restore(a,l);e.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments)})}a.queue("fx",function(){a.dequeue()});a.dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Clip 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Clip
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(b){b.effects.clip=function(e){return this.queue(function(){var a=b(this),i=["position","top","bottom","left","right","height","width"],f=b.effects.setMode(a,e.options.mode||"hide"),c=e.options.direction||"vertical";b.effects.save(a,i);a.show();var d=b.effects.createWrapper(a).css({overflow:"hidden"});d=a[0].tagName=="IMG"?d:a;var g={size:c=="vertical"?"height":"width",position:c=="vertical"?"top":"left"};c=c=="vertical"?d.height():d.width();if(f=="show"){d.css(g.size,0);d.css(g.position,
+c/2)}var h={};h[g.size]=f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h,{queue:false,duration:e.duration,easing:e.options.easing,complete:function(){f=="hide"&&a.hide();b.effects.restore(a,i);b.effects.removeWrapper(a);e.callback&&e.callback.apply(a[0],arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Drop 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Drop
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(c){c.effects.drop=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right","opacity"],e=c.effects.setMode(a,d.options.mode||"hide"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a);var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true})/2:a.outerWidth({margin:true})/2);if(e=="show")a.css("opacity",0).css(f,b=="pos"?-g:g);var i={opacity:e==
+"show"?1:0};i[f]=(e=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Explode 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Explode
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(j){j.effects.explode=function(a){return this.queue(function(){var c=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3,d=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3;a.options.mode=a.options.mode=="toggle"?j(this).is(":visible")?"hide":"show":a.options.mode;var b=j(this).show().css("visibility","hidden"),g=b.offset();g.top-=parseInt(b.css("marginTop"),10)||0;g.left-=parseInt(b.css("marginLeft"),10)||0;for(var h=b.outerWidth(true),i=b.outerHeight(true),e=0;e<c;e++)for(var f=
+0;f<d;f++)b.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+
+e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery);
+;/*
+ * jQuery UI Effects Fade 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fade
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Fold 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fold
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],
+10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery);
+;/*
+ * jQuery UI Effects Highlight 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Highlight
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&&
+this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Pulsate 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Pulsate
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c<times;c++){b.animate({opacity:animateTo},duration,a.options.easing);animateTo=(animateTo+1)%2}b.animate({opacity:animateTo},duration,
+a.options.easing,function(){animateTo==0&&b.hide();a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()}).dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Scale 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Scale
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(c){c.effects.puff=function(b){return this.queue(function(){var a=c(this),e=c.effects.setMode(a,b.options.mode||"hide"),g=parseInt(b.options.percent,10)||150,h=g/100,i={height:a.height(),width:a.width()};c.extend(b.options,{fade:true,mode:e,percent:e=="hide"?g:100,from:e=="hide"?i:{height:i.height*h,width:i.width*h}});a.effect("scale",b.options,b.duration,b.callback);a.dequeue()})};c.effects.scale=function(b){return this.queue(function(){var a=c(this),e=c.extend(true,{},b.options),g=c.effects.setMode(a,
+b.options.mode||"effect"),h=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:g=="hide"?0:100),i=b.options.direction||"both",f=b.options.origin;if(g!="effect"){e.origin=f||["middle","center"];e.restore=true}f={height:a.height(),width:a.width()};a.from=b.options.from||(g=="show"?{height:0,width:0}:f);h={y:i!="horizontal"?h/100:1,x:i!="vertical"?h/100:1};a.to={height:f.height*h.y,width:f.width*h.x};if(b.options.fade){if(g=="show"){a.from.opacity=0;a.to.opacity=1}if(g=="hide"){a.from.opacity=
+1;a.to.opacity=0}}e.from=a.from;e.to=a.to;e.mode=g;a.effect("size",e,b.duration,b.callback);a.dequeue()})};c.effects.size=function(b){return this.queue(function(){var a=c(this),e=["position","top","bottom","left","right","width","height","overflow","opacity"],g=["position","top","bottom","left","right","overflow","opacity"],h=["width","height","overflow"],i=["fontSize"],f=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],k=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],
+p=c.effects.setMode(a,b.options.mode||"effect"),n=b.options.restore||false,m=b.options.scale||"both",l=b.options.origin,j={height:a.height(),width:a.width()};a.from=b.options.from||j;a.to=b.options.to||j;if(l){l=c.effects.getBaseline(l,j);a.from.top=(j.height-a.from.height)*l.y;a.from.left=(j.width-a.from.width)*l.x;a.to.top=(j.height-a.to.height)*l.y;a.to.left=(j.width-a.to.width)*l.x}var d={from:{y:a.from.height/j.height,x:a.from.width/j.width},to:{y:a.to.height/j.height,x:a.to.width/j.width}};
+if(m=="box"||m=="both"){if(d.from.y!=d.to.y){e=e.concat(f);a.from=c.effects.setTransition(a,f,d.from.y,a.from);a.to=c.effects.setTransition(a,f,d.to.y,a.to)}if(d.from.x!=d.to.x){e=e.concat(k);a.from=c.effects.setTransition(a,k,d.from.x,a.from);a.to=c.effects.setTransition(a,k,d.to.x,a.to)}}if(m=="content"||m=="both")if(d.from.y!=d.to.y){e=e.concat(i);a.from=c.effects.setTransition(a,i,d.from.y,a.from);a.to=c.effects.setTransition(a,i,d.to.y,a.to)}c.effects.save(a,n?e:g);a.show();c.effects.createWrapper(a);
+a.css("overflow","hidden").css(a.from);if(m=="content"||m=="both"){f=f.concat(["marginTop","marginBottom"]).concat(i);k=k.concat(["marginLeft","marginRight"]);h=e.concat(f).concat(k);a.find("*[width]").each(function(){child=c(this);n&&c.effects.save(child,h);var o={height:child.height(),width:child.width()};child.from={height:o.height*d.from.y,width:o.width*d.from.x};child.to={height:o.height*d.to.y,width:o.width*d.to.x};if(d.from.y!=d.to.y){child.from=c.effects.setTransition(child,f,d.from.y,child.from);
+child.to=c.effects.setTransition(child,f,d.to.y,child.to)}if(d.from.x!=d.to.x){child.from=c.effects.setTransition(child,k,d.from.x,child.from);child.to=c.effects.setTransition(child,k,d.to.x,child.to)}child.css(child.from);child.animate(child.to,b.duration,b.options.easing,function(){n&&c.effects.restore(child,h)})})}a.animate(a.to,{queue:false,duration:b.duration,easing:b.options.easing,complete:function(){a.to.opacity===0&&a.css("opacity",a.from.opacity);p=="hide"&&a.hide();c.effects.restore(a,
+n?e:g);c.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Shake 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Shake
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(d){d.effects.shake=function(a){return this.queue(function(){var b=d(this),j=["position","top","bottom","left","right"];d.effects.setMode(b,a.options.mode||"effect");var c=a.options.direction||"left",e=a.options.distance||20,l=a.options.times||3,f=a.duration||a.options.duration||140;d.effects.save(b,j);b.show();d.effects.createWrapper(b);var g=c=="up"||c=="down"?"top":"left",h=c=="up"||c=="left"?"pos":"neg";c={};var i={},k={};c[g]=(h=="pos"?"-=":"+=")+e;i[g]=(h=="pos"?"+=":"-=")+e*2;k[g]=
+(h=="pos"?"-=":"+=")+e*2;b.animate(c,f,a.options.easing);for(e=1;e<l;e++)b.animate(i,f,a.options.easing).animate(k,f,a.options.easing);b.animate(i,f,a.options.easing).animate(c,f/2,a.options.easing,function(){d.effects.restore(b,j);d.effects.removeWrapper(b);a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()});b.dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Slide 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Slide
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(c){c.effects.slide=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right"],f=c.effects.setMode(a,d.options.mode||"show"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a).css({overflow:"hidden"});var g=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var e=d.options.distance||(g=="top"?a.outerHeight({margin:true}):a.outerWidth({margin:true}));if(f=="show")a.css(g,b=="pos"?isNaN(e)?"-"+e:-e:e);
+var i={};i[g]=(f=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+e;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){f=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Transfer 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Transfer
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(e){e.effects.transfer=function(a){return this.queue(function(){var b=e(this),c=e(a.options.to),d=c.offset();c={top:d.top,left:d.left,height:c.innerHeight(),width:c.innerWidth()};d=b.offset();var f=e('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments);
+b.dequeue()})})}})(jQuery);
+;
Index: trunk/library/Zend/Ldap/Exception.php
===================================================================
--- trunk/library/Zend/Ldap/Exception.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Exception.php	(revision 5146)
@@ -0,0 +1,172 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 22996 2010-09-22 17:01:46Z sgehrig $
+ */
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @uses       Zend_Exception
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Exception extends Zend_Exception
+{
+    const LDAP_SUCCESS                        = 0x00;
+    const LDAP_OPERATIONS_ERROR               = 0x01;
+    const LDAP_PROTOCOL_ERROR                 = 0x02;
+    const LDAP_TIMELIMIT_EXCEEDED             = 0x03;
+    const LDAP_SIZELIMIT_EXCEEDED             = 0x04;
+    const LDAP_COMPARE_FALSE                  = 0x05;
+    const LDAP_COMPARE_TRUE                   = 0x06;
+    const LDAP_AUTH_METHOD_NOT_SUPPORTED      = 0x07;
+    const LDAP_STRONG_AUTH_REQUIRED           = 0x08;
+    const LDAP_PARTIAL_RESULTS                = 0x09;
+    const LDAP_REFERRAL                       = 0x0a;
+    const LDAP_ADMINLIMIT_EXCEEDED            = 0x0b;
+    const LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 0x0c;
+    const LDAP_CONFIDENTIALITY_REQUIRED       = 0x0d;
+    const LDAP_SASL_BIND_IN_PROGRESS          = 0x0e;
+    const LDAP_NO_SUCH_ATTRIBUTE              = 0x10;
+    const LDAP_UNDEFINED_TYPE                 = 0x11;
+    const LDAP_INAPPROPRIATE_MATCHING         = 0x12;
+    const LDAP_CONSTRAINT_VIOLATION           = 0x13;
+    const LDAP_TYPE_OR_VALUE_EXISTS           = 0x14;
+    const LDAP_INVALID_SYNTAX                 = 0x15;
+    const LDAP_NO_SUCH_OBJECT                 = 0x20;
+    const LDAP_ALIAS_PROBLEM                  = 0x21;
+    const LDAP_INVALID_DN_SYNTAX              = 0x22;
+    const LDAP_IS_LEAF                        = 0x23;
+    const LDAP_ALIAS_DEREF_PROBLEM            = 0x24;
+    const LDAP_PROXY_AUTHZ_FAILURE            = 0x2F;
+    const LDAP_INAPPROPRIATE_AUTH             = 0x30;
+    const LDAP_INVALID_CREDENTIALS            = 0x31;
+    const LDAP_INSUFFICIENT_ACCESS            = 0x32;
+    const LDAP_BUSY                           = 0x33;
+    const LDAP_UNAVAILABLE                    = 0x34;
+    const LDAP_UNWILLING_TO_PERFORM           = 0x35;
+    const LDAP_LOOP_DETECT                    = 0x36;
+    const LDAP_NAMING_VIOLATION               = 0x40;
+    const LDAP_OBJECT_CLASS_VIOLATION         = 0x41;
+    const LDAP_NOT_ALLOWED_ON_NONLEAF         = 0x42;
+    const LDAP_NOT_ALLOWED_ON_RDN             = 0x43;
+    const LDAP_ALREADY_EXISTS                 = 0x44;
+    const LDAP_NO_OBJECT_CLASS_MODS           = 0x45;
+    const LDAP_RESULTS_TOO_LARGE              = 0x46;
+    const LDAP_AFFECTS_MULTIPLE_DSAS          = 0x47;
+    const LDAP_OTHER                          = 0x50;
+    const LDAP_SERVER_DOWN                    = 0x51;
+    const LDAP_LOCAL_ERROR                    = 0x52;
+    const LDAP_ENCODING_ERROR                 = 0x53;
+    const LDAP_DECODING_ERROR                 = 0x54;
+    const LDAP_TIMEOUT                        = 0x55;
+    const LDAP_AUTH_UNKNOWN                   = 0x56;
+    const LDAP_FILTER_ERROR                   = 0x57;
+    const LDAP_USER_CANCELLED                 = 0x58;
+    const LDAP_PARAM_ERROR                    = 0x59;
+    const LDAP_NO_MEMORY                      = 0x5a;
+    const LDAP_CONNECT_ERROR                  = 0x5b;
+    const LDAP_NOT_SUPPORTED                  = 0x5c;
+    const LDAP_CONTROL_NOT_FOUND              = 0x5d;
+    const LDAP_NO_RESULTS_RETURNED            = 0x5e;
+    const LDAP_MORE_RESULTS_TO_RETURN         = 0x5f;
+    const LDAP_CLIENT_LOOP                    = 0x60;
+    const LDAP_REFERRAL_LIMIT_EXCEEDED        = 0x61;
+    const LDAP_CUP_RESOURCES_EXHAUSTED        = 0x71;
+    const LDAP_CUP_SECURITY_VIOLATION         = 0x72;
+    const LDAP_CUP_INVALID_DATA               = 0x73;
+    const LDAP_CUP_UNSUPPORTED_SCHEME         = 0x74;
+    const LDAP_CUP_RELOAD_REQUIRED            = 0x75;
+    const LDAP_CANCELLED                      = 0x76;
+    const LDAP_NO_SUCH_OPERATION              = 0x77;
+    const LDAP_TOO_LATE                       = 0x78;
+    const LDAP_CANNOT_CANCEL                  = 0x79;
+    const LDAP_ASSERTION_FAILED               = 0x7A;
+    const LDAP_SYNC_REFRESH_REQUIRED          = 0x1000;
+    const LDAP_X_SYNC_REFRESH_REQUIRED        = 0x4100;
+    const LDAP_X_NO_OPERATION                 = 0x410e;
+    const LDAP_X_ASSERTION_FAILED             = 0x410f;
+    const LDAP_X_NO_REFERRALS_FOUND           = 0x4110;
+    const LDAP_X_CANNOT_CHAIN                 = 0x4111;
+
+    /* internal error code constants */
+
+    const LDAP_X_DOMAIN_MISMATCH              = 0x7001;
+    const LDAP_X_EXTENSION_NOT_LOADED         = 0x7002;
+
+    /**
+     * @param Zend_Ldap $ldap A Zend_Ldap object
+     * @param string    $str  An informtive exception message
+     * @param int       $code An LDAP error code
+     */
+    public function __construct(Zend_Ldap $ldap = null, $str = null, $code = 0)
+    {
+        $errorMessages = array();
+        $message = '';
+        if ($ldap !== null) {
+            $oldCode = $code;
+            $message = $ldap->getLastError($code, $errorMessages) . ': ';
+            if ($code === 0) {
+                $message = '';
+                $code = $oldCode;
+            }
+        }
+        if (empty($message)) {
+            if ($code > 0) {
+                $message = '0x' . dechex($code) . ': ';
+            }
+        }
+
+        if (!empty($str)) {
+            $message .= $str;
+        } else {
+            $message .= 'no exception message';
+        }
+
+        parent::__construct($message, $code);
+    }
+
+
+    /**
+     * @deprecated not necessary any more - will be removed
+     * @param Zend_Ldap $ldap A Zend_Ldap object
+     * @return int The current error code for the resource
+     */
+    public static function getLdapCode(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            return $ldap->getLastErrorCode();
+        }
+        return 0;
+    }
+
+    /**
+     * @deprecated will be removed
+     * @return int The current error code for this exception
+     */
+    public function getErrorCode()
+    {
+        return $this->getCode();
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/Logical.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Logical.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Logical.php	(revision 5146)
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Logical.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_Abstract
+ */
+require_once 'Zend/Ldap/Filter/Abstract.php';
+/**
+ * @see Zend_Ldap_Filter_String
+ */
+require_once 'Zend/Ldap/Filter/String.php';
+
+/**
+ * Zend_Ldap_Filter_Logical provides a base implementation for a grouping filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Ldap_Filter_Logical extends Zend_Ldap_Filter_Abstract
+{
+    const TYPE_AND = '&';
+    const TYPE_OR  = '|';
+
+    /**
+     * All the sub-filters for this grouping filter.
+     *
+     * @var array
+     */
+    private $_subfilters;
+
+    /**
+     * The grouping symbol.
+     *
+     * @var string
+     */
+    private $_symbol;
+
+    /**
+     * Creates a new grouping filter.
+     *
+     * @param array  $subfilters
+     * @param string $symbol
+     */
+    protected function __construct(array $subfilters, $symbol)
+    {
+        foreach ($subfilters as $key => $s) {
+            if (is_string($s)) $subfilters[$key] = new Zend_Ldap_Filter_String($s);
+            else if (!($s instanceof Zend_Ldap_Filter_Abstract)) {
+                /**
+                 * @see Zend_Ldap_Filter_Exception
+                 */
+                require_once 'Zend/Ldap/Filter/Exception.php';
+                throw new Zend_Ldap_Filter_Exception('Only strings or Zend_Ldap_Filter_Abstract allowed.');
+            }
+        }
+        $this->_subfilters = $subfilters;
+        $this->_symbol = $symbol;
+    }
+
+    /**
+     * Adds a filter to this grouping filter.
+     *
+     * @param  Zend_Ldap_Filter_Abstract $filter
+     * @return Zend_Ldap_Filter_Logical
+     */
+    public function addFilter(Zend_Ldap_Filter_Abstract $filter)
+    {
+        $new = clone $this;
+        $new->_subfilters[] = $filter;
+        return $new;
+    }
+
+    /**
+     * Returns a string representation of the filter.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        $return = '(' . $this->_symbol;
+        foreach ($this->_subfilters as $sub) $return .= $sub->toString();
+        $return .= ')';
+        return $return;
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/Mask.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Mask.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Mask.php	(revision 5146)
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Mask.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_String
+ */
+require_once 'Zend/Ldap/Filter/String.php';
+
+
+/**
+ * Zend_Ldap_Filter_Mask provides a simple string filter to be used with a mask.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_Mask extends Zend_Ldap_Filter_String
+{
+    /**
+     * Creates a Zend_Ldap_Filter_String.
+     *
+     * @param string $mask
+     * @param string $value,...
+     */
+    public function __construct($mask, $value)
+    {
+        $args = func_get_args();
+        array_shift($args);
+        for ($i = 0; $i<count($args); $i++) {
+            $args[$i] = self::escapeValue($args[$i]);
+        }
+        $filter = vsprintf($mask, $args);
+        parent::__construct($filter);
+    }
+
+    /**
+     * Returns a string representation of the filter.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return $this->_filter;
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/Exception.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Exception.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Exception.php	(revision 5146)
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_Exception extends Zend_Exception
+{
+}
Index: trunk/library/Zend/Ldap/Filter/Not.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Not.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Not.php	(revision 5146)
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Not.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_Abstract
+ */
+require_once 'Zend/Ldap/Filter/Abstract.php';
+
+/**
+ * Zend_Ldap_Filter_Not provides a negation filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_Not extends Zend_Ldap_Filter_Abstract
+{
+    /**
+     * The underlying filter.
+     *
+     * @var Zend_Ldap_Filter_Abstract
+     */
+    private $_filter;
+
+    /**
+     * Creates a Zend_Ldap_Filter_Not.
+     *
+     * @param Zend_Ldap_Filter_Abstract $filter
+     */
+    public function __construct(Zend_Ldap_Filter_Abstract $filter)
+    {
+        $this->_filter = $filter;
+    }
+
+    /**
+     * Negates the filter.
+     *
+     * @return Zend_Ldap_Filter_Abstract
+     */
+    public function negate()
+    {
+        return $this->_filter;
+    }
+
+    /**
+     * Returns a string representation of the filter.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return '(!' . $this->_filter->toString() . ')';
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/Or.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Or.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Or.php	(revision 5146)
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Or.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_Logical
+ */
+require_once 'Zend/Ldap/Filter/Logical.php';
+
+/**
+ * Zend_Ldap_Filter_Or provides an 'or' filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_Or extends Zend_Ldap_Filter_Logical
+{
+    /**
+     * Creates an 'or' grouping filter.
+     *
+     * @param array $subfilters
+     */
+    public function __construct(array $subfilters)
+    {
+        parent::__construct($subfilters, self::TYPE_OR);
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/And.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/And.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/And.php	(revision 5146)
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: And.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_Logical
+ */
+require_once 'Zend/Ldap/Filter/Logical.php';
+
+/**
+ * Zend_Ldap_Filter_And provides an 'and' filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_And extends Zend_Ldap_Filter_Logical
+{
+    /**
+     * Creates an 'and' grouping filter.
+     *
+     * @param array $subfilters
+     */
+    public function __construct(array $subfilters)
+    {
+        parent::__construct($subfilters, self::TYPE_AND);
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/Abstract.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Abstract.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Abstract.php	(revision 5146)
@@ -0,0 +1,157 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Abstract.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Filter_Abstract provides a base implementation for filters.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Ldap_Filter_Abstract
+{
+    /**
+     * Returns a string representation of the filter.
+     *
+     * @return string
+     */
+    abstract public function toString();
+
+    /**
+     * Returns a string representation of the filter.
+     * @see toString()
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->toString();
+    }
+
+    /**
+     * Negates the filter.
+     *
+     * @return Zend_Ldap_Filter_Abstract
+     */
+    public function negate()
+    {
+        /**
+         * Zend_Ldap_Filter_Not
+         */
+        require_once 'Zend/Ldap/Filter/Not.php';
+        return new Zend_Ldap_Filter_Not($this);
+    }
+
+    /**
+     * Creates an 'and' filter.
+     *
+     * @param  Zend_Ldap_Filter_Abstract $filter,...
+     * @return Zend_Ldap_Filter_And
+     */
+    public function addAnd($filter)
+    {
+        /**
+         * Zend_Ldap_Filter_And
+         */
+        require_once 'Zend/Ldap/Filter/And.php';
+        $fa = func_get_args();
+        $args = array_merge(array($this), $fa);
+        return new Zend_Ldap_Filter_And($args);
+    }
+
+    /**
+     * Creates an 'or' filter.
+     *
+     * @param  Zend_Ldap_Filter_Abstract $filter,...
+     * @return Zend_Ldap_Filter_Or
+     */
+    public function addOr($filter)
+    {
+        /**
+         * Zend_Ldap_Filter_Or
+         */
+        require_once 'Zend/Ldap/Filter/Or.php';
+        $fa = func_get_args();
+        $args = array_merge(array($this), $fa);
+        return new Zend_Ldap_Filter_Or($args);
+    }
+
+    /**
+     * Escapes the given VALUES according to RFC 2254 so that they can be safely used in LDAP filters.
+     *
+     * Any control characters with an ACII code < 32 as well as the characters with special meaning in
+     * LDAP filters "*", "(", ")", and "\" (the backslash) are converted into the representation of a
+     * backslash followed by two hex digits representing the hexadecimal value of the character.
+     * @see Net_LDAP2_Util::escape_filter_value() from Benedikt Hallinger <beni@php.net>
+     * @link http://pear.php.net/package/Net_LDAP2
+     * @author Benedikt Hallinger <beni@php.net>
+     *
+     * @param  string|array $values Array of values to escape
+     * @return array Array $values, but escaped
+     */
+    public static function escapeValue($values = array())
+    {
+        /**
+         * @see Zend_Ldap_Converter
+         */
+        require_once 'Zend/Ldap/Converter.php';
+
+        if (!is_array($values)) $values = array($values);
+        foreach ($values as $key => $val) {
+            // Escaping of filter meta characters
+            $val = str_replace(array('\\', '*', '(', ')'), array('\5c', '\2a', '\28', '\29'), $val);
+            // ASCII < 32 escaping
+            $val = Zend_Ldap_Converter::ascToHex32($val);
+            if (null === $val) $val = '\0';  // apply escaped "null" if string is empty
+            $values[$key] = $val;
+        }
+        return (count($values) == 1) ? $values[0] : $values;
+    }
+
+    /**
+     * Undoes the conversion done by {@link escapeValue()}.
+     *
+     * Converts any sequences of a backslash followed by two hex digits into the corresponding character.
+     * @see Net_LDAP2_Util::escape_filter_value() from Benedikt Hallinger <beni@php.net>
+     * @link http://pear.php.net/package/Net_LDAP2
+     * @author Benedikt Hallinger <beni@php.net>
+     *
+     * @param  string|array $values Array of values to escape
+     * @return array Array $values, but unescaped
+     */
+    public static function unescapeValue($values = array())
+    {
+        /**
+         * @see Zend_Ldap_Converter
+         */
+        require_once 'Zend/Ldap/Converter.php';
+
+        if (!is_array($values)) $values = array($values);
+        foreach ($values as $key => $value) {
+            // Translate hex code into ascii
+            $values[$key] = Zend_Ldap_Converter::hex32ToAsc($value);
+        }
+        return (count($values) == 1) ? $values[0] : $values;
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/String.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/String.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/String.php	(revision 5146)
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: String.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_Abstract
+ */
+require_once 'Zend/Ldap/Filter/Abstract.php';
+
+/**
+ * Zend_Ldap_Filter_String provides a simple custom string filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_String extends Zend_Ldap_Filter_Abstract
+{
+    /**
+     * The filter.
+     *
+     * @var string
+     */
+    protected $_filter;
+
+    /**
+     * Creates a Zend_Ldap_Filter_String.
+     *
+     * @param string $filter
+     */
+    public function __construct($filter)
+    {
+        $this->_filter = $filter;
+    }
+
+    /**
+     * Returns a string representation of the filter.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return '(' . $this->_filter . ')';
+    }
+}
Index: trunk/library/Zend/Ldap/Node/RootDse/eDirectory.php
===================================================================
--- trunk/library/Zend/Ldap/Node/RootDse/eDirectory.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/RootDse/eDirectory.php	(revision 5146)
@@ -0,0 +1,160 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: eDirectory.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_RootDse
+ */
+require_once 'Zend/Ldap/Node/RootDse.php';
+
+/**
+ * Zend_Ldap_Node_RootDse provides a simple data-container for the RootDSE node of
+ * a Novell eDirectory server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_RootDse_eDirectory extends Zend_Ldap_Node_RootDse
+{
+    /**
+     * Determines if the extension is supported
+     *
+     * @param  string|array $oids oid(s) to check
+     * @return boolean
+     */
+    public function supportsExtension($oids)
+    {
+        return $this->attributeHasValue('supportedExtension', $oids);
+    }
+
+    /**
+     * Gets the vendorName.
+     *
+     * @return string|null
+     */
+    public function getVendorName()
+    {
+        return $this->getAttribute('vendorName', 0);
+    }
+
+    /**
+     * Gets the vendorVersion.
+     *
+     * @return string|null
+     */
+    public function getVendorVersion()
+    {
+        return $this->getAttribute('vendorVersion', 0);
+    }
+
+    /**
+     * Gets the dsaName.
+     *
+     * @return string|null
+     */
+    public function getDsaName()
+    {
+        return $this->getAttribute('dsaName', 0);
+    }
+
+    /**
+     * Gets the server statistics "errors".
+     *
+     * @return string|null
+     */
+    public function getStatisticsErrors()
+    {
+        return $this->getAttribute('errors', 0);
+    }
+
+    /**
+     * Gets the server statistics "securityErrors".
+     *
+     * @return string|null
+     */
+    public function getStatisticsSecurityErrors()
+    {
+        return $this->getAttribute('securityErrors', 0);
+    }
+
+    /**
+     * Gets the server statistics "chainings".
+     *
+     * @return string|null
+     */
+    public function getStatisticsChainings()
+    {
+        return $this->getAttribute('chainings', 0);
+    }
+
+    /**
+     * Gets the server statistics "referralsReturned".
+     *
+     * @return string|null
+     */
+    public function getStatisticsReferralsReturned()
+    {
+        return $this->getAttribute('referralsReturned', 0);
+    }
+
+    /**
+     * Gets the server statistics "extendedOps".
+     *
+     * @return string|null
+     */
+    public function getStatisticsExtendedOps()
+    {
+        return $this->getAttribute('extendedOps', 0);
+    }
+
+    /**
+     * Gets the server statistics "abandonOps".
+     *
+     * @return string|null
+     */
+    public function getStatisticsAbandonOps()
+    {
+        return $this->getAttribute('abandonOps', 0);
+    }
+
+    /**
+     * Gets the server statistics "wholeSubtreeSearchOps".
+     *
+     * @return string|null
+     */
+    public function getStatisticsWholeSubtreeSearchOps()
+    {
+        return $this->getAttribute('wholeSubtreeSearchOps', 0);
+    }
+
+    /**
+     * Gets the server type
+     *
+     * @return int
+     */
+    public function getServerType()
+    {
+        return self::SERVER_TYPE_EDIRECTORY;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/RootDse/ActiveDirectory.php
===================================================================
--- trunk/library/Zend/Ldap/Node/RootDse/ActiveDirectory.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/RootDse/ActiveDirectory.php	(revision 5146)
@@ -0,0 +1,247 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: ActiveDirectory.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_RootDse
+ */
+require_once 'Zend/Ldap/Node/RootDse.php';
+
+/**
+ * Zend_Ldap_Node_RootDse provides a simple data-container for the RootDSE node of
+ * an Active Directory server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_RootDse_ActiveDirectory extends Zend_Ldap_Node_RootDse
+{
+    /**
+     * Gets the configurationNamingContext.
+     *
+     * @return string|null
+     */
+    public function getConfigurationNamingContext()
+    {
+        return $this->getAttribute('configurationNamingContext', 0);
+    }
+
+    /**
+     * Gets the currentTime.
+     *
+     * @return string|null
+     */
+    public function getCurrentTime()
+    {
+        return $this->getAttribute('currentTime', 0);
+    }
+
+    /**
+     * Gets the defaultNamingContext.
+     *
+     * @return string|null
+     */
+    public function getDefaultNamingContext()
+    {
+        return $this->getAttribute('defaultNamingContext', 0);
+    }
+
+    /**
+     * Gets the dnsHostName.
+     *
+     * @return string|null
+     */
+    public function getDnsHostName()
+    {
+        return $this->getAttribute('dnsHostName', 0);
+    }
+
+    /**
+     * Gets the domainControllerFunctionality.
+     *
+     * @return string|null
+     */
+    public function getDomainControllerFunctionality()
+    {
+        return $this->getAttribute('domainControllerFunctionality', 0);
+    }
+
+    /**
+     * Gets the domainFunctionality.
+     *
+     * @return string|null
+     */
+    public function getDomainFunctionality()
+    {
+        return $this->getAttribute('domainFunctionality', 0);
+    }
+
+    /**
+     * Gets the dsServiceName.
+     *
+     * @return string|null
+     */
+    public function getDsServiceName()
+    {
+        return $this->getAttribute('dsServiceName', 0);
+    }
+
+    /**
+     * Gets the forestFunctionality.
+     *
+     * @return string|null
+     */
+    public function getForestFunctionality()
+    {
+        return $this->getAttribute('forestFunctionality', 0);
+    }
+
+    /**
+     * Gets the highestCommittedUSN.
+     *
+     * @return string|null
+     */
+    public function getHighestCommittedUSN()
+    {
+        return $this->getAttribute('highestCommittedUSN', 0);
+    }
+
+    /**
+     * Gets the isGlobalCatalogReady.
+     *
+     * @return string|null
+     */
+    public function getIsGlobalCatalogReady()
+    {
+        return $this->getAttribute('isGlobalCatalogReady', 0);
+    }
+
+    /**
+     * Gets the isSynchronized.
+     *
+     * @return string|null
+     */
+    public function getIsSynchronized()
+    {
+        return $this->getAttribute('isSynchronized', 0);
+    }
+
+    /**
+     * Gets the ldapServiceName.
+     *
+     * @return string|null
+     */
+    public function getLdapServiceName()
+    {
+        return $this->getAttribute('ldapServiceName', 0);
+    }
+
+    /**
+     * Gets the rootDomainNamingContext.
+     *
+     * @return string|null
+     */
+    public function getRootDomainNamingContext()
+    {
+        return $this->getAttribute('rootDomainNamingContext', 0);
+    }
+
+    /**
+     * Gets the schemaNamingContext.
+     *
+     * @return string|null
+     */
+    public function getSchemaNamingContext()
+    {
+        return $this->getAttribute('schemaNamingContext', 0);
+    }
+
+    /**
+     * Gets the serverName.
+     *
+     * @return string|null
+     */
+    public function getServerName()
+    {
+        return $this->getAttribute('serverName', 0);
+    }
+
+    /**
+     * Determines if the capability is supported
+     *
+     * @param string|string|array $oids capability(s) to check
+     * @return boolean
+     */
+    public function supportsCapability($oids)
+    {
+        return $this->attributeHasValue('supportedCapabilities', $oids);
+    }
+
+    /**
+     * Determines if the control is supported
+     *
+     * @param string|array $oids control oid(s) to check
+     * @return boolean
+     */
+    public function supportsControl($oids)
+    {
+        return $this->attributeHasValue('supportedControl', $oids);
+    }
+
+    /**
+     * Determines if the version is supported
+     *
+     * @param string|array $policies policy(s) to check
+     * @return boolean
+     */
+    public function supportsPolicy($policies)
+    {
+        return $this->attributeHasValue('supportedLDAPPolicies', $policies);
+    }
+
+    /**
+     * Gets the server type
+     *
+     * @return int
+     */
+    public function getServerType()
+    {
+        return self::SERVER_TYPE_ACTIVEDIRECTORY;
+    }
+
+    /**
+     * Returns the schema DN
+     *
+     * @return Zend_Ldap_Dn
+     */
+    public function getSchemaDn()
+    {
+        $schemaDn = $this->getSchemaNamingContext();
+        /**
+         * @see Zend_Ldap_Dn
+         */
+        require_once 'Zend/Ldap/Dn.php';
+        return Zend_Ldap_Dn::fromString($schemaDn);
+    }
+}
Index: trunk/library/Zend/Ldap/Node/RootDse/OpenLdap.php
===================================================================
--- trunk/library/Zend/Ldap/Node/RootDse/OpenLdap.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/RootDse/OpenLdap.php	(revision 5146)
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: OpenLdap.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_RootDse
+ */
+require_once 'Zend/Ldap/Node/RootDse.php';
+
+/**
+ * Zend_Ldap_Node_RootDse provides a simple data-container for the RootDSE node of
+ * an OpenLDAP server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_RootDse_OpenLdap extends Zend_Ldap_Node_RootDse
+{
+    /**
+     * Gets the configContext.
+     *
+     * @return string|null
+     */
+    public function getConfigContext()
+    {
+        return $this->getAttribute('configContext', 0);
+    }
+
+    /**
+     * Gets the monitorContext.
+     *
+     * @return string|null
+     */
+    public function getMonitorContext()
+    {
+        return $this->getAttribute('monitorContext', 0);
+    }
+
+    /**
+     * Determines if the control is supported
+     *
+     * @param  string|array $oids control oid(s) to check
+     * @return boolean
+     */
+    public function supportsControl($oids)
+    {
+        return $this->attributeHasValue('supportedControl', $oids);
+    }
+
+    /**
+     * Determines if the extension is supported
+     *
+     * @param  string|array $oids oid(s) to check
+     * @return boolean
+     */
+    public function supportsExtension($oids)
+    {
+        return $this->attributeHasValue('supportedExtension', $oids);
+    }
+
+    /**
+     * Determines if the feature is supported
+     *
+     * @param  string|array $oids feature oid(s) to check
+     * @return boolean
+     */
+    public function supportsFeature($oids)
+    {
+        return $this->attributeHasValue('supportedFeatures', $oids);
+    }
+
+    /**
+     * Gets the server type
+     *
+     * @return int
+     */
+    public function getServerType()
+    {
+        return self::SERVER_TYPE_OPENLDAP;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/Item.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/Item.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/Item.php	(revision 5146)
@@ -0,0 +1,163 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Item.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Node_Schema_Item provides a base implementation for managing schema
+ * items like objectClass and attribute.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Ldap_Node_Schema_Item implements ArrayAccess, Countable
+{
+    /**
+     * The underlying data
+     *
+     * @var array
+     */
+    protected $_data;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data
+     */
+    public function __construct(array $data)
+    {
+        $this->setData($data);
+    }
+
+    /**
+     * Sets the data
+     *
+     * @param  array $data
+     * @return Zend_Ldap_Node_Schema_Item Provides a fluid interface
+     */
+    public function setData(array $data)
+    {
+        $this->_data = $data;
+        return $this;
+    }
+
+    /**
+     * Gets the data
+     *
+     * @return array
+     */
+    public function getData()
+    {
+        return $this->_data;
+    }
+
+    /**
+     * Gets a specific attribute from this item
+     *
+     * @param  string $name
+     * @return mixed
+     */
+    public function __get($name)
+    {
+        if (array_key_exists($name, $this->_data)) {
+            return $this->_data[$name];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Checks whether a specific attribute exists.
+     *
+     * @param  string $name
+     * @return boolean
+     */
+    public function __isset($name)
+    {
+        return (array_key_exists($name, $this->_data));
+    }
+
+    /**
+     * Always throws BadMethodCallException
+     * Implements ArrayAccess.
+     *
+     * This method is needed for a full implementation of ArrayAccess
+     *
+     * @param  string $name
+     * @param  mixed $value
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function offsetSet($name, $value)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Gets a specific attribute from this item
+     *
+     * @param  string $name
+     * @return mixed
+     */
+    public function offsetGet($name)
+    {
+        return $this->__get($name);
+    }
+
+    /**
+     * Always throws BadMethodCallException
+     * Implements ArrayAccess.
+     *
+     * This method is needed for a full implementation of ArrayAccess
+     *
+     * @param  string $name
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function offsetUnset($name)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Checks whether a specific attribute exists.
+     *
+     * @param  string $name
+     * @return boolean
+     */
+    public function offsetExists($name)
+    {
+        return $this->__isset($name);
+    }
+
+    /**
+     * Returns the number of attributes.
+     * Implements Countable
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->_data);
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/AttributeType/ActiveDirectory.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/AttributeType/ActiveDirectory.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/AttributeType/ActiveDirectory.php	(revision 5146)
@@ -0,0 +1,104 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: ActiveDirectory.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema_Item
+ */
+require_once 'Zend/Ldap/Node/Schema/Item.php';
+/**
+ * @see Zend_Ldap_Node_Schema_AttributeType_Interface
+ */
+require_once 'Zend/Ldap/Node/Schema/AttributeType/Interface.php';
+
+/**
+ * Zend_Ldap_Node_Schema_AttributeType_ActiveDirectory provides access to the attribute type
+ * schema information on an Active Directory server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_AttributeType_ActiveDirectory extends Zend_Ldap_Node_Schema_Item
+    implements Zend_Ldap_Node_Schema_AttributeType_Interface
+{
+    /**
+     * Gets the attribute name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->ldapdisplayname[0];
+    }
+
+    /**
+     * Gets the attribute OID
+     *
+     * @return string
+     */
+    public function getOid()
+    {
+
+    }
+
+    /**
+     * Gets the attribute syntax
+     *
+     * @return string
+     */
+    public function getSyntax()
+    {
+
+    }
+
+    /**
+     * Gets the attribute maximum length
+     *
+     * @return int|null
+     */
+    public function getMaxLength()
+    {
+
+    }
+
+    /**
+     * Returns if the attribute is single-valued.
+     *
+     * @return boolean
+     */
+    public function isSingleValued()
+    {
+
+    }
+
+    /**
+     * Gets the attribute description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/AttributeType/Interface.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/AttributeType/Interface.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/AttributeType/Interface.php	(revision 5146)
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Node_Schema_AttributeType_Interface provides a contract for schema attribute-types.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Ldap_Node_Schema_AttributeType_Interface
+{
+    /**
+     * Gets the attribute name
+     *
+     * @return string
+     */
+    public function getName();
+
+    /**
+     * Gets the attribute OID
+     *
+     * @return string
+     */
+    public function getOid();
+
+    /**
+     * Gets the attribute syntax
+     *
+     * @return string
+     */
+    public function getSyntax();
+
+    /**
+     * Gets the attribute maximum length
+     *
+     * @return int|null
+     */
+    public function getMaxLength();
+
+    /**
+     * Returns if the attribute is single-valued.
+     *
+     * @return boolean
+     */
+    public function isSingleValued();
+
+    /**
+     * Gets the attribute description
+     *
+     * @return string
+     */
+    public function getDescription();
+}
Index: trunk/library/Zend/Ldap/Node/Schema/AttributeType/OpenLdap.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/AttributeType/OpenLdap.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/AttributeType/OpenLdap.php	(revision 5146)
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: OpenLdap.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema_Item
+ */
+require_once 'Zend/Ldap/Node/Schema/Item.php';
+/**
+ * @see Zend_Ldap_Node_Schema_AttributeType_Interface
+ */
+require_once 'Zend/Ldap/Node/Schema/AttributeType/Interface.php';
+
+/**
+ * Zend_Ldap_Node_Schema_AttributeType_OpenLdap provides access to the attribute type
+ * schema information on an OpenLDAP server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_AttributeType_OpenLdap extends Zend_Ldap_Node_Schema_Item
+    implements Zend_Ldap_Node_Schema_AttributeType_Interface
+{
+    /**
+     * Gets the attribute name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Gets the attribute OID
+     *
+     * @return string
+     */
+    public function getOid()
+    {
+        return $this->oid;
+    }
+
+    /**
+     * Gets the attribute syntax
+     *
+     * @return string
+     */
+    public function getSyntax()
+    {
+        if ($this->syntax === null) {
+            $parent = $this->getParent();
+            if ($parent === null) return null;
+            else return $parent->getSyntax();
+        } else {
+            return $this->syntax;
+        }
+    }
+
+    /**
+     * Gets the attribute maximum length
+     *
+     * @return int|null
+     */
+    public function getMaxLength()
+    {
+        $maxLength = $this->{'max-length'};
+        if ($maxLength === null) {
+            $parent = $this->getParent();
+            if ($parent === null) return null;
+            else return $parent->getMaxLength();
+        } else {
+            return (int)$maxLength;
+        }
+    }
+
+    /**
+     * Returns if the attribute is single-valued.
+     *
+     * @return boolean
+     */
+    public function isSingleValued()
+    {
+        return $this->{'single-value'};
+    }
+
+    /**
+     * Gets the attribute description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->desc;
+    }
+
+    /**
+     * Returns the parent attribute type in the inhertitance tree if one exists
+     *
+     * @return Zend_Ldap_Node_Schema_AttributeType_OpenLdap|null
+     */
+    public function getParent()
+    {
+        if (count($this->_parents) === 1) {
+            return $this->_parents[0];
+        }
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/ActiveDirectory.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/ActiveDirectory.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/ActiveDirectory.php	(revision 5146)
@@ -0,0 +1,103 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: ActiveDirectory.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema
+ */
+require_once 'Zend/Ldap/Node/Schema.php';
+/**
+ * @see Zend_Ldap_Node_Schema_AttributeType_ActiveDirectory
+ */
+require_once 'Zend/Ldap/Node/Schema/AttributeType/ActiveDirectory.php';
+/**
+ * @see Zend_Ldap_Node_Schema_ObjectClass_ActiveDirectory
+ */
+require_once 'Zend/Ldap/Node/Schema/ObjectClass/ActiveDirectory.php';
+
+/**
+ * Zend_Ldap_Node_Schema_ActiveDirectory provides a simple data-container for the Schema node of
+ * an Active Directory server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_ActiveDirectory extends Zend_Ldap_Node_Schema
+{
+    /**
+     * The attribute Types
+     *
+     * @var array
+     */
+    protected $_attributeTypes = array();
+    /**
+     * The object classes
+     *
+     * @var array
+     */
+    protected $_objectClasses = array();
+
+    /**
+     * Parses the schema
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  Zend_Ldap    $ldap
+     * @return Zend_Ldap_Node_Schema Provides a fluid interface
+     */
+    protected function _parseSchema(Zend_Ldap_Dn $dn, Zend_Ldap $ldap)
+    {
+        parent::_parseSchema($dn, $ldap);
+        foreach ($ldap->search('(objectClass=classSchema)', $dn,
+                Zend_Ldap::SEARCH_SCOPE_ONE) as $node) {
+            $val = new Zend_Ldap_Node_Schema_ObjectClass_ActiveDirectory($node);
+            $this->_objectClasses[$val->getName()] = $val;
+        }
+        foreach ($ldap->search('(objectClass=attributeSchema)', $dn,
+                Zend_Ldap::SEARCH_SCOPE_ONE) as $node) {
+            $val = new Zend_Ldap_Node_Schema_AttributeType_ActiveDirectory($node);
+            $this->_attributeTypes[$val->getName()] = $val;
+        }
+        return $this;
+    }
+
+    /**
+     * Gets the attribute Types
+     *
+     * @return array
+     */
+    public function getAttributeTypes()
+    {
+        return $this->_attributeTypes;
+    }
+
+    /**
+     * Gets the object classes
+     *
+     * @return array
+     */
+    public function getObjectClasses()
+    {
+        return $this->_objectClasses;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/OpenLdap.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/OpenLdap.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/OpenLdap.php	(revision 5146)
@@ -0,0 +1,502 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: OpenLdap.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema
+ */
+require_once 'Zend/Ldap/Node/Schema.php';
+/**
+ * @see Zend_Ldap_Node_Schema_AttributeType_OpenLdap
+ */
+require_once 'Zend/Ldap/Node/Schema/AttributeType/OpenLdap.php';
+/**
+ * @see Zend_Ldap_Node_Schema_ObjectClass_OpenLdap
+ */
+require_once 'Zend/Ldap/Node/Schema/ObjectClass/OpenLdap.php';
+
+/**
+ * Zend_Ldap_Node_Schema_OpenLdap provides a simple data-container for the Schema node of
+ * an OpenLDAP server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_OpenLdap extends Zend_Ldap_Node_Schema
+{
+    /**
+     * The attribute Types
+     *
+     * @var array
+     */
+    protected $_attributeTypes = null;
+    /**
+     * The object classes
+     *
+     * @var array
+     */
+    protected $_objectClasses = null;
+    /**
+     * The LDAP syntaxes
+     *
+     * @var array
+     */
+    protected $_ldapSyntaxes = null;
+    /**
+     * The matching rules
+     *
+     * @var array
+     */
+    protected $_matchingRules = null;
+    /**
+     * The matching rule use
+     *
+     * @var array
+     */
+    protected $_matchingRuleUse = null;
+
+    /**
+     * Parses the schema
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  Zend_Ldap    $ldap
+     * @return Zend_Ldap_Node_Schema Provides a fluid interface
+     */
+    protected function _parseSchema(Zend_Ldap_Dn $dn, Zend_Ldap $ldap)
+    {
+        parent::_parseSchema($dn, $ldap);
+        $this->_loadAttributeTypes();
+        $this->_loadLdapSyntaxes();
+        $this->_loadMatchingRules();
+        $this->_loadMatchingRuleUse();
+        $this->_loadObjectClasses();
+        return $this;
+    }
+
+    /**
+     * Gets the attribute Types
+     *
+     * @return array
+     */
+    public function getAttributeTypes()
+    {
+        return $this->_attributeTypes;
+    }
+
+    /**
+     * Gets the object classes
+     *
+     * @return array
+     */
+    public function getObjectClasses()
+    {
+        return $this->_objectClasses;
+    }
+
+    /**
+     * Gets the LDAP syntaxes
+     *
+     * @return array
+     */
+    public function getLdapSyntaxes()
+    {
+        return $this->_ldapSyntaxes;
+    }
+
+    /**
+     * Gets the matching rules
+     *
+     * @return array
+     */
+    public function getMatchingRules()
+    {
+        return $this->_matchingRules;
+    }
+
+    /**
+     * Gets the matching rule use
+     *
+     * @return array
+     */
+    public function getMatchingRuleUse()
+    {
+        return $this->_matchingRuleUse;
+    }
+
+    /**
+     * Loads the attribute Types
+     *
+     * @return void
+     */
+    protected function _loadAttributeTypes()
+    {
+        $this->_attributeTypes = array();
+        foreach ($this->getAttribute('attributeTypes') as $value) {
+            $val = $this->_parseAttributeType($value);
+            $val = new Zend_Ldap_Node_Schema_AttributeType_OpenLdap($val);
+            $this->_attributeTypes[$val->getName()] = $val;
+
+        }
+        foreach ($this->_attributeTypes as $val) {
+            if (count($val->sup) > 0) {
+                $this->_resolveInheritance($val, $this->_attributeTypes);
+            }
+            foreach ($val->aliases as $alias) {
+                $this->_attributeTypes[$alias] = $val;
+            }
+        }
+        ksort($this->_attributeTypes, SORT_STRING);
+    }
+
+    /**
+     * Parses an attributeType value
+     *
+     * @param  string $value
+     * @return array
+     */
+    protected function _parseAttributeType($value)
+    {
+        $attributeType = array(
+            'oid'                  => null,
+            'name'                 => null,
+            'desc'                 => null,
+            'obsolete'             => false,
+            'sup'                  => null,
+            'equality'             => null,
+            'ordering'             => null,
+            'substr'               => null,
+            'syntax'               => null,
+            'max-length'           => null,
+            'single-value'         => false,
+            'collective'           => false,
+            'no-user-modification' => false,
+            'usage'                => 'userApplications',
+            '_string'              => $value,
+            '_parents'             => array());
+
+        $tokens = $this->_tokenizeString($value);
+        $attributeType['oid'] = array_shift($tokens); // first token is the oid
+        $this->_parseLdapSchemaSyntax($attributeType, $tokens);
+
+        if (array_key_exists('syntax', $attributeType)) {
+            // get max length from syntax
+            if (preg_match('/^(.+){(\d+)}$/', $attributeType['syntax'], $matches)) {
+                $attributeType['syntax'] = $matches[1];
+                $attributeType['max-length'] = $matches[2];
+            }
+        }
+
+        $this->_ensureNameAttribute($attributeType);
+
+        return $attributeType;
+    }
+
+    /**
+     * Loads the object classes
+     *
+     * @return void
+     */
+    protected function _loadObjectClasses()
+    {
+        $this->_objectClasses = array();
+        foreach ($this->getAttribute('objectClasses') as $value) {
+            $val = $this->_parseObjectClass($value);
+            $val = new Zend_Ldap_Node_Schema_ObjectClass_OpenLdap($val);
+            $this->_objectClasses[$val->getName()] = $val;
+        }
+        foreach ($this->_objectClasses as $val) {
+            if (count($val->sup) > 0) {
+                $this->_resolveInheritance($val, $this->_objectClasses);
+            }
+            foreach ($val->aliases as $alias) {
+                $this->_objectClasses[$alias] = $val;
+            }
+        }
+        ksort($this->_objectClasses, SORT_STRING);
+    }
+
+    /**
+     * Parses an objectClasses value
+     *
+     * @param string $value
+     * @return array
+     */
+    protected function _parseObjectClass($value)
+    {
+        $objectClass = array(
+            'oid'        => null,
+            'name'       => null,
+            'desc'       => null,
+            'obsolete'   => false,
+            'sup'        => array(),
+            'abstract'   => false,
+            'structural' => false,
+            'auxiliary'  => false,
+            'must'       => array(),
+            'may'        => array(),
+            '_string'    => $value,
+            '_parents'   => array());
+
+        $tokens = $this->_tokenizeString($value);
+        $objectClass['oid'] = array_shift($tokens); // first token is the oid
+        $this->_parseLdapSchemaSyntax($objectClass, $tokens);
+
+        $this->_ensureNameAttribute($objectClass);
+
+        return $objectClass;
+    }
+
+    /**
+     * Resolves inheritance in objectClasses and attributes
+     *
+     * @param Zend_Ldap_Node_Schema_Item $node
+     * @param array                      $repository
+     */
+    protected function _resolveInheritance(Zend_Ldap_Node_Schema_Item $node, array $repository)
+    {
+        $data = $node->getData();
+        $parents = $data['sup'];
+        if ($parents === null || !is_array($parents) || count($parents) < 1) return;
+        foreach ($parents as $parent) {
+            if (!array_key_exists($parent, $repository)) continue;
+            if (!array_key_exists('_parents', $data) || !is_array($data['_parents'])) {
+               $data['_parents'] = array();
+           }
+           $data['_parents'][] = $repository[$parent];
+        }
+        $node->setData($data);
+    }
+
+    /**
+     * Loads the LDAP syntaxes
+     *
+     * @return void
+     */
+    protected function _loadLdapSyntaxes()
+    {
+        $this->_ldapSyntaxes = array();
+        foreach ($this->getAttribute('ldapSyntaxes') as $value) {
+            $val = $this->_parseLdapSyntax($value);
+            $this->_ldapSyntaxes[$val['oid']] = $val;
+        }
+        ksort($this->_ldapSyntaxes, SORT_STRING);
+    }
+
+    /**
+     * Parses an ldapSyntaxes value
+     *
+     * @param  string $value
+     * @return array
+     */
+    protected function _parseLdapSyntax($value)
+    {
+        $ldapSyntax = array(
+            'oid'      => null,
+            'desc'     => null,
+            '_string' => $value);
+
+        $tokens = $this->_tokenizeString($value);
+        $ldapSyntax['oid'] = array_shift($tokens); // first token is the oid
+        $this->_parseLdapSchemaSyntax($ldapSyntax, $tokens);
+
+        return $ldapSyntax;
+    }
+
+    /**
+     * Loads the matching rules
+     *
+     * @return void
+     */
+    protected function _loadMatchingRules()
+    {
+        $this->_matchingRules = array();
+        foreach ($this->getAttribute('matchingRules') as $value) {
+            $val = $this->_parseMatchingRule($value);
+            $this->_matchingRules[$val['name']] = $val;
+        }
+        ksort($this->_matchingRules, SORT_STRING);
+    }
+
+    /**
+     * Parses an matchingRules value
+     *
+     * @param  string $value
+     * @return array
+     */
+    protected function _parseMatchingRule($value)
+    {
+        $matchingRule = array(
+            'oid'      => null,
+            'name'     => null,
+            'desc'     => null,
+            'obsolete' => false,
+            'syntax'   => null,
+            '_string'  => $value);
+
+        $tokens = $this->_tokenizeString($value);
+        $matchingRule['oid'] = array_shift($tokens); // first token is the oid
+        $this->_parseLdapSchemaSyntax($matchingRule, $tokens);
+
+        $this->_ensureNameAttribute($matchingRule);
+
+        return $matchingRule;
+    }
+
+    /**
+     * Loads the matching rule use
+     *
+     * @return void
+     */
+    protected function _loadMatchingRuleUse()
+    {
+        $this->_matchingRuleUse = array();
+        foreach ($this->getAttribute('matchingRuleUse') as $value) {
+            $val = $this->_parseMatchingRuleUse($value);
+            $this->_matchingRuleUse[$val['name']] = $val;
+        }
+        ksort($this->_matchingRuleUse, SORT_STRING);
+    }
+
+    /**
+     * Parses an matchingRuleUse value
+     *
+     * @param  string $value
+     * @return array
+     */
+    protected function _parseMatchingRuleUse($value)
+    {
+        $matchingRuleUse = array(
+            'oid'      => null,
+            'name'     => null,
+            'desc'     => null,
+            'obsolete' => false,
+            'applies'  => array(),
+            '_string'  => $value);
+
+        $tokens = $this->_tokenizeString($value);
+        $matchingRuleUse['oid'] = array_shift($tokens); // first token is the oid
+        $this->_parseLdapSchemaSyntax($matchingRuleUse, $tokens);
+
+        $this->_ensureNameAttribute($matchingRuleUse);
+
+        return $matchingRuleUse;
+    }
+
+    /**
+     * Ensures that a name element is present and that it is single-values.
+     *
+     * @param array $data
+     */
+    protected function _ensureNameAttribute(array &$data)
+    {
+        if (!array_key_exists('name', $data) || empty($data['name'])) {
+            // force a name
+            $data['name'] = $data['oid'];
+        }
+        if (is_array($data['name'])) {
+            // make one name the default and put the other ones into aliases
+            $aliases = $data['name'];
+            $data['name'] = array_shift($aliases);
+            $data['aliases'] = $aliases;
+        } else {
+            $data['aliases'] = array();
+        }
+    }
+
+    /**
+     * Parse the given tokens into a data structure
+     *
+     * @param  array $data
+     * @param  array $tokens
+     * @return void
+     */
+    protected function _parseLdapSchemaSyntax(array &$data, array $tokens)
+    {
+        // tokens that have no value associated
+        $noValue = array('single-value',
+            'obsolete',
+            'collective',
+            'no-user-modification',
+            'abstract',
+            'structural',
+            'auxiliary');
+        // tokens that can have multiple values
+        $multiValue = array('must', 'may', 'sup');
+
+        while (count($tokens) > 0) {
+            $token = strtolower(array_shift($tokens));
+            if (in_array($token, $noValue)) {
+                $data[$token] = true; // single value token
+            } else {
+                $data[$token] = array_shift($tokens);
+                // this one follows a string or a list if it is multivalued
+                if ($data[$token] == '(') {
+                    // this creates the list of values and cycles through the tokens
+                    // until the end of the list is reached ')'
+                    $data[$token] = array();
+                    while ($tmp = array_shift($tokens)) {
+                        if ($tmp == ')') break;
+                        if ($tmp != '$') {
+                            $data[$token][] = Zend_Ldap_Attribute::convertFromLdapValue($tmp);
+                        }
+                    }
+                } else {
+                    $data[$token] = Zend_Ldap_Attribute::convertFromLdapValue($data[$token]);
+                }
+                // create a array if the value should be multivalued but was not
+                if (in_array($token, $multiValue) && !is_array($data[$token])) {
+                    $data[$token] = array($data[$token]);
+                }
+            }
+        }
+    }
+
+    /**
+    * Tokenizes the given value into an array
+    *
+    * @param  string $value
+    * @return array tokens
+    */
+    protected function _tokenizeString($value)
+    {
+        $tokens = array();
+        $matches = array();
+        // this one is taken from PEAR::Net_LDAP2
+        $pattern = "/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x";
+        preg_match_all($pattern, $value, $matches);
+        $cMatches = count($matches[0]);
+        $cPattern = count($matches);
+        for ($i = 0; $i < $cMatches; $i++) {     // number of tokens (full pattern match)
+            for ($j = 1; $j < $cPattern; $j++) { // each subpattern
+                $tok = trim($matches[$j][$i]);
+                if (!empty($tok)) {              // pattern match in this subpattern
+                    $tokens[$i] = $tok;          // this is the token
+                }
+            }
+        }
+        if ($tokens[0] == '(') array_shift($tokens);
+        if ($tokens[count($tokens) - 1] == ')') array_pop($tokens);
+        return $tokens;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/ObjectClass/ActiveDirectory.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/ObjectClass/ActiveDirectory.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/ObjectClass/ActiveDirectory.php	(revision 5146)
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: ActiveDirectory.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema_Item
+ */
+require_once 'Zend/Ldap/Node/Schema/Item.php';
+/**
+ * @see Zend_Ldap_Node_Schema_ObjectClass_Interface
+ */
+require_once 'Zend/Ldap/Node/Schema/ObjectClass/Interface.php';
+
+/**
+ * Zend_Ldap_Node_Schema_ObjectClass_ActiveDirectory provides access to the objectClass
+ * schema information on an Active Directory server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_ObjectClass_ActiveDirectory extends Zend_Ldap_Node_Schema_Item
+    implements Zend_Ldap_Node_Schema_ObjectClass_Interface
+{
+    /**
+     * Gets the objectClass name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->ldapdisplayname[0];
+    }
+
+    /**
+     * Gets the objectClass OID
+     *
+     * @return string
+     */
+    public function getOid()
+    {
+
+    }
+
+    /**
+     * Gets the attributes that this objectClass must contain
+     *
+     * @return array
+     */
+    public function getMustContain()
+    {
+
+    }
+
+    /**
+     * Gets the attributes that this objectClass may contain
+     *
+     * @return array
+     */
+    public function getMayContain()
+    {
+
+    }
+
+    /**
+     * Gets the objectClass description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+
+    }
+
+    /**
+     * Gets the objectClass type
+     *
+     * @return integer
+     */
+    public function getType()
+    {
+
+    }
+
+    /**
+     * Returns the parent objectClasses of this class.
+     * This includes structural, abstract and auxiliary objectClasses
+     *
+     * @return array
+     */
+    public function getParentClasses()
+    {
+
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/ObjectClass/Interface.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/ObjectClass/Interface.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/ObjectClass/Interface.php	(revision 5146)
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Node_Schema_ObjectClass_Interface provides a contract for schema objectClasses.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Ldap_Node_Schema_ObjectClass_Interface
+{
+    /**
+     * Gets the objectClass name
+     *
+     * @return string
+     */
+    public function getName();
+
+    /**
+     * Gets the objectClass OID
+     *
+     * @return string
+     */
+    public function getOid();
+
+    /**
+     * Gets the attributes that this objectClass must contain
+     *
+     * @return array
+     */
+    public function getMustContain();
+
+    /**
+     * Gets the attributes that this objectClass may contain
+     *
+     * @return array
+     */
+    public function getMayContain();
+
+    /**
+     * Gets the objectClass description
+     *
+     * @return string
+     */
+    public function getDescription();
+
+    /**
+     * Gets the objectClass type
+     *
+     * @return integer
+     */
+    public function getType();
+
+    /**
+     * Returns the parent objectClasses of this class.
+     * This includes structural, abstract and auxiliary objectClasses
+     *
+     * @return array
+     */
+    public function getParentClasses();
+}
Index: trunk/library/Zend/Ldap/Node/Schema/ObjectClass/OpenLdap.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/ObjectClass/OpenLdap.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/ObjectClass/OpenLdap.php	(revision 5146)
@@ -0,0 +1,175 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: OpenLdap.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema_Item
+ */
+require_once 'Zend/Ldap/Node/Schema/Item.php';
+/**
+ * @see Zend_Ldap_Node_Schema_ObjectClass_Interface
+ */
+require_once 'Zend/Ldap/Node/Schema/ObjectClass/Interface.php';
+
+/**
+ * Zend_Ldap_Node_Schema_ObjectClass_OpenLdap provides access to the objectClass
+ * schema information on an OpenLDAP server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_ObjectClass_OpenLdap extends Zend_Ldap_Node_Schema_Item
+    implements Zend_Ldap_Node_Schema_ObjectClass_Interface
+{
+    /**
+     * All inherited "MUST" attributes
+     *
+     * @var array
+     */
+    protected $_inheritedMust = null;
+    /**
+     * All inherited "MAY" attributes
+     *
+     * @var array
+     */
+    protected $_inheritedMay = null;
+
+
+    /**
+     * Gets the objectClass name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Gets the objectClass OID
+     *
+     * @return string
+     */
+    public function getOid()
+    {
+        return $this->oid;
+    }
+
+    /**
+     * Gets the attributes that this objectClass must contain
+     *
+     * @return array
+     */
+    public function getMustContain()
+    {
+        if ($this->_inheritedMust === null) {
+            $this->_resolveInheritance();
+        }
+        return $this->_inheritedMust;
+    }
+
+    /**
+     * Gets the attributes that this objectClass may contain
+     *
+     * @return array
+     */
+    public function getMayContain()
+    {
+        if ($this->_inheritedMay === null) {
+            $this->_resolveInheritance();
+        }
+        return $this->_inheritedMay;
+    }
+
+    /**
+     * Resolves the inheritance tree
+     *
+     * @return void
+     */
+    protected function _resolveInheritance()
+    {
+        $must = $this->must;
+        $may = $this->may;
+        foreach ($this->getParents() as $p) {
+            $must = array_merge($must, $p->getMustContain());
+            $may = array_merge($may, $p->getMayContain());
+        }
+        $must = array_unique($must);
+        $may = array_unique($may);
+        $may = array_diff($may, $must);
+        sort($must, SORT_STRING);
+        sort($may, SORT_STRING);
+        $this->_inheritedMust = $must;
+        $this->_inheritedMay = $may;
+    }
+
+    /**
+     * Gets the objectClass description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->desc;
+    }
+
+    /**
+     * Gets the objectClass type
+     *
+     * @return integer
+     */
+    public function getType()
+    {
+        if ($this->structural) {
+            return Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_STRUCTURAL;
+        } else if ($this->abstract) {
+            return Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_ABSTRACT;
+        } else if ($this->auxiliary) {
+            return Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_AUXILIARY;
+        } else {
+            return Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Returns the parent objectClasses of this class.
+     * This includes structural, abstract and auxiliary objectClasses
+     *
+     * @return array
+     */
+    public function getParentClasses()
+    {
+        return $this->sup;
+    }
+
+    /**
+     * Returns the parent object classes in the inhertitance tree if one exists
+     *
+     * @return array of Zend_Ldap_Node_Schema_ObjectClass_OpenLdap
+     */
+    public function getParents()
+    {
+        return $this->_parents;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Collection.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Collection.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Collection.php	(revision 5146)
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Collection.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Collection
+ */
+require_once 'Zend/Ldap/Collection.php';
+
+
+/**
+ * Zend_Ldap_Node_Collection provides a collecion of nodes.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Collection extends Zend_Ldap_Collection
+{
+    /**
+     * Creates the data structure for the given entry data
+     *
+     * @param  array $data
+     * @return Zend_Ldap_Node
+     */
+    protected function _createEntry(array $data)
+    {
+        /**
+         * @see Zend_Ldap_Node
+         */
+        require_once 'Zend/Ldap/Node.php';
+        $node = Zend_Ldap_Node::fromArray($data, true);
+        $node->attachLdap($this->_iterator->getLdap());
+        return $node;
+    }
+
+    /**
+     * Return the child key (DN).
+     * Implements Iterator and RecursiveIterator
+     *
+     * @return string
+     */
+    public function key()
+    {
+        return $this->_iterator->key();
+    }
+}
Index: trunk/library/Zend/Ldap/Node/RootDse.php
===================================================================
--- trunk/library/Zend/Ldap/Node/RootDse.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/RootDse.php	(revision 5146)
@@ -0,0 +1,158 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: RootDse.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Abstract
+ */
+require_once 'Zend/Ldap/Node/Abstract.php';
+
+/**
+ * Zend_Ldap_Node_RootDse provides a simple data-container for the RootDSE node.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_RootDse extends Zend_Ldap_Node_Abstract
+{
+    const SERVER_TYPE_GENERIC         = 1;
+    const SERVER_TYPE_OPENLDAP        = 2;
+    const SERVER_TYPE_ACTIVEDIRECTORY = 3;
+    const SERVER_TYPE_EDIRECTORY      = 4;
+
+    /**
+     * Factory method to create the RootDSE.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node_RootDse
+     * @throws Zend_Ldap_Exception
+     */
+    public static function create(Zend_Ldap $ldap)
+    {
+        $dn = Zend_Ldap_Dn::fromString('');
+        $data = $ldap->getEntry($dn, array('*', '+'), true);
+        if (isset($data['domainfunctionality'])) {
+            /**
+             * @see Zend_Ldap_Node_RootDse_ActiveDirectory
+             */
+            require_once 'Zend/Ldap/Node/RootDse/ActiveDirectory.php';
+            return new Zend_Ldap_Node_RootDse_ActiveDirectory($dn, $data);
+        } else if (isset($data['dsaname'])) {
+            /**
+             * @see Zend_Ldap_Node_RootDse_ActiveDirectory
+             */
+            require_once 'Zend/Ldap/Node/RootDse/eDirectory.php';
+            return new Zend_Ldap_Node_RootDse_eDirectory($dn, $data);
+        } else if (isset($data['structuralobjectclass']) &&
+                $data['structuralobjectclass'][0] === 'OpenLDAProotDSE') {
+            /**
+             * @see Zend_Ldap_Node_RootDse_OpenLdap
+             */
+            require_once 'Zend/Ldap/Node/RootDse/OpenLdap.php';
+            return new Zend_Ldap_Node_RootDse_OpenLdap($dn, $data);
+        } else {
+            return new self($dn, $data);
+        }
+    }
+
+    /**
+     * Constructor.
+     *
+     * Constructor is protected to enforce the use of factory methods.
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  array        $data
+     */
+    protected function __construct(Zend_Ldap_Dn $dn, array $data)
+    {
+        parent::__construct($dn, $data, true);
+    }
+
+    /**
+     * Gets the namingContexts.
+     *
+     * @return array
+     */
+    public function getNamingContexts()
+    {
+        return $this->getAttribute('namingContexts', null);
+    }
+
+    /**
+     * Gets the subschemaSubentry.
+     *
+     * @return string|null
+     */
+    public function getSubschemaSubentry()
+    {
+        return $this->getAttribute('subschemaSubentry', 0);
+    }
+
+    /**
+     * Determines if the version is supported
+     *
+     * @param  string|int|array $versions version(s) to check
+     * @return boolean
+     */
+    public function supportsVersion($versions)
+    {
+        return $this->attributeHasValue('supportedLDAPVersion', $versions);
+    }
+
+    /**
+     * Determines if the sasl mechanism is supported
+     *
+     * @param  string|array $mechlist SASL mechanisms to check
+     * @return boolean
+     */
+    public function supportsSaslMechanism($mechlist)
+    {
+        return $this->attributeHasValue('supportedSASLMechanisms', $mechlist);
+    }
+
+    /**
+     * Gets the server type
+     *
+     * @return int
+     */
+    public function getServerType()
+    {
+        return self::SERVER_TYPE_GENERIC;
+    }
+
+    /**
+     * Returns the schema DN
+     *
+     * @return Zend_Ldap_Dn
+     */
+    public function getSchemaDn()
+    {
+        $schemaDn = $this->getSubschemaSubentry();
+        /**
+         * @see Zend_Ldap_Dn
+         */
+        require_once 'Zend/Ldap/Dn.php';
+        return Zend_Ldap_Dn::fromString($schemaDn);
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema.php	(revision 5146)
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Schema.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Abstract
+ */
+require_once 'Zend/Ldap/Node/Abstract.php';
+
+/**
+ * Zend_Ldap_Node_Schema provides a simple data-container for the Schema node.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema extends Zend_Ldap_Node_Abstract
+{
+    const OBJECTCLASS_TYPE_UNKNOWN    = 0;
+    const OBJECTCLASS_TYPE_STRUCTURAL = 1;
+    const OBJECTCLASS_TYPE_ABSTRACT   = 3;
+    const OBJECTCLASS_TYPE_AUXILIARY  = 4;
+
+    /**
+     * Factory method to create the Schema node.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node_Schema
+     * @throws Zend_Ldap_Exception
+     */
+    public static function create(Zend_Ldap $ldap)
+    {
+        $dn = $ldap->getRootDse()->getSchemaDn();
+        $data = $ldap->getEntry($dn, array('*', '+'), true);
+        switch ($ldap->getRootDse()->getServerType()) {
+            case Zend_Ldap_Node_RootDse::SERVER_TYPE_ACTIVEDIRECTORY:
+                /**
+                 * @see Zend_Ldap_Node_Schema_ActiveDirectory
+                 */
+                require_once 'Zend/Ldap/Node/Schema/ActiveDirectory.php';
+                return new Zend_Ldap_Node_Schema_ActiveDirectory($dn, $data, $ldap);
+            case Zend_Ldap_Node_RootDse::SERVER_TYPE_OPENLDAP:
+                /**
+                 * @see Zend_Ldap_Node_RootDse_ActiveDirectory
+                 */
+                require_once 'Zend/Ldap/Node/Schema/OpenLdap.php';
+                return new Zend_Ldap_Node_Schema_OpenLdap($dn, $data, $ldap);
+            case Zend_Ldap_Node_RootDse::SERVER_TYPE_EDIRECTORY:
+            default:
+                return new self($dn, $data, $ldap);
+        }
+    }
+
+    /**
+     * Constructor.
+     *
+     * Constructor is protected to enforce the use of factory methods.
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  array        $data
+     * @param  Zend_Ldap    $ldap
+     */
+    protected function __construct(Zend_Ldap_Dn $dn, array $data, Zend_Ldap $ldap)
+    {
+        parent::__construct($dn, $data, true);
+        $this->_parseSchema($dn, $ldap);
+    }
+
+    /**
+     * Parses the schema
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  Zend_Ldap    $ldap
+     * @return Zend_Ldap_Node_Schema Provides a fluid interface
+     */
+    protected function _parseSchema(Zend_Ldap_Dn $dn, Zend_Ldap $ldap)
+    {
+        return $this;
+    }
+
+    /**
+     * Gets the attribute Types
+     *
+     * @return array
+     */
+    public function getAttributeTypes()
+    {
+        return array();
+    }
+
+    /**
+     * Gets the object classes
+     *
+     * @return array
+     */
+    public function getObjectClasses()
+    {
+        return array();
+    }
+}
Index: trunk/library/Zend/Ldap/Node/ChildrenIterator.php
===================================================================
--- trunk/library/Zend/Ldap/Node/ChildrenIterator.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/ChildrenIterator.php	(revision 5146)
@@ -0,0 +1,209 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: ChildrenIterator.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node
+ */
+require_once 'Zend/Ldap/Node.php';
+
+/**
+ * Zend_Ldap_Node_ChildrenIterator provides an iterator to a collection of children nodes.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_ChildrenIterator implements Iterator, Countable, RecursiveIterator, ArrayAccess
+{
+    /**
+     * An array of Zend_Ldap_Node objects
+     *
+     * @var array
+     */
+    private $_data;
+
+    /**
+     * Constructor.
+     *
+     * @param  array $data
+     * @return void
+     */
+    public function __construct(array $data)
+    {
+        $this->_data = $data;
+    }
+
+    /**
+     * Returns the number of child nodes.
+     * Implements Countable
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->_data);
+    }
+
+    /**
+     * Return the current child.
+     * Implements Iterator
+     *
+     * @return Zend_Ldap_Node
+     */
+    public function current()
+    {
+        return current($this->_data);
+    }
+
+    /**
+     * Return the child'd RDN.
+     * Implements Iterator
+     *
+     * @return string
+     */
+    public function key()
+    {
+        return key($this->_data);
+    }
+
+    /**
+     * Move forward to next child.
+     * Implements Iterator
+     */
+    public function next()
+    {
+        next($this->_data);
+    }
+
+    /**
+     * Rewind the Iterator to the first child.
+     * Implements Iterator
+     */
+    public function rewind()
+    {
+        reset($this->_data);
+    }
+
+    /**
+     * Check if there is a current child
+     * after calls to rewind() or next().
+     * Implements Iterator
+     *
+     * @return boolean
+     */
+    public function valid()
+    {
+        return (current($this->_data)!==false);
+    }
+
+    /**
+     * Checks if current node has children.
+     * Returns whether the current element has children.
+     *
+     * @return boolean
+     */
+    public function hasChildren()
+    {
+        if ($this->current() instanceof Zend_Ldap_Node) {
+            return $this->current()->hasChildren();
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the children for the current node.
+     *
+     * @return Zend_Ldap_Node_ChildrenIterator
+     */
+    public function getChildren()
+    {
+        if ($this->current() instanceof Zend_Ldap_Node) {
+            return $this->current()->getChildren();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns a child with a given RDN.
+     * Implements ArrayAccess.
+     *
+     * @param  string $rdn
+     * @return Zend_Ldap_node
+     */
+    public function offsetGet($rdn)
+    {
+        if ($this->offsetExists($rdn)) {
+            return $this->_data[$rdn];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Checks whether a given rdn exists.
+     * Implements ArrayAccess.
+     *
+     * @param  string $rdn
+     * @return boolean
+     */
+    public function offsetExists($rdn)
+    {
+        return (array_key_exists($rdn, $this->_data));
+    }
+
+    /**
+     * Does nothing.
+     * Implements ArrayAccess.
+     *
+     * @param  string $name
+     * @return null
+     */
+    public function offsetUnset($name) { }
+
+    /**
+     * Does nothing.
+     * Implements ArrayAccess.
+     *
+     * @param  string $name
+     * @param  mixed $value
+     * @return null
+     */
+    public function offsetSet($name, $value) { }
+
+    /**
+     * Get all children as an array
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        $data = array();
+        foreach ($this as $rdn => $node) {
+            $data[$rdn] = $node;
+        }
+        return $data;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Abstract.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Abstract.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Abstract.php	(revision 5146)
@@ -0,0 +1,485 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Abstract.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Attribute
+ */
+require_once 'Zend/Ldap/Attribute.php';
+/**
+ * @see Zend_Ldap_Dn
+ */
+require_once 'Zend/Ldap/Dn.php';
+
+/**
+ * Zend_Ldap_Node_Abstract provides a bas eimplementation for LDAP nodes
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Ldap_Node_Abstract implements ArrayAccess, Countable
+{
+    protected static $_systemAttributes=array('createtimestamp', 'creatorsname',
+        'entrycsn', 'entrydn', 'entryuuid', 'hassubordinates', 'modifiersname',
+        'modifytimestamp', 'structuralobjectclass', 'subschemasubentry',
+        'distinguishedname', 'instancetype', 'name', 'objectcategory', 'objectguid',
+        'usnchanged', 'usncreated', 'whenchanged', 'whencreated');
+
+    /**
+     * Holds the node's DN.
+     *
+     * @var Zend_Ldap_Dn
+     */
+    protected $_dn;
+
+    /**
+     * Holds the node's current data.
+     *
+     * @var array
+     */
+    protected $_currentData;
+
+    /**
+     * Constructor.
+     *
+     * Constructor is protected to enforce the use of factory methods.
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  array        $data
+     * @param  boolean      $fromDataSource
+     */
+    protected function __construct(Zend_Ldap_Dn $dn, array $data, $fromDataSource)
+    {
+        $this->_dn = $dn;
+        $this->_loadData($data, $fromDataSource);
+    }
+
+    /**
+     * @param  array   $data
+     * @param  boolean $fromDataSource
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _loadData(array $data, $fromDataSource)
+    {
+        if (array_key_exists('dn', $data)) {
+            unset($data['dn']);
+        }
+        ksort($data, SORT_STRING);
+        $this->_currentData = $data;
+    }
+
+    /**
+     * Reload node attributes from LDAP.
+     *
+     * This is an online method.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node_Abstract Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function reload(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $data = $ldap->getEntry($this->_getDn(), array('*', '+'), true);
+            $this->_loadData($data, true);
+        }
+        return $this;
+    }
+
+    /**
+     * Gets the DN of the current node as a Zend_Ldap_Dn.
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Dn
+     */
+    protected function _getDn()
+    {
+        return $this->_dn;
+    }
+
+    /**
+     * Gets the DN of the current node as a Zend_Ldap_Dn.
+     * The method returns a clone of the node's DN to prohibit modification.
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Dn
+     */
+    public function getDn()
+    {
+        $dn = clone $this->_getDn();
+        return $dn;
+    }
+
+    /**
+     * Gets the DN of the current node as a string.
+     *
+     * This is an offline method.
+     *
+     * @param  string $caseFold
+     * @return string
+     */
+    public function getDnString($caseFold = null)
+    {
+        return $this->_getDn()->toString($caseFold);
+    }
+
+    /**
+     * Gets the DN of the current node as an array.
+     *
+     * This is an offline method.
+     *
+     * @param  string $caseFold
+     * @return array
+     */
+    public function getDnArray($caseFold = null)
+    {
+        return $this->_getDn()->toArray($caseFold);
+    }
+
+    /**
+     * Gets the RDN of the current node as a string.
+     *
+     * This is an offline method.
+     *
+     * @param  string $caseFold
+     * @return string
+     */
+    public function getRdnString($caseFold = null)
+    {
+        return $this->_getDn()->getRdnString($caseFold);
+    }
+
+    /**
+     * Gets the RDN of the current node as an array.
+     *
+     * This is an offline method.
+     *
+     * @param  string $caseFold
+     * @return array
+     */
+    public function getRdnArray($caseFold = null)
+    {
+        return $this->_getDn()->getRdn($caseFold);
+    }
+
+    /**
+     * Gets the objectClass of the node
+     *
+     * @return array
+     */
+    public function getObjectClass()
+    {
+        return $this->getAttribute('objectClass', null);
+    }
+
+    /**
+     * Gets all attributes of node.
+     *
+     * The collection contains all attributes.
+     *
+     * This is an offline method.
+     *
+     * @param  boolean $includeSystemAttributes
+     * @return array
+     */
+    public function getAttributes($includeSystemAttributes = true)
+    {
+        $data = array();
+        foreach ($this->getData($includeSystemAttributes) as $name => $value) {
+            $data[$name] = $this->getAttribute($name, null);
+        }
+        return $data;
+    }
+
+    /**
+     * Returns the DN of the current node. {@see getDnString()}
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return $this->getDnString();
+    }
+
+    /**
+     * Cast to string representation {@see toString()}
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->toString();
+    }
+
+    /**
+     * Returns an array representation of the current node
+     *
+     * @param  boolean $includeSystemAttributes
+     * @return array
+     */
+    public function toArray($includeSystemAttributes = true)
+    {
+        $attributes = $this->getAttributes($includeSystemAttributes);
+        return array_merge(array('dn' => $this->getDnString()), $attributes);
+    }
+
+    /**
+     * Returns a JSON representation of the current node
+     *
+     * @param  boolean $includeSystemAttributes
+     * @return string
+     */
+    public function toJson($includeSystemAttributes = true)
+    {
+        return json_encode($this->toArray($includeSystemAttributes));
+    }
+
+    /**
+     * Gets node attributes.
+     *
+     * The array contains all attributes in its internal format (no conversion).
+     *
+     * This is an offline method.
+     *
+     * @param  boolean $includeSystemAttributes
+     * @return array
+     */
+    public function getData($includeSystemAttributes = true)
+    {
+        if ($includeSystemAttributes === false) {
+            $data = array();
+            foreach ($this->_currentData as $key => $value) {
+                if (!in_array($key, self::$_systemAttributes)) {
+                    $data[$key] = $value;
+                }
+            }
+            return $data;
+        } else {
+            return $this->_currentData;
+        }
+    }
+
+    /**
+     * Checks whether a given attribute exists.
+     *
+     * If $emptyExists is false empty attributes (containing only array()) are
+     * treated as non-existent returning false.
+     * If $emptyExists is true empty attributes are treated as existent returning
+     * true. In this case method returns false only if the attribute name is
+     * missing in the key-collection.
+     *
+     * @param  string  $name
+     * @param  boolean $emptyExists
+     * @return boolean
+     */
+    public function existsAttribute($name, $emptyExists = false)
+    {
+        $name = strtolower($name);
+        if (isset($this->_currentData[$name])) {
+            if ($emptyExists) return true;
+            return count($this->_currentData[$name])>0;
+        }
+        else return false;
+    }
+
+    /**
+     * Checks if the given value(s) exist in the attribute
+     *
+     * @param  string      $attribName
+     * @param  mixed|array $value
+     * @return boolean
+     */
+    public function attributeHasValue($attribName, $value)
+    {
+        return Zend_Ldap_Attribute::attributeHasValue($this->_currentData, $attribName, $value);
+    }
+
+    /**
+     * Gets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string  $name
+     * @param  integer $index
+     * @return mixed
+     * @throws Zend_Ldap_Exception
+     */
+    public function getAttribute($name, $index = null)
+    {
+        if ($name == 'dn') {
+            return $this->getDnString();
+        }
+        else {
+            return Zend_Ldap_Attribute::getAttribute($this->_currentData, $name, $index);
+        }
+    }
+
+    /**
+     * Gets a LDAP date/time attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string  $name
+     * @param  integer $index
+     * @return array|integer
+     * @throws Zend_Ldap_Exception
+     */
+    public function getDateTimeAttribute($name, $index = null)
+    {
+        return Zend_Ldap_Attribute::getDateTimeAttribute($this->_currentData, $name, $index);
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function __set($name, $value)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Gets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return array
+     * @throws Zend_Ldap_Exception
+     */
+    public function __get($name)
+    {
+        return $this->getAttribute($name, null);
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function __unset($name)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Checks whether a given attribute exists.
+     *
+     * Empty attributes will be treated as non-existent.
+     *
+     * @param  string $name
+     * @return boolean
+     */
+    public function __isset($name)
+    {
+        return $this->existsAttribute($name, false);
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function offsetSet($name, $value)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Gets a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return array
+     * @throws Zend_Ldap_Exception
+     */
+    public function offsetGet($name)
+    {
+        return $this->getAttribute($name, null);
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function offsetUnset($name)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Checks whether a given attribute exists.
+     * Implements ArrayAccess.
+     *
+     * Empty attributes will be treated as non-existent.
+     *
+     * @param  string $name
+     * @return boolean
+     */
+    public function offsetExists($name)
+    {
+        return $this->existsAttribute($name, false);
+    }
+
+    /**
+     * Returns the number of attributes in node.
+     * Implements Countable
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->_currentData);
+    }
+}
Index: trunk/library/Zend/Ldap/Converter/Exception.php
===================================================================
--- trunk/library/Zend/Ldap/Converter/Exception.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Converter/Exception.php	(revision 5146)
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Converter_Exception extends Zend_Exception
+{
+}
Index: trunk/library/Zend/Ldap/Attribute.php
===================================================================
--- trunk/library/Zend/Ldap/Attribute.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Attribute.php	(revision 5146)
@@ -0,0 +1,417 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Attribute.php 23486 2010-12-10 04:05:30Z mjh_ca $
+ */
+
+/**
+ * @see Zend_Ldap_Converter
+ */
+require_once 'Zend/Ldap/Converter.php';
+
+/**
+ * Zend_Ldap_Attribute is a collection of LDAP attribute related functions.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Attribute
+{
+    const PASSWORD_HASH_MD5   = 'md5';
+    const PASSWORD_HASH_SMD5  = 'smd5';
+    const PASSWORD_HASH_SHA   = 'sha';
+    const PASSWORD_HASH_SSHA  = 'ssha';
+    const PASSWORD_UNICODEPWD = 'unicodePwd';
+
+    /**
+     * Sets a LDAP attribute.
+     *
+     * @param  array                    $data
+     * @param  string                   $attribName
+     * @param  scalar|array|Traversable $value
+     * @param  boolean                  $append
+     * @return void
+     */
+    public static function setAttribute(array &$data, $attribName, $value, $append = false)
+    {
+        $attribName = strtolower($attribName);
+        $valArray = array();
+        if (is_array($value) || ($value instanceof Traversable))
+        {
+            foreach ($value as $v)
+            {
+                $v = self::_valueToLdap($v);
+                if ($v !== null) $valArray[] = $v;
+            }
+        }
+        else if ($value !== null)
+        {
+            $value = self::_valueToLdap($value);
+            if ($value !== null) $valArray[] = $value;
+        }
+
+        if ($append === true && isset($data[$attribName]))
+        {
+            if (is_string($data[$attribName])) $data[$attribName] = array($data[$attribName]);
+            $data[$attribName] = array_merge($data[$attribName], $valArray);
+        }
+        else
+        {
+            $data[$attribName] = $valArray;
+        }
+    }
+
+    /**
+     * Gets a LDAP attribute.
+     *
+     * @param  array   $data
+     * @param  string  $attribName
+     * @param  integer $index
+     * @return array|mixed
+     */
+    public static function getAttribute(array $data, $attribName, $index = null)
+    {
+        $attribName = strtolower($attribName);
+        if ($index === null) {
+            if (!isset($data[$attribName])) return array();
+            $retArray = array();
+            foreach ($data[$attribName] as $v)
+            {
+                $retArray[] = self::_valueFromLdap($v);
+            }
+            return $retArray;
+        } else if (is_int($index)) {
+            if (!isset($data[$attribName])) {
+                return null;
+            } else if ($index >= 0 && $index<count($data[$attribName])) {
+                return self::_valueFromLdap($data[$attribName][$index]);
+            } else {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Checks if the given value(s) exist in the attribute
+     *
+     * @param array       $data
+     * @param string      $attribName
+     * @param mixed|array $value
+     * @return boolean
+     */
+    public static function attributeHasValue(array &$data, $attribName, $value)
+    {
+        $attribName = strtolower($attribName);
+        if (!isset($data[$attribName])) return false;
+
+        if (is_scalar($value)) {
+            $value = array($value);
+        }
+
+        foreach ($value as $v) {
+            $v = self::_valueToLdap($v);
+            if (!in_array($v, $data[$attribName], true)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Removes duplicate values from a LDAP attribute
+     *
+     * @param array  $data
+     * @param string $attribName
+     * @return void
+     */
+    public static function removeDuplicatesFromAttribute(array &$data, $attribName)
+    {
+        $attribName = strtolower($attribName);
+        if (!isset($data[$attribName])) return;
+        $data[$attribName] = array_values(array_unique($data[$attribName]));
+    }
+
+    /**
+     * Remove given values from a LDAP attribute
+     *
+     * @param array       $data
+     * @param string      $attribName
+     * @param mixed|array $value
+     * @return void
+     */
+    public static function removeFromAttribute(array &$data, $attribName, $value)
+    {
+        $attribName = strtolower($attribName);
+        if (!isset($data[$attribName])) return;
+
+        if (is_scalar($value)) {
+            $value = array($value);
+        }
+
+        $valArray = array();
+        foreach ($value as $v)
+        {
+            $v = self::_valueToLdap($v);
+            if ($v !== null) $valArray[] = $v;
+        }
+
+        $resultArray = $data[$attribName];
+        foreach ($valArray as $rv) {
+            $keys = array_keys($resultArray, $rv);
+            foreach ($keys as $k) {
+                unset($resultArray[$k]);
+            }
+        }
+        $resultArray = array_values($resultArray);
+        $data[$attribName] = $resultArray;
+    }
+
+    /**
+     * @param  mixed $value
+     * @return string|null
+     */
+    private static function _valueToLdap($value)
+    {
+        return Zend_Ldap_Converter::toLdap($value);
+    }
+
+    /**
+     * @param  string $value
+     * @return mixed
+     */
+    private static function _valueFromLdap($value)
+    {
+        try {
+            $return = Zend_Ldap_Converter::fromLdap($value, Zend_Ldap_Converter::STANDARD, false);
+            if ($return instanceof DateTime) {
+                return Zend_Ldap_Converter::toLdapDateTime($return, false);
+            } else {
+                return $return;
+            }
+        } catch (InvalidArgumentException $e) {
+            return $value;
+        }
+    }
+
+    /**
+     * Converts a PHP data type into its LDAP representation
+     *
+     * @deprected    use Zend_Ldap_Converter instead
+     * @param          mixed $value
+     * @return         string|null - null if the PHP data type cannot be converted.
+     */
+    public static function convertToLdapValue($value)
+    {
+        return self::_valueToLdap($value);
+    }
+
+    /**
+     * Converts an LDAP value into its PHP data type
+     *
+     * @deprected    use Zend_Ldap_Converter instead
+     * @param          string $value
+     * @return         mixed
+     */
+    public static function convertFromLdapValue($value)
+    {
+        return self::_valueFromLdap($value);
+    }
+
+    /**
+     * Converts a timestamp into its LDAP date/time representation
+     *
+     * @param  integer $value
+     * @param  boolean $utc
+     * @return string|null - null if the value cannot be converted.
+     */
+    public static function convertToLdapDateTimeValue($value, $utc = false)
+    {
+        return self::_valueToLdapDateTime($value, $utc);
+    }
+
+    /**
+     * Converts LDAP date/time representation into a timestamp
+     *
+     * @param  string $value
+     * @return integer|null - null if the value cannot be converted.
+     */
+    public static function convertFromLdapDateTimeValue($value)
+    {
+        return self::_valueFromLdapDateTime($value);
+    }
+
+    /**
+     * Sets a LDAP password.
+     *
+     * @param  array       $data
+     * @param  string      $password
+     * @param  string      $hashType
+     * @param  string|null $attribName
+     * @return null
+     */
+    public static function setPassword(array &$data, $password, $hashType = self::PASSWORD_HASH_MD5,
+        $attribName = null)
+    {
+        if ($attribName === null) {
+            if ($hashType === self::PASSWORD_UNICODEPWD) {
+                $attribName = 'unicodePwd';
+            } else {
+                $attribName = 'userPassword';
+            }
+        }
+
+        $hash = self::createPassword($password, $hashType);
+        self::setAttribute($data, $attribName, $hash, false);
+    }
+
+    /**
+     * Creates a LDAP password.
+     *
+     * @param  string $password
+     * @param  string $hashType
+     * @return string
+     */
+    public static function createPassword($password, $hashType = self::PASSWORD_HASH_MD5)
+    {
+        switch ($hashType) {
+            case self::PASSWORD_UNICODEPWD:
+                /* see:
+                 * http://msdn.microsoft.com/en-us/library/cc223248(PROT.10).aspx
+                 */
+                $password = '"' . $password . '"';
+                if (function_exists('mb_convert_encoding')) {
+                    $password = mb_convert_encoding($password, 'UTF-16LE', 'UTF-8');
+                } else if (function_exists('iconv')) {
+                    $password = iconv('UTF-8', 'UTF-16LE', $password);
+                } else {
+                    $len = strlen($password);
+                    $new = '';
+                    for($i=0; $i < $len; $i++) {
+                        $new .= $password[$i] . "\x00";
+                    }
+                    $password = $new;
+                }
+                return $password;
+            case self::PASSWORD_HASH_SSHA:
+                $salt    = substr(sha1(uniqid(mt_rand(), true), true), 0, 4);
+                $rawHash = sha1($password . $salt, true) . $salt;
+                $method  = '{SSHA}';
+                break;
+            case self::PASSWORD_HASH_SHA:
+                $rawHash = sha1($password, true);
+                $method  = '{SHA}';
+                break;
+            case self::PASSWORD_HASH_SMD5:
+                $salt    = substr(sha1(uniqid(mt_rand(), true), true), 0, 4);
+                $rawHash = md5($password . $salt, true) . $salt;
+                $method  = '{SMD5}';
+                break;
+            case self::PASSWORD_HASH_MD5:
+            default:
+                $rawHash = md5($password, true);
+                $method  = '{MD5}';
+                break;
+        }
+        return $method . base64_encode($rawHash);
+    }
+
+    /**
+     * Sets a LDAP date/time attribute.
+     *
+     * @param  array                     $data
+     * @param  string                    $attribName
+     * @param  integer|array|Traversable $value
+     * @param  boolean                   $utc
+     * @param  boolean                   $append
+     * @return null
+     */
+    public static function setDateTimeAttribute(array &$data, $attribName, $value, $utc = false,
+        $append = false)
+    {
+        $convertedValues = array();
+        if (is_array($value) || ($value instanceof Traversable))
+        {
+            foreach ($value as $v) {
+                $v = self::_valueToLdapDateTime($v, $utc);
+                if ($v !== null) $convertedValues[] = $v;
+            }
+        }
+        else if ($value !== null) {
+            $value = self::_valueToLdapDateTime($value, $utc);
+            if ($value !== null) $convertedValues[] = $value;
+        }
+        self::setAttribute($data, $attribName, $convertedValues, $append);
+    }
+
+    /**
+     * @param  integer $value
+     * @param  boolean $utc
+     * @return string|null
+     */
+    private static function _valueToLdapDateTime($value, $utc)
+    {
+        if (is_int($value)) {
+            return Zend_Ldap_Converter::toLdapDateTime($value, $utc);
+        }
+        else return null;
+    }
+
+    /**
+     * Gets a LDAP date/time attribute.
+     *
+     * @param  array   $data
+     * @param  string  $attribName
+     * @param  integer $index
+     * @return array|integer
+     */
+    public static function getDateTimeAttribute(array $data, $attribName, $index = null)
+    {
+        $values = self::getAttribute($data, $attribName, $index);
+        if (is_array($values)) {
+            for ($i = 0; $i<count($values); $i++) {
+                $newVal = self::_valueFromLdapDateTime($values[$i]);
+                if ($newVal !== null) $values[$i] = $newVal;
+            }
+        }
+        else {
+            $newVal = self::_valueFromLdapDateTime($values);
+            if ($newVal !== null) $values = $newVal;
+        }
+        return $values;
+    }
+
+    /**
+     * @param  string|DateTime $value
+     * @return integer|null
+     */
+    private static function _valueFromLdapDateTime($value)
+    {
+        if ($value instanceof DateTime) {
+            return $value->format('U');
+        } else if (is_string($value)) {
+            try {
+                return Zend_Ldap_Converter::fromLdapDateTime($value, false)->format('U');
+            } catch (InvalidArgumentException $e) {
+                return null;
+            }
+        } else return null;
+    }
+}
Index: trunk/library/Zend/Ldap/Collection/Iterator/Default.php
===================================================================
--- trunk/library/Zend/Ldap/Collection/Iterator/Default.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Collection/Iterator/Default.php	(revision 5146)
@@ -0,0 +1,310 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Default.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Collection_Iterator_Default is the default collection iterator implementation
+ * using ext/ldap
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Collection_Iterator_Default implements Iterator, Countable
+{
+    const ATTRIBUTE_TO_LOWER  = 1;
+    const ATTRIBUTE_TO_UPPER  = 2;
+    const ATTRIBUTE_NATIVE    = 3;
+
+    /**
+     * LDAP Connection
+     *
+     * @var Zend_Ldap
+     */
+    protected $_ldap = null;
+
+    /**
+     * Result identifier resource
+     *
+     * @var resource
+     */
+    protected $_resultId = null;
+
+    /**
+     * Current result entry identifier
+     *
+     * @var resource
+     */
+    protected $_current = null;
+
+    /**
+     * Number of items in query result
+     *
+     * @var integer
+     */
+    protected $_itemCount = -1;
+
+    /**
+     * The method that will be applied to the attribute's names.
+     *
+     * @var  integer|callback
+     */
+    protected $_attributeNameTreatment = self::ATTRIBUTE_TO_LOWER;
+
+    /**
+     * Constructor.
+     *
+     * @param  Zend_Ldap $ldap
+     * @param  resource  $resultId
+     * @return void
+     */
+    public function __construct(Zend_Ldap $ldap, $resultId)
+    {
+        $this->_ldap = $ldap;
+        $this->_resultId = $resultId;
+        $this->_itemCount = @ldap_count_entries($ldap->getResource(), $resultId);
+        if ($this->_itemCount === false) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception($this->_ldap, 'counting entries');
+        }
+    }
+
+    public function __destruct()
+    {
+        $this->close();
+    }
+
+    /**
+     * Closes the current result set
+     *
+     * @return bool
+     */
+    public function close()
+    {
+        $isClosed = false;
+        if (is_resource($this->_resultId)) {
+             $isClosed = @ldap_free_result($this->_resultId);
+             $this->_resultId = null;
+             $this->_current = null;
+        }
+        return $isClosed;
+    }
+
+    /**
+     * Gets the current LDAP connection.
+     *
+     * @return Zend_Ldap
+     */
+    public function getLdap()
+    {
+        return $this->_ldap;
+    }
+
+    /**
+     * Sets the attribute name treatment.
+     *
+     * Can either be one of the following constants
+     * - Zend_Ldap_Collection_Iterator_Default::ATTRIBUTE_TO_LOWER
+     * - Zend_Ldap_Collection_Iterator_Default::ATTRIBUTE_TO_UPPER
+     * - Zend_Ldap_Collection_Iterator_Default::ATTRIBUTE_NATIVE
+     * or a valid callback accepting the attribute's name as it's only
+     * argument and returning the new attribute's name.
+     *
+     * @param  integer|callback $attributeNameTreatment
+     * @return Zend_Ldap_Collection_Iterator_Default Provides a fluent interface
+     */
+    public function setAttributeNameTreatment($attributeNameTreatment)
+    {
+        if (is_callable($attributeNameTreatment)) {
+            if (is_string($attributeNameTreatment) && !function_exists($attributeNameTreatment)) {
+                $this->_attributeNameTreatment = self::ATTRIBUTE_TO_LOWER;
+            } else if (is_array($attributeNameTreatment) &&
+                    !method_exists($attributeNameTreatment[0], $attributeNameTreatment[1])) {
+                $this->_attributeNameTreatment = self::ATTRIBUTE_TO_LOWER;
+            } else {
+                $this->_attributeNameTreatment = $attributeNameTreatment;
+            }
+        } else {
+            $attributeNameTreatment = (int)$attributeNameTreatment;
+            switch ($attributeNameTreatment) {
+                case self::ATTRIBUTE_TO_LOWER:
+                case self::ATTRIBUTE_TO_UPPER:
+                case self::ATTRIBUTE_NATIVE:
+                    $this->_attributeNameTreatment = $attributeNameTreatment;
+                    break;
+                default:
+                    $this->_attributeNameTreatment = self::ATTRIBUTE_TO_LOWER;
+                    break;
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Returns the currently set attribute name treatment
+     *
+     * @return integer|callback
+     */
+    public function getAttributeNameTreatment()
+    {
+        return $this->_attributeNameTreatment;
+    }
+
+    /**
+     * Returns the number of items in current result
+     * Implements Countable
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return $this->_itemCount;
+    }
+
+    /**
+     * Return the current result item
+     * Implements Iterator
+     *
+     * @return array|null
+     * @throws Zend_Ldap_Exception
+     */
+    public function current()
+    {
+        if (!is_resource($this->_current)) {
+            $this->rewind();
+        }
+        if (!is_resource($this->_current)) {
+            return null;
+        }
+
+        $entry = array('dn' => $this->key());
+        $ber_identifier = null;
+        $name = @ldap_first_attribute($this->_ldap->getResource(), $this->_current,
+            $ber_identifier);
+        while ($name) {
+            $data = @ldap_get_values_len($this->_ldap->getResource(), $this->_current, $name);
+            unset($data['count']);
+
+            switch($this->_attributeNameTreatment) {
+                case self::ATTRIBUTE_TO_LOWER:
+                    $attrName = strtolower($name);
+                    break;
+                case self::ATTRIBUTE_TO_UPPER:
+                    $attrName = strtoupper($name);
+                    break;
+                case self::ATTRIBUTE_NATIVE:
+                    $attrName = $name;
+                    break;
+                default:
+                    $attrName = call_user_func($this->_attributeNameTreatment, $name);
+                    break;
+            }
+            $entry[$attrName] = $data;
+            $name = @ldap_next_attribute($this->_ldap->getResource(), $this->_current,
+                $ber_identifier);
+        }
+        ksort($entry, SORT_LOCALE_STRING);
+        return $entry;
+    }
+
+    /**
+     * Return the result item key
+     * Implements Iterator
+     *
+     * @return string|null
+     */
+    public function key()
+    {
+        if (!is_resource($this->_current)) {
+            $this->rewind();
+        }
+        if (is_resource($this->_current)) {
+            $currentDn = @ldap_get_dn($this->_ldap->getResource(), $this->_current);
+            if ($currentDn === false) {
+                /** @see Zend_Ldap_Exception */
+                require_once 'Zend/Ldap/Exception.php';
+                throw new Zend_Ldap_Exception($this->_ldap, 'getting dn');
+            }
+            return $currentDn;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Move forward to next result item
+     * Implements Iterator
+     *
+     * @throws Zend_Ldap_Exception
+     */
+    public function next()
+    {
+        if (is_resource($this->_current)) {
+            $this->_current = @ldap_next_entry($this->_ldap->getResource(), $this->_current);
+            /** @see Zend_Ldap_Exception */
+            require_once 'Zend/Ldap/Exception.php';
+            if ($this->_current === false) {
+                $msg = $this->_ldap->getLastError($code);
+                if ($code === Zend_Ldap_Exception::LDAP_SIZELIMIT_EXCEEDED) {
+                    // we have reached the size limit enforced by the server
+                    return;
+                } else if ($code > Zend_Ldap_Exception::LDAP_SUCCESS) {
+                     throw new Zend_Ldap_Exception($this->_ldap, 'getting next entry (' . $msg . ')');
+                }
+            }
+        }
+    }
+
+    /**
+     * Rewind the Iterator to the first result item
+     * Implements Iterator
+     *
+     * @throws Zend_Ldap_Exception
+     */
+    public function rewind()
+    {
+        if (is_resource($this->_resultId)) {
+            $this->_current = @ldap_first_entry($this->_ldap->getResource(), $this->_resultId);
+            /** @see Zend_Ldap_Exception */
+            require_once 'Zend/Ldap/Exception.php';
+            if ($this->_current === false &&
+                    $this->_ldap->getLastErrorCode() > Zend_Ldap_Exception::LDAP_SUCCESS) {
+                throw new Zend_Ldap_Exception($this->_ldap, 'getting first entry');
+            }
+        }
+    }
+
+    /**
+     * Check if there is a current result item
+     * after calls to rewind() or next()
+     * Implements Iterator
+     *
+     * @return boolean
+     */
+    public function valid()
+    {
+        return (is_resource($this->_current));
+    }
+
+}
Index: trunk/library/Zend/Ldap/Filter.php
===================================================================
--- trunk/library/Zend/Ldap/Filter.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter.php	(revision 5146)
@@ -0,0 +1,265 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Filter.php 22662 2010-07-24 17:37:36Z mabe $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_String
+ */
+require_once 'Zend/Ldap/Filter/String.php';
+
+/**
+ * Zend_Ldap_Filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter extends Zend_Ldap_Filter_String
+{
+    const TYPE_EQUALS         = '=';
+    const TYPE_GREATER        = '>';
+    const TYPE_GREATEROREQUAL = '>=';
+    const TYPE_LESS           = '<';
+    const TYPE_LESSOREQUAL    = '<=';
+    const TYPE_APPROX         = '~=';
+
+    /**
+     * Creates an 'equals' filter.
+     * (attr=value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function equals($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_EQUALS, null, null);
+    }
+
+    /**
+     * Creates a 'begins with' filter.
+     * (attr=value*)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function begins($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_EQUALS, null, '*');
+    }
+
+    /**
+     * Creates an 'ends with' filter.
+     * (attr=*value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function ends($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_EQUALS, '*', null);
+    }
+
+    /**
+     * Creates a 'contains' filter.
+     * (attr=*value*)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function contains($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_EQUALS, '*', '*');
+    }
+
+    /**
+     * Creates a 'greater' filter.
+     * (attr>value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function greater($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_GREATER, null, null);
+    }
+
+    /**
+     * Creates a 'greater or equal' filter.
+     * (attr>=value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function greaterOrEqual($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_GREATEROREQUAL, null, null);
+    }
+
+    /**
+     * Creates a 'less' filter.
+     * (attr<value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function less($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_LESS, null, null);
+    }
+
+    /**
+     * Creates an 'less or equal' filter.
+     * (attr<=value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function lessOrEqual($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_LESSOREQUAL, null, null);
+    }
+
+    /**
+     * Creates an 'approx' filter.
+     * (attr~=value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function approx($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_APPROX, null, null);
+    }
+
+    /**
+     * Creates an 'any' filter.
+     * (attr=*)
+     *
+     * @param  string $attr
+     * @return Zend_Ldap_Filter
+     */
+    public static function any($attr)
+    {
+        return new self($attr, '', self::TYPE_EQUALS, '*', null);
+    }
+
+    /**
+     * Creates a simple custom string filter.
+     *
+     * @param  string $filter
+     * @return Zend_Ldap_Filter_String
+     */
+    public static function string($filter)
+    {
+        return new Zend_Ldap_Filter_String($filter);
+    }
+
+    /**
+     * Creates a simple string filter to be used with a mask.
+     *
+     * @param string $mask
+     * @param string $value
+     * @return Zend_Ldap_Filter_Mask
+     */
+    public static function mask($mask, $value)
+    {
+        /**
+         * Zend_Ldap_Filter_Mask
+         */
+        require_once 'Zend/Ldap/Filter/Mask.php';
+        return new Zend_Ldap_Filter_Mask($mask, $value);
+    }
+
+    /**
+     * Creates an 'and' filter.
+     *
+     * @param  Zend_Ldap_Filter_Abstract $filter,...
+     * @return Zend_Ldap_Filter_And
+     */
+    public static function andFilter($filter)
+    {
+        /**
+         * Zend_Ldap_Filter_And
+         */
+        require_once 'Zend/Ldap/Filter/And.php';
+        return new Zend_Ldap_Filter_And(func_get_args());
+    }
+
+    /**
+     * Creates an 'or' filter.
+     *
+     * @param  Zend_Ldap_Filter_Abstract $filter,...
+     * @return Zend_Ldap_Filter_Or
+     */
+    public static function orFilter($filter)
+    {
+        /**
+         * Zend_Ldap_Filter_Or
+         */
+        require_once 'Zend/Ldap/Filter/Or.php';
+        return new Zend_Ldap_Filter_Or(func_get_args());
+    }
+
+    /**
+     * Create a filter string.
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @param  string $filtertype
+     * @param  string $prepend
+     * @param  string $append
+     * @return string
+     */
+    private static function _createFilterString($attr, $value, $filtertype, $prepend = null, $append = null)
+    {
+        $str = $attr . $filtertype;
+        if ($prepend !== null) $str .= $prepend;
+        $str .= self::escapeValue($value);
+        if ($append !== null) $str .= $append;
+        return $str;
+    }
+
+    /**
+     * Creates a new Zend_Ldap_Filter.
+     *
+     * @param string $attr
+     * @param string $value
+     * @param string $filtertype
+     * @param string $prepend
+     * @param string $append
+     */
+    public function __construct($attr, $value, $filtertype, $prepend = null, $append = null)
+    {
+        $filter = self::_createFilterString($attr, $value, $filtertype, $prepend, $append);
+        parent::__construct($filter);
+    }
+}
Index: trunk/library/Zend/Ldap/Node.php
===================================================================
--- trunk/library/Zend/Ldap/Node.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node.php	(revision 5146)
@@ -0,0 +1,1101 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Node.php 22662 2010-07-24 17:37:36Z mabe $
+ */
+
+/**
+ * @see Zend_Ldap
+ */
+require_once 'Zend/Ldap.php';
+/**
+ * @see Zend_Ldap_Node_Abstract
+ */
+require_once 'Zend/Ldap/Node/Abstract.php';
+
+/**
+ * Zend_Ldap_Node provides an object oriented view into a LDAP node.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node extends Zend_Ldap_Node_Abstract implements Iterator, RecursiveIterator
+{
+    /**
+     * Holds the node's new DN if node is renamed.
+     *
+     * @var Zend_Ldap_Dn
+     */
+    protected $_newDn;
+    /**
+     * Holds the node's orginal attributes (as loaded).
+     *
+     * @var array
+     */
+    protected $_originalData;
+    /**
+     * This node will be added
+     *
+     * @var boolean
+     */
+    protected $_new;
+    /**
+     * This node will be deleted
+     *
+     * @var boolean
+     */
+    protected $_delete;
+    /**
+     * Holds the connection to the LDAP server if in connected mode.
+     *
+     * @var Zend_Ldap
+     */
+    protected $_ldap;
+
+    /**
+     * Holds an array of the current node's children.
+     *
+     * @var array
+     */
+    protected $_children;
+
+    /**
+     * Controls iteration status
+     *
+     * @var boolean
+     */
+    private $_iteratorRewind = false;
+
+    /**
+     * Constructor.
+     *
+     * Constructor is protected to enforce the use of factory methods.
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  array        $data
+     * @param  boolean      $fromDataSource
+     * @param  Zend_Ldap    $ldap
+     * @throws Zend_Ldap_Exception
+     */
+    protected function __construct(Zend_Ldap_Dn $dn, array $data, $fromDataSource, Zend_Ldap $ldap = null)
+    {
+        parent::__construct($dn, $data, $fromDataSource);
+        if ($ldap !== null) $this->attachLdap($ldap);
+        else $this->detachLdap();
+    }
+
+    /**
+     * Serialization callback
+     *
+     * Only DN and attributes will be serialized.
+     *
+     * @return array
+     */
+    public function __sleep()
+    {
+        return array('_dn', '_currentData', '_newDn', '_originalData',
+            '_new', '_delete', '_children');
+    }
+
+    /**
+     * Deserialization callback
+     *
+     * Enforces a detached node.
+     *
+     * @return null
+     */
+    public function __wakeup()
+    {
+        $this->detachLdap();
+    }
+
+    /**
+     * Gets the current LDAP connection.
+     *
+     * @return Zend_Ldap
+     * @throws Zend_Ldap_Exception
+     */
+    public function getLdap()
+    {
+        if ($this->_ldap === null) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'No LDAP connection specified.', Zend_Ldap_Exception::LDAP_OTHER);
+        }
+        else return $this->_ldap;
+    }
+
+    /**
+     * Attach node to an LDAP connection
+     *
+     * This is an offline method.
+     *
+     * @uses   Zend_Ldap_Dn::isChildOf()
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function attachLdap(Zend_Ldap $ldap)
+    {
+        if (!Zend_Ldap_Dn::isChildOf($this->_getDn(), $ldap->getBaseDn())) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'LDAP connection is not responsible for given node.',
+                Zend_Ldap_Exception::LDAP_OTHER);
+        }
+
+        if ($ldap !== $this->_ldap) {
+            $this->_ldap = $ldap;
+            if (is_array($this->_children)) {
+                foreach ($this->_children as $child) {
+                    $child->attachLdap($ldap);
+                }
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Detach node from LDAP connection
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function detachLdap()
+    {
+        $this->_ldap = null;
+        if (is_array($this->_children)) {
+            foreach ($this->_children as $child) {
+                $child->detachLdap();
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Checks if the current node is attached to a LDAP server.
+     *
+     * This is an offline method.
+     *
+     * @return boolean
+     */
+    public function isAttached()
+    {
+        return ($this->_ldap !== null);
+    }
+
+    /**
+     * @param  array   $data
+     * @param  boolean $fromDataSource
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _loadData(array $data, $fromDataSource)
+    {
+        parent::_loadData($data, $fromDataSource);
+        if ($fromDataSource === true) {
+            $this->_originalData = $data;
+        } else {
+            $this->_originalData = array();
+        }
+        $this->_children = null;
+        $this->_markAsNew(($fromDataSource === true) ? false : true);
+        $this->_markAsToBeDeleted(false);
+    }
+
+    /**
+     * Factory method to create a new detached Zend_Ldap_Node for a given DN.
+     *
+     * @param  string|array|Zend_Ldap_Dn $dn
+     * @param  array                     $objectClass
+     * @return Zend_Ldap_Node
+     * @throws Zend_Ldap_Exception
+     */
+    public static function create($dn, array $objectClass = array())
+    {
+        if (is_string($dn) || is_array($dn)) {
+            $dn = Zend_Ldap_Dn::factory($dn);
+        } else if ($dn instanceof Zend_Ldap_Dn) {
+            $dn = clone $dn;
+        } else {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
+        }
+        $new = new self($dn, array(), false, null);
+        $new->_ensureRdnAttributeValues();
+        $new->setAttribute('objectClass', $objectClass);
+        return $new;
+    }
+
+    /**
+     * Factory method to create an attached Zend_Ldap_Node for a given DN.
+     *
+     * @param  string|array|Zend_Ldap_Dn $dn
+     * @param  Zend_Ldap                 $ldap
+     * @return Zend_Ldap_Node|null
+     * @throws Zend_Ldap_Exception
+     */
+    public static function fromLdap($dn, Zend_Ldap $ldap)
+    {
+        if (is_string($dn) || is_array($dn)) {
+            $dn = Zend_Ldap_Dn::factory($dn);
+        } else if ($dn instanceof Zend_Ldap_Dn) {
+            $dn = clone $dn;
+        } else {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
+        }
+        $data = $ldap->getEntry($dn, array('*', '+'), true);
+        if ($data === null) {
+            return null;
+        }
+        $entry = new self($dn, $data, true, $ldap);
+        return $entry;
+    }
+
+    /**
+     * Factory method to create a detached Zend_Ldap_Node from array data.
+     *
+     * @param  array   $data
+     * @param  boolean $fromDataSource
+     * @return Zend_Ldap_Node
+     * @throws Zend_Ldap_Exception
+     */
+    public static function fromArray(array $data, $fromDataSource = false)
+    {
+        if (!array_key_exists('dn', $data)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '\'dn\' key is missing in array.');
+        }
+        if (is_string($data['dn']) || is_array($data['dn'])) {
+            $dn = Zend_Ldap_Dn::factory($data['dn']);
+        } else if ($data['dn'] instanceof Zend_Ldap_Dn) {
+            $dn = clone $data['dn'];
+        } else {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '\'dn\' key is of a wrong data type.');
+        }
+        $fromDataSource = ($fromDataSource === true) ? true : false;
+        $new = new self($dn, $data, $fromDataSource, null);
+        $new->_ensureRdnAttributeValues();
+        return $new;
+    }
+
+    /**
+     * Ensures that teh RDN attributes are correctly set.
+     *
+     * @return void
+     */
+    protected function _ensureRdnAttributeValues()
+    {
+        foreach ($this->getRdnArray() as $key => $value) {
+            Zend_Ldap_Attribute::setAttribute($this->_currentData, $key, $value, false);
+        }
+    }
+
+    /**
+     * Marks this node as new.
+     *
+     * Node will be added (instead of updated) on calling update() if $new is true.
+     *
+     * @param boolean $new
+     */
+    protected function _markAsNew($new)
+    {
+        $this->_new = ($new === false) ? false : true;
+    }
+
+    /**
+     * Tells if the node is consiedered as new (not present on the server)
+     *
+     * Please note, that this doesn't tell you if the node is present on the server.
+     * Use {@link exits()} to see if a node is already there.
+     *
+     * @return boolean
+     */
+    public function isNew()
+    {
+        return $this->_new;
+    }
+
+    /**
+     * Marks this node as to be deleted.
+     *
+     * Node will be deleted on calling update() if $delete is true.
+     *
+     * @param boolean $delete
+     */
+    protected function _markAsToBeDeleted($delete)
+    {
+        $this->_delete = ($delete === true) ? true : false;
+    }
+
+
+    /**
+    * Is this node going to be deleted once update() is called?
+    *
+    * @return boolean
+    */
+    public function willBeDeleted()
+    {
+        return $this->_delete;
+    }
+
+    /**
+     * Marks this node as to be deleted
+     *
+     * Node will be deleted on calling update() if $delete is true.
+     *
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function delete()
+    {
+        $this->_markAsToBeDeleted(true);
+        return $this;
+    }
+
+    /**
+    * Is this node going to be moved once update() is called?
+    *
+    * @return boolean
+    */
+    public function willBeMoved()
+    {
+        if ($this->isNew() || $this->willBeDeleted()) {
+            return false;
+        } else if ($this->_newDn !== null) {
+            return ($this->_dn != $this->_newDn);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Sends all pending changes to the LDAP server
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function update(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        if (!($ldap instanceof Zend_Ldap)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'No LDAP connection available');
+        }
+
+        if ($this->willBeDeleted()) {
+            if ($ldap->exists($this->_dn)) {
+                $ldap->delete($this->_dn);
+            }
+            return $this;
+        }
+
+        if ($this->isNew()) {
+            $data = $this->getData();
+            $ldap->add($this->_getDn(), $data);
+            $this->_loadData($data, true);
+            return $this;
+        }
+
+        $changedData = $this->getChangedData();
+        if ($this->willBeMoved()) {
+            $recursive = $this->hasChildren();
+            $ldap->rename($this->_dn, $this->_newDn, $recursive, false);
+            foreach ($this->_newDn->getRdn() as $key => $value) {
+                if (array_key_exists($key, $changedData)) {
+                    unset($changedData[$key]);
+                }
+            }
+            $this->_dn = $this->_newDn;
+            $this->_newDn = null;
+        }
+        if (count($changedData) > 0) {
+            $ldap->update($this->_getDn(), $changedData);
+        }
+        $this->_originalData = $this->_currentData;
+        return $this;
+    }
+
+    /**
+     * Gets the DN of the current node as a Zend_Ldap_Dn.
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Dn
+     */
+    protected function _getDn()
+    {
+        return ($this->_newDn === null) ? parent::_getDn() : $this->_newDn;
+    }
+
+    /**
+     * Gets the current DN of the current node as a Zend_Ldap_Dn.
+     * The method returns a clone of the node's DN to prohibit modification.
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Dn
+     */
+    public function getCurrentDn()
+    {
+        $dn = clone parent::_getDn();
+        return $dn;
+    }
+
+    /**
+     * Sets the new DN for this node
+     *
+     * This is an offline method.
+     *
+     * @param  Zend_Ldap_Dn|string|array $newDn
+     * @throws Zend_Ldap_Exception
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function setDn($newDn)
+    {
+        if ($newDn instanceof Zend_Ldap_Dn) {
+            $this->_newDn = clone $newDn;
+        } else {
+            $this->_newDn = Zend_Ldap_Dn::factory($newDn);
+        }
+        $this->_ensureRdnAttributeValues();
+        return $this;
+    }
+
+    /**
+     * {@see setDn()}
+     *
+     * This is an offline method.
+     *
+     * @param  Zend_Ldap_Dn|string|array $newDn
+     * @throws Zend_Ldap_Exception
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function move($newDn)
+    {
+        return $this->setDn($newDn);
+    }
+
+    /**
+     * {@see setDn()}
+     *
+     * This is an offline method.
+     *
+     * @param  Zend_Ldap_Dn|string|array $newDn
+     * @throws Zend_Ldap_Exception
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function rename($newDn)
+    {
+        return $this->setDn($newDn);
+    }
+
+    /**
+     * Sets the objectClass.
+     *
+     * This is an offline method.
+     *
+     * @param  array|string $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setObjectClass($value)
+    {
+        $this->setAttribute('objectClass', $value);
+        return $this;
+    }
+
+    /**
+     * Appends to the objectClass.
+     *
+     * This is an offline method.
+     *
+     * @param  array|string $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function appendObjectClass($value)
+    {
+        $this->appendToAttribute('objectClass', $value);
+        return $this;
+    }
+
+    /**
+     * Returns a LDIF representation of the current node
+     *
+     * @param  array $options Additional options used during encoding
+     * @return string
+     */
+    public function toLdif(array $options = array())
+    {
+        $attributes = array_merge(array('dn' => $this->getDnString()), $this->getData(false));
+        /**
+         * Zend_Ldap_Ldif_Encoder
+         */
+        require_once 'Zend/Ldap/Ldif/Encoder.php';
+        return Zend_Ldap_Ldif_Encoder::encode($attributes, $options);
+    }
+
+    /**
+     * Gets changed node data.
+     *
+     * The array contains all changed attributes.
+     * This format can be used in {@link Zend_Ldap::add()} and {@link Zend_Ldap::update()}.
+     *
+     * This is an offline method.
+     *
+     * @return array
+     */
+    public function getChangedData()
+    {
+        $changed = array();
+        foreach ($this->_currentData as $key => $value) {
+            if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
+                $changed[$key] = $value;
+            } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
+                $changed[$key] = $value;
+            }
+        }
+        return $changed;
+    }
+
+    /**
+     * Returns all changes made.
+     *
+     * This is an offline method.
+     *
+     * @return array
+     */
+    public function getChanges()
+    {
+        $changes = array(
+            'add'     => array(),
+            'delete'  => array(),
+            'replace' => array());
+        foreach ($this->_currentData as $key => $value) {
+            if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
+                $changes['add'][$key] = $value;
+            } else if (count($this->_originalData[$key]) === 0 && !empty($value)) {
+                $changes['add'][$key] = $value;
+            } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
+                if (empty($value)) {
+                    $changes['delete'][$key] = $value;
+                } else {
+                    $changes['replace'][$key] = $value;
+                }
+            }
+        }
+        return $changes;
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setAttribute($name, $value)
+    {
+        $this->_setAttribute($name, $value, false);
+        return $this;
+    }
+
+    /**
+     * Appends to a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function appendToAttribute($name, $value)
+    {
+        $this->_setAttribute($name, $value, true);
+        return $this;
+    }
+
+    /**
+     * Checks if the attribute can be set and sets it accordingly.
+     *
+     * @param  string  $name
+     * @param  mixed   $value
+     * @param  boolean $append
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _setAttribute($name, $value, $append)
+    {
+        $this->_assertChangeableAttribute($name);
+        Zend_Ldap_Attribute::setAttribute($this->_currentData, $name, $value, $append);
+    }
+
+    /**
+     * Sets a LDAP date/time attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string        $name
+     * @param  integer|array $value
+     * @param  boolean       $utc
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setDateTimeAttribute($name, $value, $utc = false)
+    {
+        $this->_setDateTimeAttribute($name, $value, $utc, false);
+        return $this;
+    }
+
+    /**
+     * Appends to a LDAP date/time attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string        $name
+     * @param  integer|array $value
+     * @param  boolean       $utc
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function appendToDateTimeAttribute($name, $value, $utc = false)
+    {
+        $this->_setDateTimeAttribute($name, $value, $utc, true);
+        return $this;
+    }
+
+    /**
+     * Checks if the attribute can be set and sets it accordingly.
+     *
+     * @param  string        $name
+     * @param  integer|array $value
+     * @param  boolean       $utc
+     * @param  boolean       $append
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _setDateTimeAttribute($name, $value, $utc, $append)
+    {
+        $this->_assertChangeableAttribute($name);
+        Zend_Ldap_Attribute::setDateTimeAttribute($this->_currentData, $name, $value, $utc, $append);
+    }
+
+    /**
+     * Sets a LDAP password.
+     *
+     * @param  string $password
+     * @param  string $hashType
+     * @param  string $attribName
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setPasswordAttribute($password, $hashType = Zend_Ldap_Attribute::PASSWORD_HASH_MD5,
+        $attribName = 'userPassword')
+    {
+        $this->_assertChangeableAttribute($attribName);
+        Zend_Ldap_Attribute::setPassword($this->_currentData, $password, $hashType, $attribName);
+        return $this;
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function deleteAttribute($name)
+    {
+        if ($this->existsAttribute($name, true)) {
+            $this->_setAttribute($name, null, false);
+        }
+        return $this;
+    }
+
+    /**
+     * Removes duplicate values from a LDAP attribute
+     *
+     * @param  string $attribName
+     * @return void
+     */
+    public function removeDuplicatesFromAttribute($attribName)
+    {
+        Zend_Ldap_Attribute::removeDuplicatesFromAttribute($this->_currentData, $attribName);
+    }
+
+    /**
+     * Remove given values from a LDAP attribute
+     *
+     * @param  string      $attribName
+     * @param  mixed|array $value
+     * @return void
+     */
+    public function removeFromAttribute($attribName, $value)
+    {
+        Zend_Ldap_Attribute::removeFromAttribute($this->_currentData, $attribName, $value);
+    }
+
+    /**
+     * @param  string $name
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _assertChangeableAttribute($name)
+    {
+        $name = strtolower($name);
+        $rdn = $this->getRdnArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
+        if ($name == 'dn') {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'DN cannot be changed.');
+        }
+        else if (array_key_exists($name, $rdn)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s part of the RDN');
+        } else if (in_array($name, self::$_systemAttributes)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s read-only');
+        }
+        else return true;
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function __set($name, $value)
+    {
+        $this->setAttribute($name, $value);
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function __unset($name)
+    {
+        $this->deleteAttribute($name);
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function offsetSet($name, $value)
+    {
+        $this->setAttribute($name, $value);
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function offsetUnset($name)
+    {
+        $this->deleteAttribute($name);
+    }
+
+    /**
+     * Check if node exists on LDAP.
+     *
+     * This is an online method.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    public function exists(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        return $ldap->exists($this->_getDn());
+    }
+
+    /**
+     * Reload node attributes from LDAP.
+     *
+     * This is an online method.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function reload(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        parent::reload($ldap);
+        return $this;
+    }
+
+    /**
+     * Search current subtree with given options.
+     *
+     * This is an online method.
+     *
+     * @param  string|Zend_Ldap_Filter_Abstract $filter
+     * @param  integer                          $scope
+     * @param  string                           $sort
+     * @return Zend_Ldap_Node_Collection
+     * @throws Zend_Ldap_Exception
+     */
+    public function searchSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB, $sort = null)
+    {
+        /**
+         * @see Zend_Ldap_Node_Collection
+         */
+        require_once 'Zend/Ldap/Node/Collection.php';
+        return $this->getLdap()->search($filter, $this->_getDn(), $scope, array('*', '+'), $sort,
+            'Zend_Ldap_Node_Collection');
+    }
+
+    /**
+     * Count items in current subtree found by given filter.
+     *
+     * This is an online method.
+     *
+     * @param  string|Zend_Ldap_Filter_Abstract $filter
+     * @param  integer                          $scope
+     * @return integer
+     * @throws Zend_Ldap_Exception
+     */
+    public function countSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB)
+    {
+        return $this->getLdap()->count($filter, $this->_getDn(), $scope);
+    }
+
+    /**
+     * Count children of current node.
+     *
+     * This is an online method.
+     *
+     * @return integer
+     * @throws Zend_Ldap_Exception
+     */
+    public function countChildren()
+    {
+        return $this->countSubtree('(objectClass=*)', Zend_Ldap::SEARCH_SCOPE_ONE);
+    }
+
+    /**
+     * Gets children of current node.
+     *
+     * This is an online method.
+     *
+     * @param  string|Zend_Ldap_Filter_Abstract $filter
+     * @param  string                           $sort
+     * @return Zend_Ldap_Node_Collection
+     * @throws Zend_Ldap_Exception
+     */
+    public function searchChildren($filter, $sort = null)
+    {
+        return $this->searchSubtree($filter, Zend_Ldap::SEARCH_SCOPE_ONE, $sort);
+    }
+
+    /**
+     * Checks if current node has children.
+     * Returns whether the current element has children.
+     *
+     * Can be used offline but returns false if children have not been retrieved yet.
+     *
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    public function hasChildren()
+    {
+        if (!is_array($this->_children)) {
+            if ($this->isAttached()) {
+                return ($this->countChildren() > 0);
+            } else {
+                return false;
+            }
+        } else {
+            return (count($this->_children) > 0);
+        }
+    }
+
+    /**
+     * Returns the children for the current node.
+     *
+     * Can be used offline but returns an empty array if children have not been retrieved yet.
+     *
+     * @return Zend_Ldap_Node_ChildrenIterator
+     * @throws Zend_Ldap_Exception
+     */
+    public function getChildren()
+    {
+        if (!is_array($this->_children)) {
+            $this->_children = array();
+            if ($this->isAttached()) {
+                $children = $this->searchChildren('(objectClass=*)', null);
+                foreach ($children as $child) {
+                    $this->_children[$child->getRdnString(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER)] = $child;
+                }
+            }
+        }
+        /**
+         * @see Zend_Ldap_Node_ChildrenIterator
+         */
+        require_once 'Zend/Ldap/Node/ChildrenIterator.php';
+        return new Zend_Ldap_Node_ChildrenIterator($this->_children);
+    }
+
+    /**
+     * Returns the parent of the current node.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node
+     * @throws Zend_Ldap_Exception
+     */
+    public function getParent(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        $parentDn = $this->_getDn()->getParentDn(1);
+        return self::fromLdap($parentDn, $ldap);
+    }
+
+    /**
+     * Return the current attribute.
+     * Implements Iterator
+     *
+     * @return array
+     */
+    public function current()
+    {
+        return $this;
+    }
+
+    /**
+     * Return the attribute name.
+     * Implements Iterator
+     *
+     * @return string
+     */
+    public function key()
+    {
+        return $this->getRdnString();
+    }
+
+    /**
+     * Move forward to next attribute.
+     * Implements Iterator
+     */
+    public function next()
+    {
+        $this->_iteratorRewind = false;
+    }
+
+    /**
+     * Rewind the Iterator to the first attribute.
+     * Implements Iterator
+     */
+    public function rewind()
+    {
+        $this->_iteratorRewind = true;
+    }
+
+    /**
+     * Check if there is a current attribute
+     * after calls to rewind() or next().
+     * Implements Iterator
+     *
+     * @return boolean
+     */
+    public function valid()
+    {
+        return $this->_iteratorRewind;
+    }
+}
Index: trunk/library/Zend/Mail/Protocol/Smtp/Auth/Plain.php
===================================================================
--- trunk/library/Zend/Mail/Protocol/Smtp/Auth/Plain.php	(revision 5146)
+++ trunk/library/Zend/Mail/Protocol/Smtp/Auth/Plain.php	(revision 5146)
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Plain.php 22653 2010-07-22 18:41:39Z mabe $
+ */
+
+
+/**
+ * @see Zend_Mail_Protocol_Smtp
+ */
+require_once 'Zend/Mail/Protocol/Smtp.php';
+
+
+/**
+ * Performs PLAIN authentication
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Protocol_Smtp_Auth_Plain extends Zend_Mail_Protocol_Smtp
+{
+    /**
+     * PLAIN username
+     *
+     * @var string
+     */
+    protected $_username;
+
+
+    /**
+     * PLAIN password
+     *
+     * @var string
+     */
+    protected $_password;
+
+
+    /**
+     * Constructor.
+     *
+     * @param  string $host   (Default: 127.0.0.1)
+     * @param  int    $port   (Default: null)
+     * @param  array  $config Auth-specific parameters
+     * @return void
+     */
+    public function __construct($host = '127.0.0.1', $port = null, $config = null)
+    {
+        if (is_array($config)) {
+            if (isset($config['username'])) {
+                $this->_username = $config['username'];
+            }
+            if (isset($config['password'])) {
+                $this->_password = $config['password'];
+            }
+        }
+
+        parent::__construct($host, $port, $config);
+    }
+
+
+    /**
+     * Perform PLAIN authentication with supplied credentials
+     *
+     * @return void
+     */
+    public function auth()
+    {
+        // Ensure AUTH has not already been initiated.
+        parent::auth();
+
+        $this->_send('AUTH PLAIN');
+        $this->_expect(334);
+        $this->_send(base64_encode("\0" . $this->_username . "\0" . $this->_password));
+        $this->_expect(235);
+        $this->_auth = true;
+    }
+}
Index: trunk/library/Zend/Mail/Protocol/Smtp/Auth/Crammd5.php
===================================================================
--- trunk/library/Zend/Mail/Protocol/Smtp/Auth/Crammd5.php	(revision 5146)
+++ trunk/library/Zend/Mail/Protocol/Smtp/Auth/Crammd5.php	(revision 5146)
@@ -0,0 +1,108 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Crammd5.php 22653 2010-07-22 18:41:39Z mabe $
+ */
+
+
+/**
+ * @see Zend_Mail_Protocol_Smtp
+ */
+require_once 'Zend/Mail/Protocol/Smtp.php';
+
+
+/**
+ * Performs CRAM-MD5 authentication
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Protocol_Smtp_Auth_Crammd5 extends Zend_Mail_Protocol_Smtp
+{
+    /**
+     * Constructor.
+     *
+     * @param  string $host   (Default: 127.0.0.1)
+     * @param  int    $port   (Default: null)
+     * @param  array  $config Auth-specific parameters
+     * @return void
+     */
+    public function __construct($host = '127.0.0.1', $port = null, $config = null)
+    {
+        if (is_array($config)) {
+            if (isset($config['username'])) {
+                $this->_username = $config['username'];
+            }
+            if (isset($config['password'])) {
+                $this->_password = $config['password'];
+            }
+        }
+
+        parent::__construct($host, $port, $config);
+    }
+
+
+    /**
+     * @todo Perform CRAM-MD5 authentication with supplied credentials
+     *
+     * @return void
+     */
+    public function auth()
+    {
+        // Ensure AUTH has not already been initiated.
+        parent::auth();
+
+        $this->_send('AUTH CRAM-MD5');
+        $challenge = $this->_expect(334);
+        $challenge = base64_decode($challenge);
+        $digest = $this->_hmacMd5($this->_password, $challenge);
+        $this->_send(base64_encode($this->_username . ' ' . $digest));
+        $this->_expect(235);
+        $this->_auth = true;
+    }
+
+
+    /**
+     * Prepare CRAM-MD5 response to server's ticket
+     *
+     * @param  string $key   Challenge key (usually password)
+     * @param  string $data  Challenge data
+     * @param  string $block Length of blocks
+     * @return string
+     */
+    protected function _hmacMd5($key, $data, $block = 64)
+    {
+        if (strlen($key) > 64) {
+            $key = pack('H32', md5($key));
+        } elseif (strlen($key) < 64) {
+            $key = str_pad($key, $block, "\0");
+        }
+
+        $k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64);
+        $k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64);
+
+        $inner = pack('H32', md5($k_ipad . $data));
+        $digest = md5($k_opad . $inner);
+
+        return $digest;
+    }
+}
Index: trunk/library/Zend/Mail/Protocol/Smtp/Auth/Login.php
===================================================================
--- trunk/library/Zend/Mail/Protocol/Smtp/Auth/Login.php	(revision 5146)
+++ trunk/library/Zend/Mail/Protocol/Smtp/Auth/Login.php	(revision 5146)
@@ -0,0 +1,98 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Login.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mail_Protocol_Smtp
+ */
+require_once 'Zend/Mail/Protocol/Smtp.php';
+
+
+/**
+ * Performs LOGIN authentication
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Protocol_Smtp_Auth_Login extends Zend_Mail_Protocol_Smtp
+{
+    /**
+     * LOGIN username
+     *
+     * @var string
+     */
+    protected $_username;
+
+
+    /**
+     * LOGIN password
+     *
+     * @var string
+     */
+    protected $_password;
+
+
+    /**
+     * Constructor.
+     *
+     * @param  string $host   (Default: 127.0.0.1)
+     * @param  int    $port   (Default: null)
+     * @param  array  $config Auth-specific parameters
+     * @return void
+     */
+    public function __construct($host = '127.0.0.1', $port = null, $config = null)
+    {
+        if (is_array($config)) {
+            if (isset($config['username'])) {
+                $this->_username = $config['username'];
+            }
+            if (isset($config['password'])) {
+                $this->_password = $config['password'];
+            }
+        }
+
+        parent::__construct($host, $port, $config);
+    }
+
+
+    /**
+     * Perform LOGIN authentication with supplied credentials
+     *
+     * @return void
+     */
+    public function auth()
+    {
+        // Ensure AUTH has not already been initiated.
+        parent::auth();
+
+        $this->_send('AUTH LOGIN');
+        $this->_expect(334);
+        $this->_send(base64_encode($this->_username));
+        $this->_expect(334);
+        $this->_send(base64_encode($this->_password));
+        $this->_expect(235);
+        $this->_auth = true;
+    }
+}
Index: trunk/library/Zend/Mail/Protocol/Exception.php
===================================================================
--- trunk/library/Zend/Mail/Protocol/Exception.php	(revision 5146)
+++ trunk/library/Zend/Mail/Protocol/Exception.php	(revision 5146)
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Mail/Exception.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Protocol_Exception extends Zend_Mail_Exception
+{}
+
Index: trunk/library/Zend/Mail/Protocol/Pop3.php
===================================================================
--- trunk/library/Zend/Mail/Protocol/Pop3.php	(revision 5146)
+++ trunk/library/Zend/Mail/Protocol/Pop3.php	(revision 5146)
@@ -0,0 +1,472 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Pop3.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Protocol_Pop3
+{
+    /**
+     * Default timeout in seconds for initiating session
+     */
+    const TIMEOUT_CONNECTION = 30;
+
+    /**
+     * saves if server supports top
+     * @var null|bool
+     */
+    public $hasTop = null;
+
+    /**
+     * socket to pop3
+     * @var null|resource
+     */
+    protected $_socket;
+
+    /**
+     * greeting timestamp for apop
+     * @var null|string
+     */
+    protected $_timestamp;
+
+
+    /**
+     * Public constructor
+     *
+     * @param  string      $host  hostname or IP address of POP3 server, if given connect() is called
+     * @param  int|null    $port  port of POP3 server, null for default (110 or 995 for ssl)
+     * @param  bool|string $ssl   use ssl? 'SSL', 'TLS' or false
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function __construct($host = '', $port = null, $ssl = false)
+    {
+        if ($host) {
+            $this->connect($host, $port, $ssl);
+        }
+    }
+
+
+    /**
+     * Public destructor
+     */
+    public function __destruct()
+    {
+        $this->logout();
+    }
+
+
+    /**
+     * Open connection to POP3 server
+     *
+     * @param  string      $host  hostname or IP address of POP3 server
+     * @param  int|null    $port  of POP3 server, default is 110 (995 for ssl)
+     * @param  string|bool $ssl   use 'SSL', 'TLS' or false
+     * @return string welcome message
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function connect($host, $port = null, $ssl = false)
+    {
+        if ($ssl == 'SSL') {
+            $host = 'ssl://' . $host;
+        }
+
+        if ($port === null) {
+            $port = $ssl == 'SSL' ? 995 : 110;
+        }
+
+        $errno  =  0;
+        $errstr = '';
+        $this->_socket = @fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
+        if (!$this->_socket) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('cannot connect to host; error = ' . $errstr .
+                                                   ' (errno = ' . $errno . ' )');
+        }
+
+        $welcome = $this->readResponse();
+
+        strtok($welcome, '<');
+        $this->_timestamp = strtok('>');
+        if (!strpos($this->_timestamp, '@')) {
+            $this->_timestamp = null;
+        } else {
+            $this->_timestamp = '<' . $this->_timestamp . '>';
+        }
+
+        if ($ssl === 'TLS') {
+            $this->request('STLS');
+            $result = stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
+            if (!$result) {
+                /**
+                 * @see Zend_Mail_Protocol_Exception
+                 */
+                require_once 'Zend/Mail/Protocol/Exception.php';
+                throw new Zend_Mail_Protocol_Exception('cannot enable TLS');
+            }
+        }
+
+        return $welcome;
+    }
+
+
+    /**
+     * Send a request
+     *
+     * @param string $request your request without newline
+     * @return null
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function sendRequest($request)
+    {
+        $result = @fputs($this->_socket, $request . "\r\n");
+        if (!$result) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('send failed - connection closed?');
+        }
+    }
+
+
+    /**
+     * read a response
+     *
+     * @param  boolean $multiline response has multiple lines and should be read until "<nl>.<nl>"
+     * @return string response
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function readResponse($multiline = false)
+    {
+        $result = @fgets($this->_socket);
+        if (!is_string($result)) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('read failed - connection closed?');
+        }
+
+        $result = trim($result);
+        if (strpos($result, ' ')) {
+            list($status, $message) = explode(' ', $result, 2);
+        } else {
+            $status = $result;
+            $message = '';
+        }
+
+        if ($status != '+OK') {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('last request failed');
+        }
+
+        if ($multiline) {
+            $message = '';
+            $line = fgets($this->_socket);
+            while ($line && rtrim($line, "\r\n") != '.') {
+                if ($line[0] == '.') {
+                    $line = substr($line, 1);
+                }
+                $message .= $line;
+                $line = fgets($this->_socket);
+            };
+        }
+
+        return $message;
+    }
+
+
+    /**
+     * Send request and get resposne
+     *
+     * @see sendRequest(), readResponse()
+     *
+     * @param  string $request    request
+     * @param  bool   $multiline  multiline response?
+     * @return string             result from readResponse()
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function request($request, $multiline = false)
+    {
+        $this->sendRequest($request);
+        return $this->readResponse($multiline);
+    }
+
+
+    /**
+     * End communication with POP3 server (also closes socket)
+     *
+     * @return null
+     */
+    public function logout()
+    {
+        if (!$this->_socket) {
+            return;
+        }
+
+        try {
+            $this->request('QUIT');
+        } catch (Zend_Mail_Protocol_Exception $e) {
+            // ignore error - we're closing the socket anyway
+        }
+
+        fclose($this->_socket);
+        $this->_socket = null;
+    }
+
+
+    /**
+     * Get capabilities from POP3 server
+     *
+     * @return array list of capabilities
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function capa()
+    {
+        $result = $this->request('CAPA', true);
+        return explode("\n", $result);
+    }
+
+
+    /**
+     * Login to POP3 server. Can use APOP
+     *
+     * @param  string $user      username
+     * @param  string $password  password
+     * @param  bool   $try_apop  should APOP be tried?
+     * @return void
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function login($user, $password, $tryApop = true)
+    {
+        if ($tryApop && $this->_timestamp) {
+            try {
+                $this->request("APOP $user " . md5($this->_timestamp . $password));
+                return;
+            } catch (Zend_Mail_Protocol_Exception $e) {
+                // ignore
+            }
+        }
+
+        $result = $this->request("USER $user");
+        $result = $this->request("PASS $password");
+    }
+
+
+    /**
+     * Make STAT call for message count and size sum
+     *
+     * @param  int $messages  out parameter with count of messages
+     * @param  int $octets    out parameter with size in octects of messages
+     * @return void
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function status(&$messages, &$octets)
+    {
+        $messages = 0;
+        $octets = 0;
+        $result = $this->request('STAT');
+
+        list($messages, $octets) = explode(' ', $result);
+    }
+
+
+    /**
+     * Make LIST call for size of message(s)
+     *
+     * @param  int|null $msgno number of message, null for all
+     * @return int|array size of given message or list with array(num => size)
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function getList($msgno = null)
+    {
+        if ($msgno !== null) {
+            $result = $this->request("LIST $msgno");
+
+            list(, $result) = explode(' ', $result);
+            return (int)$result;
+        }
+
+        $result = $this->request('LIST', true);
+        $messages = array();
+        $line = strtok($result, "\n");
+        while ($line) {
+            list($no, $size) = explode(' ', trim($line));
+            $messages[(int)$no] = (int)$size;
+            $line = strtok("\n");
+        }
+
+        return $messages;
+    }
+
+
+    /**
+     * Make UIDL call for getting a uniqueid
+     *
+     * @param  int|null $msgno number of message, null for all
+     * @return string|array uniqueid of message or list with array(num => uniqueid)
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function uniqueid($msgno = null)
+    {
+        if ($msgno !== null) {
+            $result = $this->request("UIDL $msgno");
+
+            list(, $result) = explode(' ', $result);
+            return $result;
+        }
+
+        $result = $this->request('UIDL', true);
+
+        $result = explode("\n", $result);
+        $messages = array();
+        foreach ($result as $line) {
+            if (!$line) {
+                continue;
+            }
+            list($no, $id) = explode(' ', trim($line), 2);
+            $messages[(int)$no] = $id;
+        }
+
+        return $messages;
+
+    }
+
+
+    /**
+     * Make TOP call for getting headers and maybe some body lines
+     * This method also sets hasTop - before it it's not known if top is supported
+     *
+     * The fallback makes normale RETR call, which retrieves the whole message. Additional
+     * lines are not removed.
+     *
+     * @param  int  $msgno    number of message
+     * @param  int  $lines    number of wanted body lines (empty line is inserted after header lines)
+     * @param  bool $fallback fallback with full retrieve if top is not supported
+     * @return string message headers with wanted body lines
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function top($msgno, $lines = 0, $fallback = false)
+    {
+        if ($this->hasTop === false) {
+            if ($fallback) {
+                return $this->retrieve($msgno);
+            } else {
+                /**
+                 * @see Zend_Mail_Protocol_Exception
+                 */
+                require_once 'Zend/Mail/Protocol/Exception.php';
+                throw new Zend_Mail_Protocol_Exception('top not supported and no fallback wanted');
+            }
+        }
+        $this->hasTop = true;
+
+        $lines = (!$lines || $lines < 1) ? 0 : (int)$lines;
+
+        try {
+            $result = $this->request("TOP $msgno $lines", true);
+        } catch (Zend_Mail_Protocol_Exception $e) {
+            $this->hasTop = false;
+            if ($fallback) {
+                $result = $this->retrieve($msgno);
+            } else {
+                throw $e;
+            }
+        }
+
+        return $result;
+    }
+
+
+    /**
+     * Make a RETR call for retrieving a full message with headers and body
+     *
+     * @deprecated since 1.1.0; this method has a typo - please use retrieve()
+     * @param  int $msgno  message number
+     * @return string message
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function retrive($msgno)
+    {
+        return $this->retrieve($msgno);
+    }
+
+
+    /**
+     * Make a RETR call for retrieving a full message with headers and body
+     *
+     * @param  int $msgno  message number
+     * @return string message
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function retrieve($msgno)
+    {
+        $result = $this->request("RETR $msgno", true);
+        return $result;
+    }
+
+    /**
+     * Make a NOOP call, maybe needed for keeping the server happy
+     *
+     * @return null
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function noop()
+    {
+        $this->request('NOOP');
+    }
+
+
+    /**
+     * Make a DELE count to remove a message
+     *
+     * @return null
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function delete($msgno)
+    {
+        $this->request("DELE $msgno");
+    }
+
+
+    /**
+     * Make RSET call, which rollbacks delete requests
+     *
+     * @return null
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function undelete()
+    {
+        $this->request('RSET');
+    }
+}
Index: trunk/library/Zend/Mail/Protocol/Abstract.php
===================================================================
--- trunk/library/Zend/Mail/Protocol/Abstract.php	(revision 5146)
+++ trunk/library/Zend/Mail/Protocol/Abstract.php	(revision 5146)
@@ -0,0 +1,447 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Abstract.php 23484 2010-12-10 03:57:59Z mjh_ca $
+ */
+
+
+/**
+ * @see Zend_Validate
+ */
+require_once 'Zend/Validate.php';
+
+
+/**
+ * @see Zend_Validate_Hostname
+ */
+require_once 'Zend/Validate/Hostname.php';
+
+
+/**
+ * Zend_Mail_Protocol_Abstract
+ *
+ * Provides low-level methods for concrete adapters to communicate with a remote mail server and track requests and responses.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Abstract.php 23484 2010-12-10 03:57:59Z mjh_ca $
+ * @todo Implement proxy settings
+ */
+abstract class Zend_Mail_Protocol_Abstract
+{
+    /**
+     * Mail default EOL string
+     */
+    const EOL = "\r\n";
+
+
+    /**
+     * Default timeout in seconds for initiating session
+     */
+    const TIMEOUT_CONNECTION = 30;
+
+    /**
+     * Maximum of the transaction log
+     * @var integer
+     */
+    protected $_maximumLog = 64;
+
+
+    /**
+     * Hostname or IP address of remote server
+     * @var string
+     */
+    protected $_host;
+
+
+    /**
+     * Port number of connection
+     * @var integer
+     */
+    protected $_port;
+
+
+    /**
+     * Instance of Zend_Validate to check hostnames
+     * @var Zend_Validate
+     */
+    protected $_validHost;
+
+
+    /**
+     * Socket connection resource
+     * @var resource
+     */
+    protected $_socket;
+
+
+    /**
+     * Last request sent to server
+     * @var string
+     */
+    protected $_request;
+
+
+    /**
+     * Array of server responses to last request
+     * @var array
+     */
+    protected $_response;
+
+
+    /**
+     * String template for parsing server responses using sscanf (default: 3 digit code and response string)
+     * @var resource
+     * @deprecated Since 1.10.3
+     */
+    protected $_template = '%d%s';
+
+
+    /**
+     * Log of mail requests and server responses for a session
+     * @var array
+     */
+    private $_log = array();
+
+
+    /**
+     * Constructor.
+     *
+     * @param  string  $host OPTIONAL Hostname of remote connection (default: 127.0.0.1)
+     * @param  integer $port OPTIONAL Port number (default: null)
+     * @throws Zend_Mail_Protocol_Exception
+     * @return void
+     */
+    public function __construct($host = '127.0.0.1', $port = null)
+    {
+        $this->_validHost = new Zend_Validate();
+        $this->_validHost->addValidator(new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL));
+
+        if (!$this->_validHost->isValid($host)) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception(join(', ', $this->_validHost->getMessages()));
+        }
+
+        $this->_host = $host;
+        $this->_port = $port;
+    }
+
+
+    /**
+     * Class destructor to cleanup open resources
+     *
+     * @return void
+     */
+    public function __destruct()
+    {
+        $this->_disconnect();
+    }
+
+    /**
+     * Set the maximum log size
+     *
+     * @param integer $maximumLog Maximum log size
+     * @return void
+     */
+    public function setMaximumLog($maximumLog)
+    {
+        $this->_maximumLog = (int) $maximumLog;
+    }
+
+
+    /**
+     * Get the maximum log size
+     *
+     * @return int the maximum log size
+     */
+    public function getMaximumLog()
+    {
+        return $this->_maximumLog;
+    }
+
+
+    /**
+     * Create a connection to the remote host
+     *
+     * Concrete adapters for this class will implement their own unique connect scripts, using the _connect() method to create the socket resource.
+     */
+    abstract public function connect();
+
+
+    /**
+     * Retrieve the last client request
+     *
+     * @return string
+     */
+    public function getRequest()
+    {
+        return $this->_request;
+    }
+
+
+    /**
+     * Retrieve the last server response
+     *
+     * @return array
+     */
+    public function getResponse()
+    {
+        return $this->_response;
+    }
+
+
+    /**
+     * Retrieve the transaction log
+     *
+     * @return string
+     */
+    public function getLog()
+    {
+        return implode('', $this->_log);
+    }
+
+
+    /**
+     * Reset the transaction log
+     *
+     * @return void
+     */
+    public function resetLog()
+    {
+        $this->_log = array();
+    }
+
+    /**
+     * Add the transaction log
+     *
+     * @param  string new transaction
+     * @return void
+     */
+    protected function _addLog($value)
+    {
+        if ($this->_maximumLog >= 0 && count($this->_log) >= $this->_maximumLog) {
+            array_shift($this->_log);
+        }
+
+        $this->_log[] = $value;
+    }
+
+    /**
+     * Connect to the server using the supplied transport and target
+     *
+     * An example $remote string may be 'tcp://mail.example.com:25' or 'ssh://hostname.com:2222'
+     *
+     * @param  string $remote Remote
+     * @throws Zend_Mail_Protocol_Exception
+     * @return boolean
+     */
+    protected function _connect($remote)
+    {
+        $errorNum = 0;
+        $errorStr = '';
+
+        // open connection
+        $this->_socket = @stream_socket_client($remote, $errorNum, $errorStr, self::TIMEOUT_CONNECTION);
+
+        if ($this->_socket === false) {
+            if ($errorNum == 0) {
+                $errorStr = 'Could not open socket';
+            }
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception($errorStr);
+        }
+
+        if (($result = $this->_setStreamTimeout(self::TIMEOUT_CONNECTION)) === false) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('Could not set stream timeout');
+        }
+
+        return $result;
+    }
+
+
+    /**
+     * Disconnect from remote host and free resource
+     *
+     * @return void
+     */
+    protected function _disconnect()
+    {
+        if (is_resource($this->_socket)) {
+            fclose($this->_socket);
+        }
+    }
+
+
+    /**
+     * Send the given request followed by a LINEEND to the server.
+     *
+     * @param  string $request
+     * @throws Zend_Mail_Protocol_Exception
+     * @return integer|boolean Number of bytes written to remote host
+     */
+    protected function _send($request)
+    {
+        if (!is_resource($this->_socket)) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('No connection has been established to ' . $this->_host);
+        }
+
+        $this->_request = $request;
+
+        $result = fwrite($this->_socket, $request . self::EOL);
+
+        // Save request to internal log
+        $this->_addLog($request . self::EOL);
+
+        if ($result === false) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('Could not send request to ' . $this->_host);
+        }
+
+        return $result;
+    }
+
+
+    /**
+     * Get a line from the stream.
+     *
+     * @var    integer $timeout Per-request timeout value if applicable
+     * @throws Zend_Mail_Protocol_Exception
+     * @return string
+     */
+    protected function _receive($timeout = null)
+    {
+        if (!is_resource($this->_socket)) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('No connection has been established to ' . $this->_host);
+        }
+
+        // Adapters may wish to supply per-commend timeouts according to appropriate RFC
+        if ($timeout !== null) {
+            $this->_setStreamTimeout($timeout);
+        }
+
+        // Retrieve response
+        $reponse = fgets($this->_socket, 1024);
+
+        // Save request to internal log
+        $this->_addLog($reponse);
+
+        // Check meta data to ensure connection is still valid
+        $info = stream_get_meta_data($this->_socket);
+
+        if (!empty($info['timed_out'])) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception($this->_host . ' has timed out');
+        }
+
+        if ($reponse === false) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('Could not read from ' . $this->_host);
+        }
+
+        return $reponse;
+    }
+
+
+    /**
+     * Parse server response for successful codes
+     *
+     * Read the response from the stream and check for expected return code.
+     * Throws a Zend_Mail_Protocol_Exception if an unexpected code is returned.
+     *
+     * @param  string|array $code One or more codes that indicate a successful response
+     * @throws Zend_Mail_Protocol_Exception
+     * @return string Last line of response string
+     */
+    protected function _expect($code, $timeout = null)
+    {
+        $this->_response = array();
+        $cmd  = '';
+        $more = '';
+        $msg  = '';
+        $errMsg = '';
+
+        if (!is_array($code)) {
+            $code = array($code);
+        }
+
+        do {
+            $this->_response[] = $result = $this->_receive($timeout);
+            list($cmd, $more, $msg) = preg_split('/([\s-]+)/', $result, 2, PREG_SPLIT_DELIM_CAPTURE);
+
+            if ($errMsg !== '') {
+                $errMsg .= ' ' . $msg;
+            } elseif ($cmd === null || !in_array($cmd, $code)) {
+                $errMsg =  $msg;
+            }
+
+        } while (strpos($more, '-') === 0); // The '-' message prefix indicates an information string instead of a response string.
+
+        if ($errMsg !== '') {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception($errMsg, $cmd);
+        }
+
+        return $msg;
+    }
+
+    /**
+     * Set stream timeout
+     *
+     * @param integer $timeout
+     * @return boolean
+     */
+    protected function _setStreamTimeout($timeout)
+    {
+       return stream_set_timeout($this->_socket, $timeout);
+    }
+}
Index: trunk/library/Zend/Mail/Protocol/Smtp.php
===================================================================
--- trunk/library/Zend/Mail/Protocol/Smtp.php	(revision 5146)
+++ trunk/library/Zend/Mail/Protocol/Smtp.php	(revision 5146)
@@ -0,0 +1,443 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Smtp.php 23476 2010-12-06 20:10:27Z mjh_ca $
+ */
+
+
+/**
+ * @see Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+
+/**
+ * @see Zend_Mail_Protocol_Abstract
+ */
+require_once 'Zend/Mail/Protocol/Abstract.php';
+
+
+/**
+ * Smtp implementation of Zend_Mail_Protocol_Abstract
+ *
+ * Minimum implementation according to RFC2821: EHLO, MAIL FROM, RCPT TO, DATA, RSET, NOOP, QUIT
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Protocol_Smtp extends Zend_Mail_Protocol_Abstract
+{
+    /**
+     * The transport method for the socket
+     *
+     * @var string
+     */
+    protected $_transport = 'tcp';
+
+
+    /**
+     * Indicates that a session is requested to be secure
+     *
+     * @var string
+     */
+    protected $_secure;
+
+
+    /**
+     * Indicates an smtp session has been started by the HELO command
+     *
+     * @var boolean
+     */
+    protected $_sess = false;
+
+
+    /**
+     * Indicates the HELO command has been issues
+     *
+     * @var unknown_type
+     */
+    protected $_helo = false;
+
+
+    /**
+     * Indicates an smtp AUTH has been issued and authenticated
+     *
+     * @var unknown_type
+     */
+    protected $_auth = false;
+
+
+    /**
+     * Indicates a MAIL command has been issued
+     *
+     * @var unknown_type
+     */
+    protected $_mail = false;
+
+
+    /**
+     * Indicates one or more RCTP commands have been issued
+     *
+     * @var unknown_type
+     */
+    protected $_rcpt = false;
+
+
+    /**
+     * Indicates that DATA has been issued and sent
+     *
+     * @var unknown_type
+     */
+    protected $_data = null;
+
+
+    /**
+     * Constructor.
+     *
+     * @param  string  $host
+     * @param  integer $port
+     * @param  array   $config
+     * @return void
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function __construct($host = '127.0.0.1', $port = null, array $config = array())
+    {
+        if (isset($config['ssl'])) {
+            switch (strtolower($config['ssl'])) {
+                case 'tls':
+                    $this->_secure = 'tls';
+                    break;
+
+                case 'ssl':
+                    $this->_transport = 'ssl';
+                    $this->_secure = 'ssl';
+                    if ($port == null) {
+                        $port = 465;
+                    }
+                    break;
+
+                default:
+                    /**
+                     * @see Zend_Mail_Protocol_Exception
+                     */
+                    require_once 'Zend/Mail/Protocol/Exception.php';
+                    throw new Zend_Mail_Protocol_Exception($config['ssl'] . ' is unsupported SSL type');
+                    break;
+            }
+        }
+
+        // If no port has been specified then check the master PHP ini file. Defaults to 25 if the ini setting is null.
+        if ($port == null) {
+            if (($port = ini_get('smtp_port')) == '') {
+                $port = 25;
+            }
+        }
+
+        parent::__construct($host, $port);
+    }
+
+
+    /**
+     * Connect to the server with the parameters given in the constructor.
+     *
+     * @return boolean
+     */
+    public function connect()
+    {
+        return $this->_connect($this->_transport . '://' . $this->_host . ':'. $this->_port);
+    }
+
+
+    /**
+     * Initiate HELO/EHLO sequence and set flag to indicate valid smtp session
+     *
+     * @param  string $host The client hostname or IP address (default: 127.0.0.1)
+     * @throws Zend_Mail_Protocol_Exception
+     * @return void
+     */
+    public function helo($host = '127.0.0.1')
+    {
+        // Respect RFC 2821 and disallow HELO attempts if session is already initiated.
+        if ($this->_sess === true) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('Cannot issue HELO to existing session');
+        }
+
+        // Validate client hostname
+        if (!$this->_validHost->isValid($host)) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception(join(', ', $this->_validHost->getMessages()));
+        }
+
+        // Initiate helo sequence
+        $this->_expect(220, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+        $this->_ehlo($host);
+
+        // If a TLS session is required, commence negotiation
+        if ($this->_secure == 'tls') {
+            $this->_send('STARTTLS');
+            $this->_expect(220, 180);
+            if (!stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
+                /**
+                 * @see Zend_Mail_Protocol_Exception
+                 */
+                require_once 'Zend/Mail/Protocol/Exception.php';
+                throw new Zend_Mail_Protocol_Exception('Unable to connect via TLS');
+            }
+            $this->_ehlo($host);
+        }
+
+        $this->_startSession();
+        $this->auth();
+    }
+
+
+    /**
+     * Send EHLO or HELO depending on capabilities of smtp host
+     *
+     * @param  string $host The client hostname or IP address (default: 127.0.0.1)
+     * @throws Zend_Mail_Protocol_Exception
+     * @return void
+     */
+    protected function _ehlo($host)
+    {
+        // Support for older, less-compliant remote servers. Tries multiple attempts of EHLO or HELO.
+        try {
+            $this->_send('EHLO ' . $host);
+            $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+        } catch (Zend_Mail_Protocol_Exception $e) {
+            $this->_send('HELO ' . $host);
+            $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+        } catch (Zend_Mail_Protocol_Exception $e) {
+            throw $e;
+        }
+    }
+
+
+    /**
+     * Issues MAIL command
+     *
+     * @param  string $from Sender mailbox
+     * @throws Zend_Mail_Protocol_Exception
+     * @return void
+     */
+    public function mail($from)
+    {
+        if ($this->_sess !== true) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('A valid session has not been started');
+        }
+
+        $this->_send('MAIL FROM:<' . $from . '>');
+        $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+
+        // Set mail to true, clear recipients and any existing data flags as per 4.1.1.2 of RFC 2821
+        $this->_mail = true;
+        $this->_rcpt = false;
+        $this->_data = false;
+    }
+
+
+    /**
+     * Issues RCPT command
+     *
+     * @param  string $to Receiver(s) mailbox
+     * @throws Zend_Mail_Protocol_Exception
+     * @return void
+     */
+    public function rcpt($to)
+    {
+        if ($this->_mail !== true) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('No sender reverse path has been supplied');
+        }
+
+        // Set rcpt to true, as per 4.1.1.3 of RFC 2821
+        $this->_send('RCPT TO:<' . $to . '>');
+        $this->_expect(array(250, 251), 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+        $this->_rcpt = true;
+    }
+
+
+    /**
+     * Issues DATA command
+     *
+     * @param  string $data
+     * @throws Zend_Mail_Protocol_Exception
+     * @return void
+     */
+    public function data($data)
+    {
+        // Ensure recipients have been set
+        if ($this->_rcpt !== true) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('No recipient forward path has been supplied');
+        }
+
+        $this->_send('DATA');
+        $this->_expect(354, 120); // Timeout set for 2 minutes as per RFC 2821 4.5.3.2
+
+        foreach (explode(Zend_Mime::LINEEND, $data) as $line) {
+            if (strpos($line, '.') === 0) {
+                // Escape lines prefixed with a '.'
+                $line = '.' . $line;
+            }
+            $this->_send($line);
+        }
+
+        $this->_send('.');
+        $this->_expect(250, 600); // Timeout set for 10 minutes as per RFC 2821 4.5.3.2
+        $this->_data = true;
+    }
+
+
+    /**
+     * Issues the RSET command and validates answer
+     *
+     * Can be used to restore a clean smtp communication state when a transaction has been cancelled or commencing a new transaction.
+     *
+     * @return void
+     */
+    public function rset()
+    {
+        $this->_send('RSET');
+        // MS ESMTP doesn't follow RFC, see [ZF-1377]
+        $this->_expect(array(250, 220));
+
+        $this->_mail = false;
+        $this->_rcpt = false;
+        $this->_data = false;
+    }
+
+
+    /**
+     * Issues the NOOP command and validates answer
+     *
+     * Not used by Zend_Mail, could be used to keep a connection alive or check if it is still open.
+     *
+     * @return void
+     */
+    public function noop()
+    {
+        $this->_send('NOOP');
+        $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+    }
+
+
+    /**
+     * Issues the VRFY command and validates answer
+     *
+     * Not used by Zend_Mail.
+     *
+     * @param  string $user User Name or eMail to verify
+     * @return void
+     */
+    public function vrfy($user)
+    {
+        $this->_send('VRFY ' . $user);
+        $this->_expect(array(250, 251, 252), 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+    }
+
+
+    /**
+     * Issues the QUIT command and clears the current session
+     *
+     * @return void
+     */
+    public function quit()
+    {
+        if ($this->_sess) {
+            $this->_send('QUIT');
+            $this->_expect(221, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
+            $this->_stopSession();
+        }
+    }
+
+
+    /**
+     * Default authentication method
+     *
+     * This default method is implemented by AUTH adapters to properly authenticate to a remote host.
+     *
+     * @throws Zend_Mail_Protocol_Exception
+     * @return void
+     */
+    public function auth()
+    {
+        if ($this->_auth === true) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('Already authenticated for this session');
+        }
+    }
+
+
+    /**
+     * Closes connection
+     *
+     * @return void
+     */
+    public function disconnect()
+    {
+        $this->_disconnect();
+    }
+
+
+    /**
+     * Start mail session
+     *
+     * @return void
+     */
+    protected function _startSession()
+    {
+        $this->_sess = true;
+    }
+
+
+    /**
+     * Stop mail session
+     *
+     * @return void
+     */
+    protected function _stopSession()
+    {
+        $this->_sess = false;
+    }
+}
Index: trunk/library/Zend/Mail/Protocol/Imap.php
===================================================================
--- trunk/library/Zend/Mail/Protocol/Imap.php	(revision 5146)
+++ trunk/library/Zend/Mail/Protocol/Imap.php	(revision 5146)
@@ -0,0 +1,838 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Imap.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Protocol
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Protocol_Imap
+{
+    /**
+     * Default timeout in seconds for initiating session
+     */
+    const TIMEOUT_CONNECTION = 30;
+
+    /**
+     * socket to imap server
+     * @var resource|null
+     */
+    protected $_socket;
+
+    /**
+     * counter for request tag
+     * @var int
+     */
+    protected $_tagCount = 0;
+
+    /**
+     * Public constructor
+     *
+     * @param  string   $host  hostname or IP address of IMAP server, if given connect() is called
+     * @param  int|null $port  port of IMAP server, null for default (143 or 993 for ssl)
+     * @param  bool     $ssl   use ssl? 'SSL', 'TLS' or false
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    function __construct($host = '', $port = null, $ssl = false)
+    {
+        if ($host) {
+            $this->connect($host, $port, $ssl);
+        }
+    }
+
+    /**
+     * Public destructor
+     */
+    public function __destruct()
+    {
+        $this->logout();
+    }
+
+    /**
+     * Open connection to IMAP server
+     *
+     * @param  string      $host  hostname or IP address of IMAP server
+     * @param  int|null    $port  of IMAP server, default is 143 (993 for ssl)
+     * @param  string|bool $ssl   use 'SSL', 'TLS' or false
+     * @return string welcome message
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function connect($host, $port = null, $ssl = false)
+    {
+        if ($ssl == 'SSL') {
+            $host = 'ssl://' . $host;
+        }
+
+        if ($port === null) {
+            $port = $ssl === 'SSL' ? 993 : 143;
+        }
+
+        $errno  =  0;
+        $errstr = '';
+        $this->_socket = @fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
+        if (!$this->_socket) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('cannot connect to host; error = ' . $errstr .
+                                                   ' (errno = ' . $errno . ' )');
+        }
+
+        if (!$this->_assumedNextLine('* OK')) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('host doesn\'t allow connection');
+        }
+
+        if ($ssl === 'TLS') {
+            $result = $this->requestAndResponse('STARTTLS');
+            $result = $result && stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
+            if (!$result) {
+                /**
+                 * @see Zend_Mail_Protocol_Exception
+                 */
+                require_once 'Zend/Mail/Protocol/Exception.php';
+                throw new Zend_Mail_Protocol_Exception('cannot enable TLS');
+            }
+        }
+    }
+
+    /**
+     * get the next line from socket with error checking, but nothing else
+     *
+     * @return string next line
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    protected function _nextLine()
+    {
+        $line = @fgets($this->_socket);
+        if ($line === false) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('cannot read - connection closed?');
+        }
+
+        return $line;
+    }
+
+    /**
+     * get next line and assume it starts with $start. some requests give a simple
+     * feedback so we can quickly check if we can go on.
+     *
+     * @param  string $start the first bytes we assume to be in the next line
+     * @return bool line starts with $start
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    protected function _assumedNextLine($start)
+    {
+        $line = $this->_nextLine();
+        return strpos($line, $start) === 0;
+    }
+
+    /**
+     * get next line and split the tag. that's the normal case for a response line
+     *
+     * @param  string $tag tag of line is returned by reference
+     * @return string next line
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    protected function _nextTaggedLine(&$tag)
+    {
+        $line = $this->_nextLine();
+
+        // seperate tag from line
+        list($tag, $line) = explode(' ', $line, 2);
+
+        return $line;
+    }
+
+    /**
+     * split a given line in tokens. a token is literal of any form or a list
+     *
+     * @param  string $line line to decode
+     * @return array tokens, literals are returned as string, lists as array
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    protected function _decodeLine($line)
+    {
+        $tokens = array();
+        $stack = array();
+
+        /*
+            We start to decode the response here. The unterstood tokens are:
+                literal
+                "literal" or also "lit\\er\"al"
+                {bytes}<NL>literal
+                (literals*)
+            All tokens are returned in an array. Literals in braces (the last unterstood
+            token in the list) are returned as an array of tokens. I.e. the following response:
+                "foo" baz {3}<NL>bar ("f\\\"oo" bar)
+            would be returned as:
+                array('foo', 'baz', 'bar', array('f\\\"oo', 'bar'));
+
+            // TODO: add handling of '[' and ']' to parser for easier handling of response text
+        */
+        //  replace any trailling <NL> including spaces with a single space
+        $line = rtrim($line) . ' ';
+        while (($pos = strpos($line, ' ')) !== false) {
+            $token = substr($line, 0, $pos);
+            while ($token[0] == '(') {
+                array_push($stack, $tokens);
+                $tokens = array();
+                $token = substr($token, 1);
+            }
+            if ($token[0] == '"') {
+                if (preg_match('%^\(*"((.|\\\\|\\")*?)" *%', $line, $matches)) {
+                    $tokens[] = $matches[1];
+                    $line = substr($line, strlen($matches[0]));
+                    continue;
+                }
+            }
+            if ($token[0] == '{') {
+                $endPos = strpos($token, '}');
+                $chars = substr($token, 1, $endPos - 1);
+                if (is_numeric($chars)) {
+                    $token = '';
+                    while (strlen($token) < $chars) {
+                        $token .= $this->_nextLine();
+                    }
+                    $line = '';
+                    if (strlen($token) > $chars) {
+                        $line = substr($token, $chars);
+                        $token = substr($token, 0, $chars);
+                    } else {
+                        $line .= $this->_nextLine();
+                    }
+                    $tokens[] = $token;
+                    $line = trim($line) . ' ';
+                    continue;
+                }
+            }
+            if ($stack && $token[strlen($token) - 1] == ')') {
+                // closing braces are not seperated by spaces, so we need to count them
+                $braces = strlen($token);
+                $token = rtrim($token, ')');
+                // only count braces if more than one
+                $braces -= strlen($token) + 1;
+                // only add if token had more than just closing braces
+                if (rtrim($token) != '') {
+                    $tokens[] = rtrim($token);
+                }
+                $token = $tokens;
+                $tokens = array_pop($stack);
+                // special handline if more than one closing brace
+                while ($braces-- > 0) {
+                    $tokens[] = $token;
+                    $token = $tokens;
+                    $tokens = array_pop($stack);
+                }
+            }
+            $tokens[] = $token;
+            $line = substr($line, $pos + 1);
+        }
+
+        // maybe the server forgot to send some closing braces
+        while ($stack) {
+            $child = $tokens;
+            $tokens = array_pop($stack);
+            $tokens[] = $child;
+        }
+
+        return $tokens;
+    }
+
+    /**
+     * read a response "line" (could also be more than one real line if response has {..}<NL>)
+     * and do a simple decode
+     *
+     * @param  array|string  $tokens    decoded tokens are returned by reference, if $dontParse
+     *                                  is true the unparsed line is returned here
+     * @param  string        $wantedTag check for this tag for response code. Default '*' is
+     *                                  continuation tag.
+     * @param  bool          $dontParse if true only the unparsed line is returned $tokens
+     * @return bool if returned tag matches wanted tag
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function readLine(&$tokens = array(), $wantedTag = '*', $dontParse = false)
+    {
+        $line = $this->_nextTaggedLine($tag);
+        if (!$dontParse) {
+            $tokens = $this->_decodeLine($line);
+        } else {
+            $tokens = $line;
+        }
+
+        // if tag is wanted tag we might be at the end of a multiline response
+        return $tag == $wantedTag;
+    }
+
+    /**
+     * read all lines of response until given tag is found (last line of response)
+     *
+     * @param  string       $tag       the tag of your request
+     * @param  string|array $filter    you can filter the response so you get only the
+     *                                 given response lines
+     * @param  bool         $dontParse if true every line is returned unparsed instead of
+     *                                 the decoded tokens
+     * @return null|bool|array tokens if success, false if error, null if bad request
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function readResponse($tag, $dontParse = false)
+    {
+        $lines = array();
+        while (!$this->readLine($tokens, $tag, $dontParse)) {
+            $lines[] = $tokens;
+        }
+
+        if ($dontParse) {
+            // last to chars are still needed for response code
+            $tokens = array(substr($tokens, 0, 2));
+        }
+        // last line has response code
+        if ($tokens[0] == 'OK') {
+            return $lines ? $lines : true;
+        } else if ($tokens[0] == 'NO'){
+            return false;
+        }
+        return null;
+    }
+
+    /**
+     * send a request
+     *
+     * @param  string $command your request command
+     * @param  array  $tokens  additional parameters to command, use escapeString() to prepare
+     * @param  string $tag     provide a tag otherwise an autogenerated is returned
+     * @return null
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function sendRequest($command, $tokens = array(), &$tag = null)
+    {
+        if (!$tag) {
+            ++$this->_tagCount;
+            $tag = 'TAG' . $this->_tagCount;
+        }
+
+        $line = $tag . ' ' . $command;
+
+        foreach ($tokens as $token) {
+            if (is_array($token)) {
+                if (@fputs($this->_socket, $line . ' ' . $token[0] . "\r\n") === false) {
+                    /**
+                     * @see Zend_Mail_Protocol_Exception
+                     */
+                    require_once 'Zend/Mail/Protocol/Exception.php';
+                    throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?');
+                }
+                if (!$this->_assumedNextLine('+ ')) {
+                    /**
+                     * @see Zend_Mail_Protocol_Exception
+                     */
+                    require_once 'Zend/Mail/Protocol/Exception.php';
+                    throw new Zend_Mail_Protocol_Exception('cannot send literal string');
+                }
+                $line = $token[1];
+            } else {
+                $line .= ' ' . $token;
+            }
+        }
+
+        if (@fputs($this->_socket, $line . "\r\n") === false) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?');
+        }
+    }
+
+    /**
+     * send a request and get response at once
+     *
+     * @param  string $command   command as in sendRequest()
+     * @param  array  $tokens    parameters as in sendRequest()
+     * @param  bool   $dontParse if true unparsed lines are returned instead of tokens
+     * @return mixed response as in readResponse()
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function requestAndResponse($command, $tokens = array(), $dontParse = false)
+    {
+        $this->sendRequest($command, $tokens, $tag);
+        $response = $this->readResponse($tag, $dontParse);
+
+        return $response;
+    }
+
+    /**
+     * escape one or more literals i.e. for sendRequest
+     *
+     * @param  string|array $string the literal/-s
+     * @return string|array escape literals, literals with newline ar returned
+     *                      as array('{size}', 'string');
+     */
+    public function escapeString($string)
+    {
+        if (func_num_args() < 2) {
+            if (strpos($string, "\n") !== false) {
+                return array('{' . strlen($string) . '}', $string);
+            } else {
+                return '"' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $string) . '"';
+            }
+        }
+        $result = array();
+        foreach (func_get_args() as $string) {
+            $result[] = $this->escapeString($string);
+        }
+        return $result;
+    }
+
+    /**
+     * escape a list with literals or lists
+     *
+     * @param  array $list list with literals or lists as PHP array
+     * @return string escaped list for imap
+     */
+    public function escapeList($list)
+    {
+        $result = array();
+        foreach ($list as $k => $v) {
+            if (!is_array($v)) {
+//              $result[] = $this->escapeString($v);
+                $result[] = $v;
+                continue;
+            }
+            $result[] = $this->escapeList($v);
+        }
+        return '(' . implode(' ', $result) . ')';
+    }
+
+    /**
+     * Login to IMAP server.
+     *
+     * @param  string $user      username
+     * @param  string $password  password
+     * @return bool success
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function login($user, $password)
+    {
+        return $this->requestAndResponse('LOGIN', $this->escapeString($user, $password), true);
+    }
+
+    /**
+     * logout of imap server
+     *
+     * @return bool success
+     */
+    public function logout()
+    {
+        $result = false;
+        if ($this->_socket) {
+            try {
+                $result = $this->requestAndResponse('LOGOUT', array(), true);
+            } catch (Zend_Mail_Protocol_Exception $e) {
+                // ignoring exception
+            }
+            fclose($this->_socket);
+            $this->_socket = null;
+        }
+        return $result;
+    }
+
+
+    /**
+     * Get capabilities from IMAP server
+     *
+     * @return array list of capabilities
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function capability()
+    {
+        $response = $this->requestAndResponse('CAPABILITY');
+
+        if (!$response) {
+            return $response;
+        }
+
+        $capabilities = array();
+        foreach ($response as $line) {
+            $capabilities = array_merge($capabilities, $line);
+        }
+        return $capabilities;
+    }
+
+    /**
+     * Examine and select have the same response. The common code for both
+     * is in this method
+     *
+     * @param  string $command can be 'EXAMINE' or 'SELECT' and this is used as command
+     * @param  string $box which folder to change to or examine
+     * @return bool|array false if error, array with returned information
+     *                    otherwise (flags, exists, recent, uidvalidity)
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function examineOrSelect($command = 'EXAMINE', $box = 'INBOX')
+    {
+        $this->sendRequest($command, array($this->escapeString($box)), $tag);
+
+        $result = array();
+        while (!$this->readLine($tokens, $tag)) {
+            if ($tokens[0] == 'FLAGS') {
+                array_shift($tokens);
+                $result['flags'] = $tokens;
+                continue;
+            }
+            switch ($tokens[1]) {
+                case 'EXISTS':
+                case 'RECENT':
+                    $result[strtolower($tokens[1])] = $tokens[0];
+                    break;
+                case '[UIDVALIDITY':
+                    $result['uidvalidity'] = (int)$tokens[2];
+                    break;
+                default:
+                    // ignore
+            }
+        }
+
+        if ($tokens[0] != 'OK') {
+            return false;
+        }
+        return $result;
+    }
+
+    /**
+     * change folder
+     *
+     * @param  string $box change to this folder
+     * @return bool|array see examineOrselect()
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function select($box = 'INBOX')
+    {
+        return $this->examineOrSelect('SELECT', $box);
+    }
+
+    /**
+     * examine folder
+     *
+     * @param  string $box examine this folder
+     * @return bool|array see examineOrselect()
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function examine($box = 'INBOX')
+    {
+        return $this->examineOrSelect('EXAMINE', $box);
+    }
+
+    /**
+     * fetch one or more items of one or more messages
+     *
+     * @param  string|array $items items to fetch from message(s) as string (if only one item)
+     *                             or array of strings
+     * @param  int          $from  message for items or start message if $to !== null
+     * @param  int|null     $to    if null only one message ($from) is fetched, else it's the
+     *                             last message, INF means last message avaible
+     * @return string|array if only one item of one message is fetched it's returned as string
+     *                      if items of one message are fetched it's returned as (name => value)
+     *                      if one items of messages are fetched it's returned as (msgno => value)
+     *                      if items of messages are fetchted it's returned as (msgno => (name => value))
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function fetch($items, $from, $to = null)
+    {
+        if (is_array($from)) {
+            $set = implode(',', $from);
+        } else if ($to === null) {
+            $set = (int)$from;
+        } else if ($to === INF) {
+            $set = (int)$from . ':*';
+        } else {
+            $set = (int)$from . ':' . (int)$to;
+        }
+
+        $items = (array)$items;
+        $itemList = $this->escapeList($items);
+
+        $this->sendRequest('FETCH', array($set, $itemList), $tag);
+
+        $result = array();
+        while (!$this->readLine($tokens, $tag)) {
+            // ignore other responses
+            if ($tokens[1] != 'FETCH') {
+                continue;
+            }
+            // ignore other messages
+            if ($to === null && !is_array($from) && $tokens[0] != $from) {
+                continue;
+            }
+            // if we only want one item we return that one directly
+            if (count($items) == 1) {
+                if ($tokens[2][0] == $items[0]) {
+                    $data = $tokens[2][1];
+                } else {
+                    // maybe the server send an other field we didn't wanted
+                    $count = count($tokens[2]);
+                    // we start with 2, because 0 was already checked
+                    for ($i = 2; $i < $count; $i += 2) {
+                        if ($tokens[2][$i] != $items[0]) {
+                            continue;
+                        }
+                        $data = $tokens[2][$i + 1];
+                        break;
+                    }
+                }
+            } else {
+                $data = array();
+                while (key($tokens[2]) !== null) {
+                    $data[current($tokens[2])] = next($tokens[2]);
+                    next($tokens[2]);
+                }
+            }
+            // if we want only one message we can ignore everything else and just return
+            if ($to === null && !is_array($from) && $tokens[0] == $from) {
+                // we still need to read all lines
+                while (!$this->readLine($tokens, $tag));
+                return $data;
+            }
+            $result[$tokens[0]] = $data;
+        }
+
+        if ($to === null && !is_array($from)) {
+            /**
+             * @see Zend_Mail_Protocol_Exception
+             */
+            require_once 'Zend/Mail/Protocol/Exception.php';
+            throw new Zend_Mail_Protocol_Exception('the single id was not found in response');
+        }
+
+        return $result;
+    }
+
+    /**
+     * get mailbox list
+     *
+     * this method can't be named after the IMAP command 'LIST', as list is a reserved keyword
+     *
+     * @param  string $reference mailbox reference for list
+     * @param  string $mailbox   mailbox name match with wildcards
+     * @return array mailboxes that matched $mailbox as array(globalName => array('delim' => .., 'flags' => ..))
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function listMailbox($reference = '', $mailbox = '*')
+    {
+        $result = array();
+        $list = $this->requestAndResponse('LIST', $this->escapeString($reference, $mailbox));
+        if (!$list || $list === true) {
+            return $result;
+        }
+
+        foreach ($list as $item) {
+            if (count($item) != 4 || $item[0] != 'LIST') {
+                continue;
+            }
+            $result[$item[3]] = array('delim' => $item[2], 'flags' => $item[1]);
+        }
+
+        return $result;
+    }
+
+    /**
+     * set flags
+     *
+     * @param  array       $flags  flags to set, add or remove - see $mode
+     * @param  int         $from   message for items or start message if $to !== null
+     * @param  int|null    $to     if null only one message ($from) is fetched, else it's the
+     *                             last message, INF means last message avaible
+     * @param  string|null $mode   '+' to add flags, '-' to remove flags, everything else sets the flags as given
+     * @param  bool        $silent if false the return values are the new flags for the wanted messages
+     * @return bool|array new flags if $silent is false, else true or false depending on success
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function store(array $flags, $from, $to = null, $mode = null, $silent = true)
+    {
+        $item = 'FLAGS';
+        if ($mode == '+' || $mode == '-') {
+            $item = $mode . $item;
+        }
+        if ($silent) {
+            $item .= '.SILENT';
+        }
+
+        $flags = $this->escapeList($flags);
+        $set = (int)$from;
+        if ($to != null) {
+            $set .= ':' . ($to == INF ? '*' : (int)$to);
+        }
+
+        $result = $this->requestAndResponse('STORE', array($set, $item, $flags), $silent);
+
+        if ($silent) {
+            return $result ? true : false;
+        }
+
+        $tokens = $result;
+        $result = array();
+        foreach ($tokens as $token) {
+            if ($token[1] != 'FETCH' || $token[2][0] != 'FLAGS') {
+                continue;
+            }
+            $result[$token[0]] = $token[2][1];
+        }
+
+        return $result;
+    }
+
+    /**
+     * append a new message to given folder
+     *
+     * @param string $folder  name of target folder
+     * @param string $message full message content
+     * @param array  $flags   flags for new message
+     * @param string $date    date for new message
+     * @return bool success
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function append($folder, $message, $flags = null, $date = null)
+    {
+        $tokens = array();
+        $tokens[] = $this->escapeString($folder);
+        if ($flags !== null) {
+            $tokens[] = $this->escapeList($flags);
+        }
+        if ($date !== null) {
+            $tokens[] = $this->escapeString($date);
+        }
+        $tokens[] = $this->escapeString($message);
+
+        return $this->requestAndResponse('APPEND', $tokens, true);
+    }
+
+    /**
+     * copy message set from current folder to other folder
+     *
+     * @param string   $folder destination folder
+     * @param int|null $to     if null only one message ($from) is fetched, else it's the
+     *                         last message, INF means last message avaible
+     * @return bool success
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function copy($folder, $from, $to = null)
+    {
+        $set = (int)$from;
+        if ($to != null) {
+            $set .= ':' . ($to == INF ? '*' : (int)$to);
+        }
+
+        return $this->requestAndResponse('COPY', array($set, $this->escapeString($folder)), true);
+    }
+
+    /**
+     * create a new folder (and parent folders if needed)
+     *
+     * @param string $folder folder name
+     * @return bool success
+     */
+    public function create($folder)
+    {
+        return $this->requestAndResponse('CREATE', array($this->escapeString($folder)), true);
+    }
+
+    /**
+     * rename an existing folder
+     *
+     * @param string $old old name
+     * @param string $new new name
+     * @return bool success
+     */
+    public function rename($old, $new)
+    {
+        return $this->requestAndResponse('RENAME', $this->escapeString($old, $new), true);
+    }
+
+    /**
+     * remove a folder
+     *
+     * @param string $folder folder name
+     * @return bool success
+     */
+    public function delete($folder)
+    {
+        return $this->requestAndResponse('DELETE', array($this->escapeString($folder)), true);
+    }
+
+    /**
+     * permanently remove messages
+     *
+     * @return bool success
+     */
+    public function expunge()
+    {
+        // TODO: parse response?
+        return $this->requestAndResponse('EXPUNGE');
+    }
+
+    /**
+     * send noop
+     *
+     * @return bool success
+     */
+    public function noop()
+    {
+        // TODO: parse response
+        return $this->requestAndResponse('NOOP');
+    }
+
+    /**
+     * do a search request
+     *
+     * This method is currently marked as internal as the API might change and is not
+     * safe if you don't take precautions.
+     *
+     * @internal
+     * @return array message ids
+     */
+    public function search(array $params)
+    {
+        $response = $this->requestAndResponse('SEARCH', $params);
+        if (!$response) {
+            return $response;
+        }
+
+        foreach ($response as $ids) {
+            if ($ids[0] == 'SEARCH') {
+                array_shift($ids);
+                return $ids;
+            }
+        }
+        return array();
+    }
+
+}
Index: trunk/library/Zend/Mail/Exception.php
===================================================================
--- trunk/library/Zend/Mail/Exception.php	(revision 5146)
+++ trunk/library/Zend/Mail/Exception.php	(revision 5146)
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Exception extends Zend_Exception
+{}
+
Index: trunk/library/Zend/Mail/Message/File.php
===================================================================
--- trunk/library/Zend/Mail/Message/File.php	(revision 5146)
+++ trunk/library/Zend/Mail/Message/File.php	(revision 5146)
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: File.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * Zend_Mail_Part
+ */
+require_once 'Zend/Mail/Part/File.php';
+
+/**
+ * Zend_Mail_Message_Interface
+ */
+require_once 'Zend/Mail/Message/Interface.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Message_File extends Zend_Mail_Part_File implements Zend_Mail_Message_Interface
+{
+    /**
+     * flags for this message
+     * @var array
+     */
+    protected $_flags = array();
+
+    /**
+     * Public constructor
+     *
+     * In addition to the parameters of Zend_Mail_Part::__construct() this constructor supports:
+     * - flags array with flags for message, keys are ignored, use constants defined in Zend_Mail_Storage
+     *
+     * @param  string $rawMessage  full message with or without headers
+     * @throws Zend_Mail_Exception
+     */
+    public function __construct(array $params)
+    {
+        if (!empty($params['flags'])) {
+            // set key and value to the same value for easy lookup
+            $this->_flags = array_combine($params['flags'], $params['flags']);
+        }
+
+        parent::__construct($params);
+    }
+
+    /**
+     * return toplines as found after headers
+     *
+     * @return string toplines
+     */
+    public function getTopLines()
+    {
+        return $this->_topLines;
+    }
+
+    /**
+     * check if flag is set
+     *
+     * @param mixed $flag a flag name, use constants defined in Zend_Mail_Storage
+     * @return bool true if set, otherwise false
+     */
+    public function hasFlag($flag)
+    {
+        return isset($this->_flags[$flag]);
+    }
+
+    /**
+     * get all set flags
+     *
+     * @return array array with flags, key and value are the same for easy lookup
+     */
+    public function getFlags()
+    {
+        return $this->_flags;
+    }
+}
Index: trunk/library/Zend/Mail/Message/Interface.php
===================================================================
--- trunk/library/Zend/Mail/Message/Interface.php	(revision 5146)
+++ trunk/library/Zend/Mail/Message/Interface.php	(revision 5146)
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+interface Zend_Mail_Message_Interface
+{
+    /**
+     * return toplines as found after headers
+     *
+     * @return string toplines
+     */
+    public function getTopLines();
+
+    /**
+     * check if flag is set
+     *
+     * @param mixed $flag a flag name, use constants defined in Zend_Mail_Storage
+     * @return bool true if set, otherwise false
+     */
+    public function hasFlag($flag);
+
+    /**
+     * get all set flags
+     *
+     * @return array array with flags, key and value are the same for easy lookup
+     */
+    public function getFlags();
+}
Index: trunk/library/Zend/Mail/Storage/Exception.php
===================================================================
--- trunk/library/Zend/Mail/Storage/Exception.php	(revision 5146)
+++ trunk/library/Zend/Mail/Storage/Exception.php	(revision 5146)
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mail_Exception
+ */
+require_once 'Zend/Mail/Exception.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Storage_Exception extends Zend_Mail_Exception
+{}
+
Index: trunk/library/Zend/Mail/Storage/Pop3.php
===================================================================
--- trunk/library/Zend/Mail/Storage/Pop3.php	(revision 5146)
+++ trunk/library/Zend/Mail/Storage/Pop3.php	(revision 5146)
@@ -0,0 +1,328 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Pop3.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Abstract
+ */
+require_once 'Zend/Mail/Storage/Abstract.php';
+
+/**
+ * @see Zend_Mail_Protocol_Pop3
+ */
+require_once 'Zend/Mail/Protocol/Pop3.php';
+
+/**
+ * @see Zend_Mail_Message
+ */
+require_once 'Zend/Mail/Message.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Storage_Pop3 extends Zend_Mail_Storage_Abstract
+{
+    /**
+     * protocol handler
+     * @var null|Zend_Mail_Protocol_Pop3
+     */
+    protected $_protocol;
+
+
+    /**
+     * Count messages all messages in current box
+     *
+     * @return int number of messages
+     * @throws Zend_Mail_Storage_Exception
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function countMessages()
+    {
+        $this->_protocol->status($count, $null);
+        return (int)$count;
+    }
+
+    /**
+     * get a list of messages with number and size
+     *
+     * @param int $id number of message
+     * @return int|array size of given message of list with all messages as array(num => size)
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function getSize($id = 0)
+    {
+        $id = $id ? $id : null;
+        return $this->_protocol->getList($id);
+    }
+
+    /**
+     * Fetch a message
+     *
+     * @param int $id number of message
+     * @return Zend_Mail_Message
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function getMessage($id)
+    {
+        $bodyLines = 0;
+        $message = $this->_protocol->top($id, $bodyLines, true);
+
+        return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $message,
+                                              'noToplines' => $bodyLines < 1));
+    }
+
+    /*
+     * Get raw header of message or part
+     *
+     * @param  int               $id       number of message
+     * @param  null|array|string $part     path to part or null for messsage header
+     * @param  int               $topLines include this many lines with header (after an empty line)
+     * @return string raw header
+     * @throws Zend_Mail_Protocol_Exception
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getRawHeader($id, $part = null, $topLines = 0)
+    {
+        if ($part !== null) {
+            // TODO: implement
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('not implemented');
+        }
+
+        return $this->_protocol->top($id, 0, true);
+    }
+
+    /*
+     * Get raw content of message or part
+     *
+     * @param  int               $id   number of message
+     * @param  null|array|string $part path to part or null for messsage content
+     * @return string raw content
+     * @throws Zend_Mail_Protocol_Exception
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getRawContent($id, $part = null)
+    {
+        if ($part !== null) {
+            // TODO: implement
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('not implemented');
+        }
+
+        $content = $this->_protocol->retrieve($id);
+        // TODO: find a way to avoid decoding the headers
+        Zend_Mime_Decode::splitMessage($content, $null, $body);
+        return $body;
+    }
+
+    /**
+     * create instance with parameters
+     * Supported paramters are
+     *   - host hostname or ip address of POP3 server
+     *   - user username
+     *   - password password for user 'username' [optional, default = '']
+     *   - port port for POP3 server [optional, default = 110]
+     *   - ssl 'SSL' or 'TLS' for secure sockets
+     *
+     * @param  $params array  mail reader specific parameters
+     * @throws Zend_Mail_Storage_Exception
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function __construct($params)
+    {
+        if (is_array($params)) {
+            $params = (object)$params;
+        }
+
+        $this->_has['fetchPart'] = false;
+        $this->_has['top']       = null;
+        $this->_has['uniqueid']  = null;
+
+        if ($params instanceof Zend_Mail_Protocol_Pop3) {
+            $this->_protocol = $params;
+            return;
+        }
+
+        if (!isset($params->user)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('need at least user in params');
+        }
+
+        $host     = isset($params->host)     ? $params->host     : 'localhost';
+        $password = isset($params->password) ? $params->password : '';
+        $port     = isset($params->port)     ? $params->port     : null;
+        $ssl      = isset($params->ssl)      ? $params->ssl      : false;
+
+        $this->_protocol = new Zend_Mail_Protocol_Pop3();
+        $this->_protocol->connect($host, $port, $ssl);
+        $this->_protocol->login($params->user, $password);
+    }
+
+    /**
+     * Close resource for mail lib. If you need to control, when the resource
+     * is closed. Otherwise the destructor would call this.
+     *
+     * @return null
+     */
+    public function close()
+    {
+        $this->_protocol->logout();
+    }
+
+    /**
+     * Keep the server busy.
+     *
+     * @return null
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function noop()
+    {
+        return $this->_protocol->noop();
+    }
+
+    /**
+     * Remove a message from server. If you're doing that from a web enviroment
+     * you should be careful and use a uniqueid as parameter if possible to
+     * identify the message.
+     *
+     * @param  int $id number of message
+     * @return null
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function removeMessage($id)
+    {
+        $this->_protocol->delete($id);
+    }
+
+    /**
+     * get unique id for one or all messages
+     *
+     * if storage does not support unique ids it's the same as the message number
+     *
+     * @param int|null $id message number
+     * @return array|string message number for given message or all messages as array
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getUniqueId($id = null)
+    {
+        if (!$this->hasUniqueid) {
+            if ($id) {
+                return $id;
+            }
+            $count = $this->countMessages();
+            if ($count < 1) {
+                return array();
+            }
+            $range = range(1, $count);
+            return array_combine($range, $range);
+        }
+
+        return $this->_protocol->uniqueid($id);
+    }
+
+    /**
+     * get a message number from a unique id
+     *
+     * I.e. if you have a webmailer that supports deleting messages you should use unique ids
+     * as parameter and use this method to translate it to message number right before calling removeMessage()
+     *
+     * @param string $id unique id
+     * @return int message number
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getNumberByUniqueId($id)
+    {
+        if (!$this->hasUniqueid) {
+            return $id;
+        }
+
+        $ids = $this->getUniqueId();
+        foreach ($ids as $k => $v) {
+            if ($v == $id) {
+                return $k;
+            }
+        }
+
+        /**
+         * @see Zend_Mail_Storage_Exception
+         */
+        require_once 'Zend/Mail/Storage/Exception.php';
+        throw new Zend_Mail_Storage_Exception('unique id not found');
+    }
+
+    /**
+     * Special handling for hasTop and hasUniqueid. The headers of the first message is
+     * retrieved if Top wasn't needed/tried yet.
+     *
+     * @see Zend_Mail_Storage_Abstract:__get()
+     * @param  string $var
+     * @return string
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function __get($var)
+    {
+        $result = parent::__get($var);
+        if ($result !== null) {
+            return $result;
+        }
+
+        if (strtolower($var) == 'hastop') {
+            if ($this->_protocol->hasTop === null) {
+                // need to make a real call, because not all server are honest in their capas
+                try {
+                    $this->_protocol->top(1, 0, false);
+                } catch(Zend_Mail_Exception $e) {
+                    // ignoring error
+                }
+            }
+            $this->_has['top'] = $this->_protocol->hasTop;
+            return $this->_protocol->hasTop;
+        }
+
+        if (strtolower($var) == 'hasuniqueid') {
+            $id = null;
+            try {
+                $id = $this->_protocol->uniqueid(1);
+            } catch(Zend_Mail_Exception $e) {
+                // ignoring error
+            }
+            $this->_has['uniqueid'] = $id ? true : false;
+            return $this->_has['uniqueid'];
+        }
+
+        return $result;
+    }
+}
Index: trunk/library/Zend/Mail/Storage/Folder/Interface.php
===================================================================
--- trunk/library/Zend/Mail/Storage/Folder/Interface.php	(revision 5146)
+++ trunk/library/Zend/Mail/Storage/Folder/Interface.php	(revision 5146)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Mail_Storage_Folder_Interface
+{
+    /**
+     * get root folder or given folder
+     *
+     * @param string $rootFolder get folder structure for given folder, else root
+     * @return Zend_Mail_Storage_Folder root or wanted folder
+     */
+    public function getFolders($rootFolder = null);
+
+    /**
+     * select given folder
+     *
+     * folder must be selectable!
+     *
+     * @param Zend_Mail_Storage_Folder|string $globalName global name of folder or instance for subfolder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function selectFolder($globalName);
+
+
+    /**
+     * get Zend_Mail_Storage_Folder instance for current folder
+     *
+     * @return Zend_Mail_Storage_Folder instance of current folder
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getCurrentFolder();
+}
Index: trunk/library/Zend/Mail/Storage/Folder/Maildir.php
===================================================================
--- trunk/library/Zend/Mail/Storage/Folder/Maildir.php	(revision 5146)
+++ trunk/library/Zend/Mail/Storage/Folder/Maildir.php	(revision 5146)
@@ -0,0 +1,265 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Maildir.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Folder
+ */
+require_once 'Zend/Mail/Storage/Folder.php';
+
+/**
+ * @see Zend_Mail_Storage_Folder_Interface
+ */
+require_once 'Zend/Mail/Storage/Folder/Interface.php';
+
+/**
+ * @see Zend_Mail_Storage_Maildir
+ */
+require_once 'Zend/Mail/Storage/Maildir.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Storage_Folder_Maildir extends Zend_Mail_Storage_Maildir implements Zend_Mail_Storage_Folder_Interface
+{
+    /**
+     * Zend_Mail_Storage_Folder root folder for folder structure
+     * @var Zend_Mail_Storage_Folder
+     */
+    protected $_rootFolder;
+
+    /**
+     * rootdir of folder structure
+     * @var string
+     */
+    protected $_rootdir;
+
+    /**
+     * name of current folder
+     * @var string
+     */
+    protected $_currentFolder;
+
+    /**
+     * delim char for subfolders
+     * @var string
+     */
+    protected $_delim;
+
+    /**
+     * Create instance with parameters
+     * Supported parameters are:
+     *   - dirname rootdir of maildir structure
+     *   - delim   delim char for folder structur, default is '.'
+     *   - folder intial selected folder, default is 'INBOX'
+     *
+     * @param  $params array mail reader specific parameters
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function __construct($params)
+    {
+        if (is_array($params)) {
+            $params = (object)$params;
+        }
+
+        if (!isset($params->dirname) || !is_dir($params->dirname)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('no valid dirname given in params');
+        }
+
+        $this->_rootdir = rtrim($params->dirname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+
+        $this->_delim = isset($params->delim) ? $params->delim : '.';
+
+        $this->_buildFolderTree();
+        $this->selectFolder(!empty($params->folder) ? $params->folder : 'INBOX');
+        $this->_has['top'] = true;
+        $this->_has['flags'] = true;
+    }
+
+    /**
+     * find all subfolders and mbox files for folder structure
+     *
+     * Result is save in Zend_Mail_Storage_Folder instances with the root in $this->_rootFolder.
+     * $parentFolder and $parentGlobalName are only used internally for recursion.
+     *
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    protected function _buildFolderTree()
+    {
+        $this->_rootFolder = new Zend_Mail_Storage_Folder('/', '/', false);
+        $this->_rootFolder->INBOX = new Zend_Mail_Storage_Folder('INBOX', 'INBOX', true);
+
+        $dh = @opendir($this->_rootdir);
+        if (!$dh) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception("can't read folders in maildir");
+        }
+        $dirs = array();
+        while (($entry = readdir($dh)) !== false) {
+            // maildir++ defines folders must start with .
+            if ($entry[0] != '.' || $entry == '.' || $entry == '..') {
+                continue;
+            }
+            if ($this->_isMaildir($this->_rootdir . $entry)) {
+                $dirs[] = $entry;
+            }
+        }
+        closedir($dh);
+
+        sort($dirs);
+        $stack = array(null);
+        $folderStack = array(null);
+        $parentFolder = $this->_rootFolder;
+        $parent = '.';
+
+        foreach ($dirs as $dir) {
+            do {
+                if (strpos($dir, $parent) === 0) {
+                    $local = substr($dir, strlen($parent));
+                    if (strpos($local, $this->_delim) !== false) {
+                        /**
+                         * @see Zend_Mail_Storage_Exception
+                         */
+                        require_once 'Zend/Mail/Storage/Exception.php';
+                        throw new Zend_Mail_Storage_Exception('error while reading maildir');
+                    }
+                    array_push($stack, $parent);
+                    $parent = $dir . $this->_delim;
+                    $folder = new Zend_Mail_Storage_Folder($local, substr($dir, 1), true);
+                    $parentFolder->$local = $folder;
+                    array_push($folderStack, $parentFolder);
+                    $parentFolder = $folder;
+                    break;
+                } else if ($stack) {
+                    $parent = array_pop($stack);
+                    $parentFolder = array_pop($folderStack);
+                }
+            } while ($stack);
+            if (!$stack) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception('error while reading maildir');
+            }
+        }
+    }
+
+    /**
+     * get root folder or given folder
+     *
+     * @param string $rootFolder get folder structure for given folder, else root
+     * @return Zend_Mail_Storage_Folder root or wanted folder
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getFolders($rootFolder = null)
+    {
+        if (!$rootFolder || $rootFolder == 'INBOX') {
+            return $this->_rootFolder;
+        }
+
+        // rootdir is same as INBOX in maildir
+        if (strpos($rootFolder, 'INBOX' . $this->_delim) === 0) {
+            $rootFolder = substr($rootFolder, 6);
+        }
+        $currentFolder = $this->_rootFolder;
+        $subname = trim($rootFolder, $this->_delim);
+        while ($currentFolder) {
+            @list($entry, $subname) = @explode($this->_delim, $subname, 2);
+            $currentFolder = $currentFolder->$entry;
+            if (!$subname) {
+                break;
+            }
+        }
+
+        if ($currentFolder->getGlobalName() != rtrim($rootFolder, $this->_delim)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception("folder $rootFolder not found");
+        }
+        return $currentFolder;
+    }
+
+    /**
+     * select given folder
+     *
+     * folder must be selectable!
+     *
+     * @param Zend_Mail_Storage_Folder|string $globalName global name of folder or instance for subfolder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function selectFolder($globalName)
+    {
+        $this->_currentFolder = (string)$globalName;
+
+        // getting folder from folder tree for validation
+        $folder = $this->getFolders($this->_currentFolder);
+
+        try {
+            $this->_openMaildir($this->_rootdir . '.' . $folder->getGlobalName());
+        } catch(Zend_Mail_Storage_Exception $e) {
+            // check what went wrong
+            if (!$folder->isSelectable()) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception("{$this->_currentFolder} is not selectable", 0, $e);
+            }
+            // seems like file has vanished; rebuilding folder tree - but it's still an exception
+            $this->_buildFolderTree($this->_rootdir);
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('seems like the maildir has vanished, I\'ve rebuild the ' .
+                                                         'folder tree, search for an other folder and try again', 0, $e);
+        }
+    }
+
+    /**
+     * get Zend_Mail_Storage_Folder instance for current folder
+     *
+     * @return Zend_Mail_Storage_Folder instance of current folder
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getCurrentFolder()
+    {
+        return $this->_currentFolder;
+    }
+}
Index: trunk/library/Zend/Mail/Storage/Folder/Mbox.php
===================================================================
--- trunk/library/Zend/Mail/Storage/Folder/Mbox.php	(revision 5146)
+++ trunk/library/Zend/Mail/Storage/Folder/Mbox.php	(revision 5146)
@@ -0,0 +1,264 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Mbox.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Folder
+ */
+require_once 'Zend/Mail/Storage/Folder.php';
+
+/**
+ * @see Zend_Mail_Storage_Folder_Interface
+ */
+require_once 'Zend/Mail/Storage/Folder/Interface.php';
+
+/**
+ * @see Zend_Mail_Storage_Mbox
+ */
+require_once 'Zend/Mail/Storage/Mbox.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Storage_Folder_Mbox extends Zend_Mail_Storage_Mbox implements Zend_Mail_Storage_Folder_Interface
+{
+    /**
+     * Zend_Mail_Storage_Folder root folder for folder structure
+     * @var Zend_Mail_Storage_Folder
+     */
+    protected $_rootFolder;
+
+    /**
+     * rootdir of folder structure
+     * @var string
+     */
+    protected $_rootdir;
+
+    /**
+     * name of current folder
+     * @var string
+     */
+    protected $_currentFolder;
+
+    /**
+     * Create instance with parameters
+     *
+     * Disallowed parameters are:
+     *   - filename use Zend_Mail_Storage_Mbox for a single file
+     * Supported parameters are:
+     *   - dirname rootdir of mbox structure
+     *   - folder intial selected folder, default is 'INBOX'
+     *
+     * @param  $params array mail reader specific parameters
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function __construct($params)
+    {
+        if (is_array($params)) {
+            $params = (object)$params;
+        }
+
+        if (isset($params->filename)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('use Zend_Mail_Storage_Mbox for a single file');
+        }
+
+        if (!isset($params->dirname) || !is_dir($params->dirname)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('no valid dirname given in params');
+        }
+
+        $this->_rootdir = rtrim($params->dirname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+
+        $this->_buildFolderTree($this->_rootdir);
+        $this->selectFolder(!empty($params->folder) ? $params->folder : 'INBOX');
+        $this->_has['top']      = true;
+        $this->_has['uniqueid'] = false;
+    }
+
+    /**
+     * find all subfolders and mbox files for folder structure
+     *
+     * Result is save in Zend_Mail_Storage_Folder instances with the root in $this->_rootFolder.
+     * $parentFolder and $parentGlobalName are only used internally for recursion.
+     *
+     * @param string $currentDir call with root dir, also used for recursion.
+     * @param Zend_Mail_Storage_Folder|null $parentFolder used for recursion
+     * @param string $parentGlobalName used for rescursion
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    protected function _buildFolderTree($currentDir, $parentFolder = null, $parentGlobalName = '')
+    {
+        if (!$parentFolder) {
+            $this->_rootFolder = new Zend_Mail_Storage_Folder('/', '/', false);
+            $parentFolder = $this->_rootFolder;
+        }
+
+        $dh = @opendir($currentDir);
+        if (!$dh) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception("can't read dir $currentDir");
+        }
+        while (($entry = readdir($dh)) !== false) {
+            // ignore hidden files for mbox
+            if ($entry[0] == '.') {
+                continue;
+            }
+            $absoluteEntry = $currentDir . $entry;
+            $globalName = $parentGlobalName . DIRECTORY_SEPARATOR . $entry;
+            if (is_file($absoluteEntry) && $this->_isMboxFile($absoluteEntry)) {
+                $parentFolder->$entry = new Zend_Mail_Storage_Folder($entry, $globalName);
+                continue;
+            }
+            if (!is_dir($absoluteEntry) /* || $entry == '.' || $entry == '..' */) {
+                continue;
+            }
+            $folder = new Zend_Mail_Storage_Folder($entry, $globalName, false);
+            $parentFolder->$entry = $folder;
+            $this->_buildFolderTree($absoluteEntry . DIRECTORY_SEPARATOR, $folder, $globalName);
+        }
+
+        closedir($dh);
+    }
+
+    /**
+     * get root folder or given folder
+     *
+     * @param string $rootFolder get folder structure for given folder, else root
+     * @return Zend_Mail_Storage_Folder root or wanted folder
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getFolders($rootFolder = null)
+    {
+        if (!$rootFolder) {
+            return $this->_rootFolder;
+        }
+
+        $currentFolder = $this->_rootFolder;
+        $subname = trim($rootFolder, DIRECTORY_SEPARATOR);
+        while ($currentFolder) {
+            @list($entry, $subname) = @explode(DIRECTORY_SEPARATOR, $subname, 2);
+            $currentFolder = $currentFolder->$entry;
+            if (!$subname) {
+                break;
+            }
+        }
+
+        if ($currentFolder->getGlobalName() != DIRECTORY_SEPARATOR . trim($rootFolder, DIRECTORY_SEPARATOR)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception("folder $rootFolder not found");
+        }
+        return $currentFolder;
+    }
+
+    /**
+     * select given folder
+     *
+     * folder must be selectable!
+     *
+     * @param Zend_Mail_Storage_Folder|string $globalName global name of folder or instance for subfolder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function selectFolder($globalName)
+    {
+        $this->_currentFolder = (string)$globalName;
+
+        // getting folder from folder tree for validation
+        $folder = $this->getFolders($this->_currentFolder);
+
+        try {
+            $this->_openMboxFile($this->_rootdir . $folder->getGlobalName());
+        } catch(Zend_Mail_Storage_Exception $e) {
+            // check what went wrong
+            if (!$folder->isSelectable()) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception("{$this->_currentFolder} is not selectable", 0, $e);
+            }
+            // seems like file has vanished; rebuilding folder tree - but it's still an exception
+            $this->_buildFolderTree($this->_rootdir);
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('seems like the mbox file has vanished, I\'ve rebuild the ' .
+                                                         'folder tree, search for an other folder and try again', 0, $e);
+        }
+    }
+
+    /**
+     * get Zend_Mail_Storage_Folder instance for current folder
+     *
+     * @return Zend_Mail_Storage_Folder instance of current folder
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getCurrentFolder()
+    {
+        return $this->_currentFolder;
+    }
+
+    /**
+     * magic method for serialize()
+     *
+     * with this method you can cache the mbox class
+     *
+     * @return array name of variables
+     */
+    public function __sleep()
+    {
+        return array_merge(parent::__sleep(), array('_currentFolder', '_rootFolder', '_rootdir'));
+    }
+
+    /**
+     * magic method for unserialize()
+     *
+     * with this method you can cache the mbox class
+     *
+     * @return null
+     */
+    public function __wakeup()
+    {
+        // if cache is stall selectFolder() rebuilds the tree on error
+        parent::__wakeup();
+    }
+}
Index: trunk/library/Zend/Mail/Storage/Mbox.php
===================================================================
--- trunk/library/Zend/Mail/Storage/Mbox.php	(revision 5146)
+++ trunk/library/Zend/Mail/Storage/Mbox.php	(revision 5146)
@@ -0,0 +1,447 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Mbox.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Loader
+ * May be used in constructor, but commented out for now
+ */
+// require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Mail_Storage_Abstract
+ */
+require_once 'Zend/Mail/Storage/Abstract.php';
+
+/**
+ * @see Zend_Mail_Message_File
+ */
+require_once 'Zend/Mail/Message/File.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Storage_Mbox extends Zend_Mail_Storage_Abstract
+{
+    /**
+     * file handle to mbox file
+     * @var null|resource
+     */
+    protected $_fh;
+
+    /**
+     * filename of mbox file for __wakeup
+     * @var string
+     */
+    protected $_filename;
+
+    /**
+     * modification date of mbox file for __wakeup
+     * @var int
+     */
+    protected $_filemtime;
+
+    /**
+     * start and end position of messages as array('start' => start, 'seperator' => headersep, 'end' => end)
+     * @var array
+     */
+    protected $_positions;
+
+    /**
+     * used message class, change it in an extened class to extend the returned message class
+     * @var string
+     */
+    protected $_messageClass = 'Zend_Mail_Message_File';
+
+    /**
+     * Count messages all messages in current box
+     *
+     * @return int number of messages
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function countMessages()
+    {
+        return count($this->_positions);
+    }
+
+
+    /**
+     * Get a list of messages with number and size
+     *
+     * @param  int|null $id  number of message or null for all messages
+     * @return int|array size of given message of list with all messages as array(num => size)
+     */
+    public function getSize($id = 0)
+    {
+        if ($id) {
+            $pos = $this->_positions[$id - 1];
+            return $pos['end'] - $pos['start'];
+        }
+
+        $result = array();
+        foreach ($this->_positions as $num => $pos) {
+            $result[$num + 1] = $pos['end'] - $pos['start'];
+        }
+
+        return $result;
+    }
+
+
+    /**
+     * Get positions for mail message or throw exeption if id is invalid
+     *
+     * @param int $id number of message
+     * @return array positions as in _positions
+     * @throws Zend_Mail_Storage_Exception
+     */
+    protected function _getPos($id)
+    {
+        if (!isset($this->_positions[$id - 1])) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('id does not exist');
+        }
+
+        return $this->_positions[$id - 1];
+    }
+
+
+    /**
+     * Fetch a message
+     *
+     * @param  int $id number of message
+     * @return Zend_Mail_Message_File
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getMessage($id)
+    {
+        // TODO that's ugly, would be better to let the message class decide
+        if (strtolower($this->_messageClass) == 'zend_mail_message_file' || is_subclass_of($this->_messageClass, 'zend_mail_message_file')) {
+            // TODO top/body lines
+            $messagePos = $this->_getPos($id);
+            return new $this->_messageClass(array('file' => $this->_fh, 'startPos' => $messagePos['start'],
+                                                  'endPos' => $messagePos['end']));
+        }
+
+        $bodyLines = 0; // TODO: need a way to change that
+
+        $message = $this->getRawHeader($id);
+        // file pointer is after headers now
+        if ($bodyLines) {
+            $message .= "\n";
+            while ($bodyLines-- && ftell($this->_fh) < $this->_positions[$id - 1]['end']) {
+                $message .= fgets($this->_fh);
+            }
+        }
+
+        return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $message));
+    }
+
+    /*
+     * Get raw header of message or part
+     *
+     * @param  int               $id       number of message
+     * @param  null|array|string $part     path to part or null for messsage header
+     * @param  int               $topLines include this many lines with header (after an empty line)
+     * @return string raw header
+     * @throws Zend_Mail_Protocol_Exception
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getRawHeader($id, $part = null, $topLines = 0)
+    {
+        if ($part !== null) {
+            // TODO: implement
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('not implemented');
+        }
+        $messagePos = $this->_getPos($id);
+        // TODO: toplines
+        return stream_get_contents($this->_fh, $messagePos['separator'] - $messagePos['start'], $messagePos['start']);
+    }
+
+    /*
+     * Get raw content of message or part
+     *
+     * @param  int               $id   number of message
+     * @param  null|array|string $part path to part or null for messsage content
+     * @return string raw content
+     * @throws Zend_Mail_Protocol_Exception
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getRawContent($id, $part = null)
+    {
+        if ($part !== null) {
+            // TODO: implement
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('not implemented');
+        }
+        $messagePos = $this->_getPos($id);
+        return stream_get_contents($this->_fh, $messagePos['end'] - $messagePos['separator'], $messagePos['separator']);
+    }
+
+    /**
+     * Create instance with parameters
+     * Supported parameters are:
+     *   - filename filename of mbox file
+     *
+     * @param  $params array mail reader specific parameters
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function __construct($params)
+    {
+        if (is_array($params)) {
+            $params = (object)$params;
+        }
+
+        if (!isset($params->filename) /* || Zend_Loader::isReadable($params['filename']) */) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('no valid filename given in params');
+        }
+
+        $this->_openMboxFile($params->filename);
+        $this->_has['top']      = true;
+        $this->_has['uniqueid'] = false;
+    }
+
+    /**
+     * check if given file is a mbox file
+     *
+     * if $file is a resource its file pointer is moved after the first line
+     *
+     * @param  resource|string $file stream resource of name of file
+     * @param  bool $fileIsString file is string or resource
+     * @return bool file is mbox file
+     */
+    protected function _isMboxFile($file, $fileIsString = true)
+    {
+        if ($fileIsString) {
+            $file = @fopen($file, 'r');
+            if (!$file) {
+                return false;
+            }
+        } else {
+            fseek($file, 0);
+        }
+
+        $result = false;
+
+        $line = fgets($file);
+        if (strpos($line, 'From ') === 0) {
+            $result = true;
+        }
+
+        if ($fileIsString) {
+            @fclose($file);
+        }
+
+        return $result;
+    }
+
+    /**
+     * open given file as current mbox file
+     *
+     * @param  string $filename filename of mbox file
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    protected function _openMboxFile($filename)
+    {
+        if ($this->_fh) {
+            $this->close();
+        }
+
+        $this->_fh = @fopen($filename, 'r');
+        if (!$this->_fh) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot open mbox file');
+        }
+        $this->_filename = $filename;
+        $this->_filemtime = filemtime($this->_filename);
+
+        if (!$this->_isMboxFile($this->_fh, false)) {
+            @fclose($this->_fh);
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('file is not a valid mbox format');
+        }
+
+        $messagePos = array('start' => ftell($this->_fh), 'separator' => 0, 'end' => 0);
+        while (($line = fgets($this->_fh)) !== false) {
+            if (strpos($line, 'From ') === 0) {
+                $messagePos['end'] = ftell($this->_fh) - strlen($line) - 2; // + newline
+                if (!$messagePos['separator']) {
+                    $messagePos['separator'] = $messagePos['end'];
+                }
+                $this->_positions[] = $messagePos;
+                $messagePos = array('start' => ftell($this->_fh), 'separator' => 0, 'end' => 0);
+            }
+            if (!$messagePos['separator'] && !trim($line)) {
+                $messagePos['separator'] = ftell($this->_fh);
+            }
+        }
+
+        $messagePos['end'] = ftell($this->_fh);
+        if (!$messagePos['separator']) {
+            $messagePos['separator'] = $messagePos['end'];
+        }
+        $this->_positions[] = $messagePos;
+    }
+
+    /**
+     * Close resource for mail lib. If you need to control, when the resource
+     * is closed. Otherwise the destructor would call this.
+     *
+     * @return void
+     */
+    public function close()
+    {
+        @fclose($this->_fh);
+        $this->_positions = array();
+    }
+
+
+    /**
+     * Waste some CPU cycles doing nothing.
+     *
+     * @return void
+     */
+    public function noop()
+    {
+        return true;
+    }
+
+
+    /**
+     * stub for not supported message deletion
+     *
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function removeMessage($id)
+    {
+        /**
+         * @see Zend_Mail_Storage_Exception
+         */
+        require_once 'Zend/Mail/Storage/Exception.php';
+        throw new Zend_Mail_Storage_Exception('mbox is read-only');
+    }
+
+    /**
+     * get unique id for one or all messages
+     *
+     * Mbox does not support unique ids (yet) - it's always the same as the message number.
+     * That shouldn't be a problem, because we can't change mbox files. Therefor the message
+     * number is save enough.
+     *
+     * @param int|null $id message number
+     * @return array|string message number for given message or all messages as array
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getUniqueId($id = null)
+    {
+        if ($id) {
+            // check if id exists
+            $this->_getPos($id);
+            return $id;
+        }
+
+        $range = range(1, $this->countMessages());
+        return array_combine($range, $range);
+    }
+
+    /**
+     * get a message number from a unique id
+     *
+     * I.e. if you have a webmailer that supports deleting messages you should use unique ids
+     * as parameter and use this method to translate it to message number right before calling removeMessage()
+     *
+     * @param string $id unique id
+     * @return int message number
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getNumberByUniqueId($id)
+    {
+        // check if id exists
+        $this->_getPos($id);
+        return $id;
+    }
+
+    /**
+     * magic method for serialize()
+     *
+     * with this method you can cache the mbox class
+     *
+     * @return array name of variables
+     */
+    public function __sleep()
+    {
+        return array('_filename', '_positions', '_filemtime');
+    }
+
+    /**
+     * magic method for unserialize()
+     *
+     * with this method you can cache the mbox class
+     * for cache validation the mtime of the mbox file is used
+     *
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function __wakeup()
+    {
+        if ($this->_filemtime != @filemtime($this->_filename)) {
+            $this->close();
+            $this->_openMboxFile($this->_filename);
+        } else {
+            $this->_fh = @fopen($this->_filename, 'r');
+            if (!$this->_fh) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception('cannot open mbox file');
+            }
+        }
+    }
+
+}
Index: trunk/library/Zend/Mail/Storage/Imap.php
===================================================================
--- trunk/library/Zend/Mail/Storage/Imap.php	(revision 5146)
+++ trunk/library/Zend/Mail/Storage/Imap.php	(revision 5146)
@@ -0,0 +1,644 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Imap.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Abstract
+ */
+require_once 'Zend/Mail/Storage/Abstract.php';
+
+/**
+ * @see Zend_Mail_Protocol_Imap
+ */
+require_once 'Zend/Mail/Protocol/Imap.php';
+
+/**
+ * @see Zend_Mail_Storage_Writable_Interface
+ */
+require_once 'Zend/Mail/Storage/Writable/Interface.php';
+
+/**
+ * @see Zend_Mail_Storage_Folder_Interface
+ */
+require_once 'Zend/Mail/Storage/Folder/Interface.php';
+
+/**
+ * @see Zend_Mail_Storage_Folder
+ */
+require_once 'Zend/Mail/Storage/Folder.php';
+
+/**
+ * @see Zend_Mail_Message
+ */
+require_once 'Zend/Mail/Message.php';
+
+/**
+ * @see Zend_Mail_Storage
+ */
+require_once 'Zend/Mail/Storage.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Storage_Imap extends Zend_Mail_Storage_Abstract
+                             implements Zend_Mail_Storage_Folder_Interface, Zend_Mail_Storage_Writable_Interface
+{
+    // TODO: with an internal cache we could optimize this class, or create an extra class with
+    // such optimizations. Especially the various fetch calls could be combined to one cache call
+
+    /**
+     * protocol handler
+     * @var null|Zend_Mail_Protocol_Imap
+     */
+    protected $_protocol;
+
+    /**
+     * name of current folder
+     * @var string
+     */
+    protected $_currentFolder = '';
+
+    /**
+     * imap flags to constants translation
+     * @var array
+     */
+    protected static $_knownFlags = array('\Passed'   => Zend_Mail_Storage::FLAG_PASSED,
+                                          '\Answered' => Zend_Mail_Storage::FLAG_ANSWERED,
+                                          '\Seen'     => Zend_Mail_Storage::FLAG_SEEN,
+                                          '\Deleted'  => Zend_Mail_Storage::FLAG_DELETED,
+                                          '\Draft'    => Zend_Mail_Storage::FLAG_DRAFT,
+                                          '\Flagged'  => Zend_Mail_Storage::FLAG_FLAGGED);
+
+    /**
+     * map flags to search criterias
+     * @var array
+     */
+    protected static $_searchFlags = array('\Recent'   => 'RECENT',
+                                           '\Answered' => 'ANSWERED',
+                                           '\Seen'     => 'SEEN',
+                                           '\Deleted'  => 'DELETED',
+                                           '\Draft'    => 'DRAFT',
+                                           '\Flagged'  => 'FLAGGED');
+
+    /**
+     * Count messages all messages in current box
+     *
+     * @return int number of messages
+     * @throws Zend_Mail_Storage_Exception
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function countMessages($flags = null)
+    {
+        if (!$this->_currentFolder) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('No selected folder to count');
+        }
+
+        if ($flags === null) {
+            return count($this->_protocol->search(array('ALL')));
+        }
+
+        $params = array();
+        foreach ((array)$flags as $flag) {
+            if (isset(self::$_searchFlags[$flag])) {
+                $params[] = self::$_searchFlags[$flag];
+            } else {
+                $params[] = 'KEYWORD';
+                $params[] = $this->_protocol->escapeString($flag);
+            }
+        }
+        return count($this->_protocol->search($params));
+    }
+
+    /**
+     * get a list of messages with number and size
+     *
+     * @param int $id number of message
+     * @return int|array size of given message of list with all messages as array(num => size)
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function getSize($id = 0)
+    {
+        if ($id) {
+            return $this->_protocol->fetch('RFC822.SIZE', $id);
+        }
+        return $this->_protocol->fetch('RFC822.SIZE', 1, INF);
+    }
+
+    /**
+     * Fetch a message
+     *
+     * @param int $id number of message
+     * @return Zend_Mail_Message
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function getMessage($id)
+    {
+        $data = $this->_protocol->fetch(array('FLAGS', 'RFC822.HEADER'), $id);
+        $header = $data['RFC822.HEADER'];
+
+        $flags = array();
+        foreach ($data['FLAGS'] as $flag) {
+            $flags[] = isset(self::$_knownFlags[$flag]) ? self::$_knownFlags[$flag] : $flag;
+        }
+
+        return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $header, 'flags' => $flags));
+    }
+
+    /*
+     * Get raw header of message or part
+     *
+     * @param  int               $id       number of message
+     * @param  null|array|string $part     path to part or null for messsage header
+     * @param  int               $topLines include this many lines with header (after an empty line)
+     * @param  int $topLines include this many lines with header (after an empty line)
+     * @return string raw header
+     * @throws Zend_Mail_Protocol_Exception
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getRawHeader($id, $part = null, $topLines = 0)
+    {
+        if ($part !== null) {
+            // TODO: implement
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('not implemented');
+        }
+
+        // TODO: toplines
+        return $this->_protocol->fetch('RFC822.HEADER', $id);
+    }
+
+    /*
+     * Get raw content of message or part
+     *
+     * @param  int               $id   number of message
+     * @param  null|array|string $part path to part or null for messsage content
+     * @return string raw content
+     * @throws Zend_Mail_Protocol_Exception
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getRawContent($id, $part = null)
+    {
+        if ($part !== null) {
+            // TODO: implement
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('not implemented');
+        }
+
+        return $this->_protocol->fetch('RFC822.TEXT', $id);
+    }
+
+    /**
+     * create instance with parameters
+     * Supported paramters are
+     *   - user username
+     *   - host hostname or ip address of IMAP server [optional, default = 'localhost']
+     *   - password password for user 'username' [optional, default = '']
+     *   - port port for IMAP server [optional, default = 110]
+     *   - ssl 'SSL' or 'TLS' for secure sockets
+     *   - folder select this folder [optional, default = 'INBOX']
+     *
+     * @param  array $params mail reader specific parameters
+     * @throws Zend_Mail_Storage_Exception
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function __construct($params)
+    {
+        if (is_array($params)) {
+            $params = (object)$params;
+        }
+
+        $this->_has['flags'] = true;
+
+        if ($params instanceof Zend_Mail_Protocol_Imap) {
+            $this->_protocol = $params;
+            try {
+                $this->selectFolder('INBOX');
+            } catch(Zend_Mail_Storage_Exception $e) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception('cannot select INBOX, is this a valid transport?', 0, $e);
+            }
+            return;
+        }
+
+        if (!isset($params->user)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('need at least user in params');
+        }
+
+        $host     = isset($params->host)     ? $params->host     : 'localhost';
+        $password = isset($params->password) ? $params->password : '';
+        $port     = isset($params->port)     ? $params->port     : null;
+        $ssl      = isset($params->ssl)      ? $params->ssl      : false;
+
+        $this->_protocol = new Zend_Mail_Protocol_Imap();
+        $this->_protocol->connect($host, $port, $ssl);
+        if (!$this->_protocol->login($params->user, $password)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot login, user or password wrong');
+        }
+        $this->selectFolder(isset($params->folder) ? $params->folder : 'INBOX');
+    }
+
+    /**
+     * Close resource for mail lib. If you need to control, when the resource
+     * is closed. Otherwise the destructor would call this.
+     *
+     * @return null
+     */
+    public function close()
+    {
+        $this->_currentFolder = '';
+        $this->_protocol->logout();
+    }
+
+    /**
+     * Keep the server busy.
+     *
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function noop()
+    {
+        if (!$this->_protocol->noop()) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('could not do nothing');
+        }
+    }
+
+    /**
+     * Remove a message from server. If you're doing that from a web enviroment
+     * you should be careful and use a uniqueid as parameter if possible to
+     * identify the message.
+     *
+     * @param   int $id number of message
+     * @return  null
+     * @throws  Zend_Mail_Storage_Exception
+     */
+    public function removeMessage($id)
+    {
+        if (!$this->_protocol->store(array(Zend_Mail_Storage::FLAG_DELETED), $id, null, '+')) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot set deleted flag');
+        }
+        // TODO: expunge here or at close? we can handle an error here better and are more fail safe
+        if (!$this->_protocol->expunge()) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('message marked as deleted, but could not expunge');
+        }
+    }
+
+    /**
+     * get unique id for one or all messages
+     *
+     * if storage does not support unique ids it's the same as the message number
+     *
+     * @param int|null $id message number
+     * @return array|string message number for given message or all messages as array
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getUniqueId($id = null)
+    {
+        if ($id) {
+            return $this->_protocol->fetch('UID', $id);
+        }
+
+        return $this->_protocol->fetch('UID', 1, INF);
+    }
+
+    /**
+     * get a message number from a unique id
+     *
+     * I.e. if you have a webmailer that supports deleting messages you should use unique ids
+     * as parameter and use this method to translate it to message number right before calling removeMessage()
+     *
+     * @param string $id unique id
+     * @return int message number
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getNumberByUniqueId($id)
+    {
+        // TODO: use search to find number directly
+        $ids = $this->getUniqueId();
+        foreach ($ids as $k => $v) {
+            if ($v == $id) {
+                return $k;
+            }
+        }
+
+        /**
+         * @see Zend_Mail_Storage_Exception
+         */
+        require_once 'Zend/Mail/Storage/Exception.php';
+        throw new Zend_Mail_Storage_Exception('unique id not found');
+    }
+
+
+    /**
+     * get root folder or given folder
+     *
+     * @param  string $rootFolder get folder structure for given folder, else root
+     * @return Zend_Mail_Storage_Folder root or wanted folder
+     * @throws Zend_Mail_Storage_Exception
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function getFolders($rootFolder = null)
+    {
+        $folders = $this->_protocol->listMailbox((string)$rootFolder);
+        if (!$folders) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('folder not found');
+        }
+
+        ksort($folders, SORT_STRING);
+        $root = new Zend_Mail_Storage_Folder('/', '/', false);
+        $stack = array(null);
+        $folderStack = array(null);
+        $parentFolder = $root;
+        $parent = '';
+
+        foreach ($folders as $globalName => $data) {
+            do {
+                if (!$parent || strpos($globalName, $parent) === 0) {
+                    $pos = strrpos($globalName, $data['delim']);
+                    if ($pos === false) {
+                        $localName = $globalName;
+                    } else {
+                        $localName = substr($globalName, $pos + 1);
+                    }
+                    $selectable = !$data['flags'] || !in_array('\\Noselect', $data['flags']);
+
+                    array_push($stack, $parent);
+                    $parent = $globalName . $data['delim'];
+                    $folder = new Zend_Mail_Storage_Folder($localName, $globalName, $selectable);
+                    $parentFolder->$localName = $folder;
+                    array_push($folderStack, $parentFolder);
+                    $parentFolder = $folder;
+                    break;
+                } else if ($stack) {
+                    $parent = array_pop($stack);
+                    $parentFolder = array_pop($folderStack);
+                }
+            } while ($stack);
+            if (!$stack) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception('error while constructing folder tree');
+            }
+        }
+
+        return $root;
+    }
+
+    /**
+     * select given folder
+     *
+     * folder must be selectable!
+     *
+     * @param  Zend_Mail_Storage_Folder|string $globalName global name of folder or instance for subfolder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     * @throws Zend_Mail_Protocol_Exception
+     */
+    public function selectFolder($globalName)
+    {
+        $this->_currentFolder = $globalName;
+        if (!$this->_protocol->select($this->_currentFolder)) {
+            $this->_currentFolder = '';
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot change folder, maybe it does not exist');
+        }
+    }
+
+
+    /**
+     * get Zend_Mail_Storage_Folder instance for current folder
+     *
+     * @return Zend_Mail_Storage_Folder instance of current folder
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getCurrentFolder()
+    {
+        return $this->_currentFolder;
+    }
+
+    /**
+     * create a new folder
+     *
+     * This method also creates parent folders if necessary. Some mail storages may restrict, which folder
+     * may be used as parent or which chars may be used in the folder name
+     *
+     * @param  string                          $name         global name of folder, local name if $parentFolder is set
+     * @param  string|Zend_Mail_Storage_Folder $parentFolder parent folder for new folder, else root folder is parent
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function createFolder($name, $parentFolder = null)
+    {
+        // TODO: we assume / as the hierarchy delim - need to get that from the folder class!
+        if ($parentFolder instanceof Zend_Mail_Storage_Folder) {
+            $folder = $parentFolder->getGlobalName() . '/' . $name;
+        } else if ($parentFolder != null) {
+            $folder = $parentFolder . '/' . $name;
+        } else {
+            $folder = $name;
+        }
+
+        if (!$this->_protocol->create($folder)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot create folder');
+        }
+    }
+
+    /**
+     * remove a folder
+     *
+     * @param  string|Zend_Mail_Storage_Folder $name      name or instance of folder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function removeFolder($name)
+    {
+        if ($name instanceof Zend_Mail_Storage_Folder) {
+            $name = $name->getGlobalName();
+        }
+
+        if (!$this->_protocol->delete($name)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot delete folder');
+        }
+    }
+
+    /**
+     * rename and/or move folder
+     *
+     * The new name has the same restrictions as in createFolder()
+     *
+     * @param  string|Zend_Mail_Storage_Folder $oldName name or instance of folder
+     * @param  string                          $newName new global name of folder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function renameFolder($oldName, $newName)
+    {
+        if ($oldName instanceof Zend_Mail_Storage_Folder) {
+            $oldName = $oldName->getGlobalName();
+        }
+
+        if (!$this->_protocol->rename($oldName, $newName)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot rename folder');
+        }
+    }
+
+    /**
+     * append a new message to mail storage
+     *
+     * @param  string                                     $message message as string or instance of message class
+     * @param  null|string|Zend_Mail_Storage_Folder       $folder  folder for new message, else current folder is taken
+     * @param  null|array                                 $flags   set flags for new message, else a default set is used
+     * @throws Zend_Mail_Storage_Exception
+     */
+     // not yet * @param string|Zend_Mail_Message|Zend_Mime_Message $message message as string or instance of message class
+    public function appendMessage($message, $folder = null, $flags = null)
+    {
+        if ($folder === null) {
+            $folder = $this->_currentFolder;
+        }
+
+        if ($flags === null) {
+            $flags = array(Zend_Mail_Storage::FLAG_SEEN);
+        }
+
+        // TODO: handle class instances for $message
+        if (!$this->_protocol->append($folder, $message, $flags)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot create message, please check if the folder exists and your flags');
+        }
+    }
+
+    /**
+     * copy an existing message
+     *
+     * @param  int                             $id     number of message
+     * @param  string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function copyMessage($id, $folder)
+    {
+        if (!$this->_protocol->copy($folder, $id)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot copy message, does the folder exist?');
+        }
+    }
+
+    /**
+     * move an existing message
+     *
+     * NOTE: imap has no native move command, thus it's emulated with copy and delete
+     *
+     * @param  int                             $id     number of message
+     * @param  string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function moveMessage($id, $folder) {
+        $this->copyMessage($id, $folder);
+        $this->removeMessage($id);
+    }
+
+    /**
+     * set flags for message
+     *
+     * NOTE: this method can't set the recent flag.
+     *
+     * @param  int   $id    number of message
+     * @param  array $flags new flags for message
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function setFlags($id, $flags)
+    {
+        if (!$this->_protocol->store($flags, $id)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot set flags, have you tried to set the recent flag or special chars?');
+        }
+    }
+}
+
Index: trunk/library/Zend/Mail/Part/File.php
===================================================================
--- trunk/library/Zend/Mail/Part/File.php	(revision 5146)
+++ trunk/library/Zend/Mail/Part/File.php	(revision 5146)
@@ -0,0 +1,198 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: File.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mime_Decode
+ */
+require_once 'Zend/Mime/Decode.php';
+
+/**
+ * @see Zend_Mail_Part
+ */
+require_once 'Zend/Mail/Part.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Part_File extends Zend_Mail_Part
+{
+    protected $_contentPos = array();
+    protected $_partPos = array();
+    protected $_fh;
+
+    /**
+     * Public constructor
+     *
+     * This handler supports the following params:
+     * - file     filename or open file handler with message content (required)
+     * - startPos start position of message or part in file (default: current position)
+     * - endPos   end position of message or part in file (default: end of file)
+     *
+     * @param   array $params  full message with or without headers
+     * @throws  Zend_Mail_Exception
+     */
+    public function __construct(array $params)
+    {
+        if (empty($params['file'])) {
+            /**
+             * @see Zend_Mail_Exception
+             */
+            require_once 'Zend/Mail/Exception.php';
+            throw new Zend_Mail_Exception('no file given in params');
+        }
+
+        if (!is_resource($params['file'])) {
+            $this->_fh = fopen($params['file'], 'r');
+        } else {
+            $this->_fh = $params['file'];
+        }
+        if (!$this->_fh) {
+            /**
+             * @see Zend_Mail_Exception
+             */
+            require_once 'Zend/Mail/Exception.php';
+            throw new Zend_Mail_Exception('could not open file');
+        }
+        if (isset($params['startPos'])) {
+            fseek($this->_fh, $params['startPos']);
+        }
+        $header = '';
+        $endPos = isset($params['endPos']) ? $params['endPos'] : null;
+        while (($endPos === null || ftell($this->_fh) < $endPos) && trim($line = fgets($this->_fh))) {
+            $header .= $line;
+        }
+
+        Zend_Mime_Decode::splitMessage($header, $this->_headers, $null);
+
+        $this->_contentPos[0] = ftell($this->_fh);
+        if ($endPos !== null) {
+            $this->_contentPos[1] = $endPos;
+        } else {
+            fseek($this->_fh, 0, SEEK_END);
+            $this->_contentPos[1] = ftell($this->_fh);
+        }
+        if (!$this->isMultipart()) {
+            return;
+        }
+
+        $boundary = $this->getHeaderField('content-type', 'boundary');
+        if (!$boundary) {
+            /**
+             * @see Zend_Mail_Exception
+             */
+            require_once 'Zend/Mail/Exception.php';
+            throw new Zend_Mail_Exception('no boundary found in content type to split message');
+        }
+
+        $part = array();
+        $pos = $this->_contentPos[0];
+        fseek($this->_fh, $pos);
+        while (!feof($this->_fh) && ($endPos === null || $pos < $endPos)) {
+            $line = fgets($this->_fh);
+            if ($line === false) {
+                if (feof($this->_fh)) {
+                    break;
+                }
+                /**
+                 * @see Zend_Mail_Exception
+                 */
+                require_once 'Zend/Mail/Exception.php';
+                throw new Zend_Mail_Exception('error reading file');
+            }
+
+            $lastPos = $pos;
+            $pos = ftell($this->_fh);
+            $line = trim($line);
+
+            if ($line == '--' . $boundary) {
+                if ($part) {
+                    // not first part
+                    $part[1] = $lastPos;
+                    $this->_partPos[] = $part;
+                }
+                $part = array($pos);
+            } else if ($line == '--' . $boundary . '--') {
+                $part[1] = $lastPos;
+                $this->_partPos[] = $part;
+                break;
+            }
+        }
+        $this->_countParts = count($this->_partPos);
+
+    }
+
+
+    /**
+     * Body of part
+     *
+     * If part is multipart the raw content of this part with all sub parts is returned
+     *
+     * @return string body
+     * @throws Zend_Mail_Exception
+     */
+    public function getContent($stream = null)
+    {
+        fseek($this->_fh, $this->_contentPos[0]);
+        if ($stream !== null) {
+            return stream_copy_to_stream($this->_fh, $stream, $this->_contentPos[1] - $this->_contentPos[0]);
+        }
+        $length = $this->_contentPos[1] - $this->_contentPos[0];
+        return $length < 1 ? '' : fread($this->_fh, $length);
+    }
+
+    /**
+     * Return size of part
+     *
+     * Quite simple implemented currently (not decoding). Handle with care.
+     *
+     * @return int size
+     */
+    public function getSize() {
+        return $this->_contentPos[1] - $this->_contentPos[0];
+    }
+
+    /**
+     * Get part of multipart message
+     *
+     * @param  int $num number of part starting with 1 for first part
+     * @return Zend_Mail_Part wanted part
+     * @throws Zend_Mail_Exception
+     */
+    public function getPart($num)
+    {
+        --$num;
+        if (!isset($this->_partPos[$num])) {
+            /**
+             * @see Zend_Mail_Exception
+             */
+            require_once 'Zend/Mail/Exception.php';
+            throw new Zend_Mail_Exception('part not found');
+        }
+
+        return new self(array('file' => $this->_fh, 'startPos' => $this->_partPos[$num][0],
+                              'endPos' => $this->_partPos[$num][1]));
+    }
+}
Index: trunk/library/Zend/Mail/Part/Interface.php
===================================================================
--- trunk/library/Zend/Mail/Part/Interface.php	(revision 5146)
+++ trunk/library/Zend/Mail/Part/Interface.php	(revision 5146)
@@ -0,0 +1,136 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+interface Zend_Mail_Part_Interface extends RecursiveIterator
+{
+    /**
+     * Check if part is a multipart message
+     *
+     * @return bool if part is multipart
+     */
+    public function isMultipart();
+
+
+    /**
+     * Body of part
+     *
+     * If part is multipart the raw content of this part with all sub parts is returned
+     *
+     * @return string body
+     * @throws Zend_Mail_Exception
+     */
+    public function getContent();
+
+    /**
+     * Return size of part
+     *
+     * @return int size
+     */
+    public function getSize();
+
+    /**
+     * Get part of multipart message
+     *
+     * @param  int $num number of part starting with 1 for first part
+     * @return Zend_Mail_Part wanted part
+     * @throws Zend_Mail_Exception
+     */
+    public function getPart($num);
+
+    /**
+     * Count parts of a multipart part
+     *
+     * @return int number of sub-parts
+     */
+    public function countParts();
+
+
+    /**
+     * Get all headers
+     *
+     * The returned headers are as saved internally. All names are lowercased. The value is a string or an array
+     * if a header with the same name occurs more than once.
+     *
+     * @return array headers as array(name => value)
+     */
+    public function getHeaders();
+
+    /**
+     * Get a header in specificed format
+     *
+     * Internally headers that occur more than once are saved as array, all other as string. If $format
+     * is set to string implode is used to concat the values (with Zend_Mime::LINEEND as delim).
+     *
+     * @param  string $name   name of header, matches case-insensitive, but camel-case is replaced with dashes
+     * @param  string $format change type of return value to 'string' or 'array'
+     * @return string|array value of header in wanted or internal format
+     * @throws Zend_Mail_Exception
+     */
+    public function getHeader($name, $format = null);
+
+    /**
+     * Get a specific field from a header like content type or all fields as array
+     *
+     * If the header occurs more than once, only the value from the first header
+     * is returned.
+     *
+     * Throws a Zend_Mail_Exception if the requested header does not exist. If
+     * the specific header field does not exist, returns null.
+     *
+     * @param  string $name       name of header, like in getHeader()
+     * @param  string $wantedPart the wanted part, default is first, if null an array with all parts is returned
+     * @param  string $firstName  key name for the first part
+     * @return string|array wanted part or all parts as array($firstName => firstPart, partname => value)
+     * @throws Zend_Exception, Zend_Mail_Exception
+     */
+    public function getHeaderField($name, $wantedPart = 0, $firstName = 0);
+
+
+    /**
+     * Getter for mail headers - name is matched in lowercase
+     *
+     * This getter is short for Zend_Mail_Part::getHeader($name, 'string')
+     *
+     * @see Zend_Mail_Part::getHeader()
+     *
+     * @param  string $name header name
+     * @return string value of header
+     * @throws Zend_Mail_Exception
+     */
+    public function __get($name);
+
+    /**
+     * magic method to get content of part
+     *
+     * @return string content
+     */
+    public function __toString();
+}
Index: trunk/library/Zend/Mail/Message.php
===================================================================
--- trunk/library/Zend/Mail/Message.php	(revision 5146)
+++ trunk/library/Zend/Mail/Message.php	(revision 5146)
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Message.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * Zend_Mail_Part
+ */
+require_once 'Zend/Mail/Part.php';
+
+/**
+ * Zend_Mail_Message_Interface
+ */
+require_once 'Zend/Mail/Message/Interface.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Message extends Zend_Mail_Part implements Zend_Mail_Message_Interface
+{
+    /**
+     * flags for this message
+     * @var array
+     */
+    protected $_flags = array();
+
+    /**
+     * Public constructor
+     *
+     * In addition to the parameters of Zend_Mail_Part::__construct() this constructor supports:
+     * - file  filename or file handle of a file with raw message content
+     * - flags array with flags for message, keys are ignored, use constants defined in Zend_Mail_Storage
+     *
+     * @param  string $rawMessage  full message with or without headers
+     * @throws Zend_Mail_Exception
+     */
+    public function __construct(array $params)
+    {
+        if (isset($params['file'])) {
+            if (!is_resource($params['file'])) {
+                $params['raw'] = @file_get_contents($params['file']);
+                if ($params['raw'] === false) {
+                    /**
+                     * @see Zend_Mail_Exception
+                     */
+                    require_once 'Zend/Mail/Exception.php';
+                    throw new Zend_Mail_Exception('could not open file');
+                }
+            } else {
+                $params['raw'] = stream_get_contents($params['file']);
+            }
+        }
+
+        if (!empty($params['flags'])) {
+            // set key and value to the same value for easy lookup
+            $this->_flags = array_combine($params['flags'], $params['flags']);
+        }
+
+        parent::__construct($params);
+    }
+
+    /**
+     * return toplines as found after headers
+     *
+     * @return string toplines
+     */
+    public function getTopLines()
+    {
+        return $this->_topLines;
+    }
+
+    /**
+     * check if flag is set
+     *
+     * @param mixed $flag a flag name, use constants defined in Zend_Mail_Storage
+     * @return bool true if set, otherwise false
+     */
+    public function hasFlag($flag)
+    {
+        return isset($this->_flags[$flag]);
+    }
+
+    /**
+     * get all set flags
+     *
+     * @return array array with flags, key and value are the same for easy lookup
+     */
+    public function getFlags()
+    {
+        return $this->_flags;
+    }
+}
Index: trunk/library/Zend/Mail/Storage.php
===================================================================
--- trunk/library/Zend/Mail/Storage.php	(revision 5146)
+++ trunk/library/Zend/Mail/Storage.php	(revision 5146)
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Storage.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Storage
+{
+    // maildir and IMAP flags, using IMAP names, where possible to be able to distinguish between IMAP
+    // system flags and other flags
+    const FLAG_PASSED   = 'Passed';
+    const FLAG_SEEN     = '\Seen';
+    const FLAG_ANSWERED = '\Answered';
+    const FLAG_FLAGGED  = '\Flagged';
+    const FLAG_DELETED  = '\Deleted';
+    const FLAG_DRAFT    = '\Draft';
+    const FLAG_RECENT   = '\Recent';
+}
Index: trunk/library/Zend/Mail/Transport/Sendmail.php
===================================================================
--- trunk/library/Zend/Mail/Transport/Sendmail.php	(revision 5146)
+++ trunk/library/Zend/Mail/Transport/Sendmail.php	(revision 5146)
@@ -0,0 +1,220 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Transport
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Sendmail.php 23484 2010-12-10 03:57:59Z mjh_ca $
+ */
+
+
+/**
+ * @see Zend_Mail_Transport_Abstract
+ */
+require_once 'Zend/Mail/Transport/Abstract.php';
+
+
+/**
+ * Class for sending eMails via the PHP internal mail() function
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Transport
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Transport_Sendmail extends Zend_Mail_Transport_Abstract
+{
+    /**
+     * Subject
+     * @var string
+     * @access public
+     */
+    public $subject = null;
+
+
+    /**
+     * Config options for sendmail parameters
+     *
+     * @var string
+     */
+    public $parameters;
+
+    /**
+     * EOL character string
+     * @var string
+     * @access public
+     */
+    public $EOL = PHP_EOL;
+
+    /**
+     * error information
+     * @var string
+     */
+    protected $_errstr;
+
+    /**
+     * Constructor.
+     *
+     * @param  string|array|Zend_Config $parameters OPTIONAL (Default: null)
+     * @return void
+     */
+    public function __construct($parameters = null)
+    {
+        if ($parameters instanceof Zend_Config) {
+            $parameters = $parameters->toArray();
+        }
+
+        if (is_array($parameters)) {
+            $parameters = implode(' ', $parameters);
+        }
+
+        $this->parameters = $parameters;
+    }
+
+
+    /**
+     * Send mail using PHP native mail()
+     *
+     * @access public
+     * @return void
+     * @throws Zend_Mail_Transport_Exception if parameters is set
+     *         but not a string
+     * @throws Zend_Mail_Transport_Exception on mail() failure
+     */
+    public function _sendMail()
+    {
+        if ($this->parameters === null) {
+            set_error_handler(array($this, '_handleMailErrors'));
+            $result = mail(
+                $this->recipients,
+                $this->_mail->getSubject(),
+                $this->body,
+                $this->header);
+            restore_error_handler();
+        } else {
+            if(!is_string($this->parameters)) {
+                /**
+                 * @see Zend_Mail_Transport_Exception
+                 *
+                 * Exception is thrown here because
+                 * $parameters is a public property
+                 */
+                require_once 'Zend/Mail/Transport/Exception.php';
+                throw new Zend_Mail_Transport_Exception(
+                    'Parameters were set but are not a string'
+                );
+            }
+
+            set_error_handler(array($this, '_handleMailErrors'));
+            $result = mail(
+                $this->recipients,
+                $this->_mail->getSubject(),
+                $this->body,
+                $this->header,
+                $this->parameters);
+            restore_error_handler();
+        }
+
+        if ($this->_errstr !== null || !$result) {
+            /**
+             * @see Zend_Mail_Transport_Exception
+             */
+            require_once 'Zend/Mail/Transport/Exception.php';
+            throw new Zend_Mail_Transport_Exception('Unable to send mail. ' . $this->_errstr);
+        }
+    }
+
+
+    /**
+     * Format and fix headers
+     *
+     * mail() uses its $to and $subject arguments to set the To: and Subject:
+     * headers, respectively. This method strips those out as a sanity check to
+     * prevent duplicate header entries.
+     *
+     * @access  protected
+     * @param   array $headers
+     * @return  void
+     * @throws  Zend_Mail_Transport_Exception
+     */
+    protected function _prepareHeaders($headers)
+    {
+        if (!$this->_mail) {
+            /**
+             * @see Zend_Mail_Transport_Exception
+             */
+            require_once 'Zend/Mail/Transport/Exception.php';
+            throw new Zend_Mail_Transport_Exception('_prepareHeaders requires a registered Zend_Mail object');
+        }
+
+        // mail() uses its $to parameter to set the To: header, and the $subject
+        // parameter to set the Subject: header. We need to strip them out.
+        if (0 === strpos(PHP_OS, 'WIN')) {
+            // If the current recipients list is empty, throw an error
+            if (empty($this->recipients)) {
+                /**
+                 * @see Zend_Mail_Transport_Exception
+                 */
+                require_once 'Zend/Mail/Transport/Exception.php';
+                throw new Zend_Mail_Transport_Exception('Missing To addresses');
+            }
+        } else {
+            // All others, simply grab the recipients and unset the To: header
+            if (!isset($headers['To'])) {
+                /**
+                 * @see Zend_Mail_Transport_Exception
+                 */
+                require_once 'Zend/Mail/Transport/Exception.php';
+                throw new Zend_Mail_Transport_Exception('Missing To header');
+            }
+
+            unset($headers['To']['append']);
+            $this->recipients = implode(',', $headers['To']);
+        }
+
+        // Remove recipient header
+        unset($headers['To']);
+
+        // Remove subject header, if present
+        if (isset($headers['Subject'])) {
+            unset($headers['Subject']);
+        }
+
+        // Prepare headers
+        parent::_prepareHeaders($headers);
+
+        // Fix issue with empty blank line ontop when using Sendmail Trnasport
+        $this->header = rtrim($this->header);
+    }
+
+    /**
+     * Temporary error handler for PHP native mail().
+     *
+     * @param int    $errno
+     * @param string $errstr
+     * @param string $errfile
+     * @param string $errline
+     * @param array  $errcontext
+     * @return true
+     */
+    public function _handleMailErrors($errno, $errstr, $errfile = null, $errline = null, array $errcontext = null)
+    {
+        $this->_errstr = $errstr;
+        return true;
+    }
+
+}
Index: trunk/library/Zend/Mail/Transport/Exception.php
===================================================================
--- trunk/library/Zend/Mail/Transport/Exception.php	(revision 5146)
+++ trunk/library/Zend/Mail/Transport/Exception.php	(revision 5146)
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Transport
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mail_Exception
+ */
+require_once 'Zend/Mail/Exception.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Transport
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Transport_Exception extends Zend_Mail_Exception
+{}
+
Index: trunk/library/Zend/Mail/Transport/File.php
===================================================================
--- trunk/library/Zend/Mail/Transport/File.php	(revision 5146)
+++ trunk/library/Zend/Mail/Transport/File.php	(revision 5146)
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Transport
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @see Zend_Mail_Transport_Abstract
+ */
+require_once 'Zend/Mail/Transport/Abstract.php';
+
+
+/**
+ * File transport
+ *
+ * Class for saving outgoing emails in filesystem
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Transport
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Transport_File extends Zend_Mail_Transport_Abstract
+{
+    /**
+     * Target directory for saving sent email messages
+     *
+     * @var string
+     */
+    protected $_path;
+
+    /**
+     * Callback function generating a file name
+     *
+     * @var string|array
+     */
+    protected $_callback;
+
+    /**
+     * Constructor
+     *
+     * @param  array|Zend_Config $options OPTIONAL (Default: null)
+     * @return void
+     */
+    public function __construct($options = null)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (!is_array($options)) {
+            $options = array();
+        }
+
+        // Making sure we have some defaults to work with
+        if (!isset($options['path'])) {
+            $options['path'] = sys_get_temp_dir();
+        }
+        if (!isset($options['callback'])) {
+            $options['callback'] = array($this, 'defaultCallback');
+        }
+
+        $this->setOptions($options);
+    }
+
+    /**
+     * Sets options
+     *
+     * @param  array $options
+     * @return void
+     */
+    public function setOptions(array $options)
+    {
+        if (isset($options['path'])&& is_dir($options['path'])) {
+            $this->_path = $options['path'];
+        }
+        if (isset($options['callback']) && is_callable($options['callback'])) {
+            $this->_callback = $options['callback'];
+        }
+    }
+
+    /**
+     * Saves e-mail message to a file
+     *
+     * @return void
+     * @throws Zend_Mail_Transport_Exception on not writable target directory
+     * @throws Zend_Mail_Transport_Exception on file_put_contents() failure
+     */
+    protected function _sendMail()
+    {
+        $file = $this->_path . DIRECTORY_SEPARATOR . call_user_func($this->_callback, $this);
+
+        if (!is_writable(dirname($file))) {
+            require_once 'Zend/Mail/Transport/Exception.php';
+            throw new Zend_Mail_Transport_Exception(sprintf(
+                'Target directory "%s" does not exist or is not writable',
+                dirname($file)
+            ));
+        }
+
+        $email = $this->header . $this->EOL . $this->body;
+
+        if (!file_put_contents($file, $email)) {
+            require_once 'Zend/Mail/Transport/Exception.php';
+            throw new Zend_Mail_Transport_Exception('Unable to send mail');
+        }
+    }
+
+    /**
+     * Default callback for generating filenames
+     *
+     * @param Zend_Mail_Transport_File File transport instance
+     * @return string
+     */
+    public function defaultCallback($transport)
+    {
+        return 'ZendMail_' . $_SERVER['REQUEST_TIME'] . '_' . mt_rand() . '.tmp';
+    }
+}
Index: trunk/library/Zend/Mail/Transport/Abstract.php
===================================================================
--- trunk/library/Zend/Mail/Transport/Abstract.php	(revision 5146)
+++ trunk/library/Zend/Mail/Transport/Abstract.php	(revision 5146)
@@ -0,0 +1,350 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Transport
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Abstract.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+
+/**
+ * Abstract for sending eMails through different
+ * ways of transport
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Transport
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Mail_Transport_Abstract
+{
+    /**
+     * Mail body
+     * @var string
+     * @access public
+     */
+    public $body = '';
+
+    /**
+     * MIME boundary
+     * @var string
+     * @access public
+     */
+    public $boundary = '';
+
+    /**
+     * Mail header string
+     * @var string
+     * @access public
+     */
+    public $header = '';
+
+    /**
+     * Array of message headers
+     * @var array
+     * @access protected
+     */
+    protected $_headers = array();
+
+    /**
+     * Message is a multipart message
+     * @var boolean
+     * @access protected
+     */
+    protected $_isMultipart = false;
+
+    /**
+     * Zend_Mail object
+     * @var false|Zend_Mail
+     * @access protected
+     */
+    protected $_mail = false;
+
+    /**
+     * Array of message parts
+     * @var array
+     * @access protected
+     */
+    protected $_parts = array();
+
+    /**
+     * Recipients string
+     * @var string
+     * @access public
+     */
+    public $recipients = '';
+
+    /**
+     * EOL character string used by transport
+     * @var string
+     * @access public
+     */
+    public $EOL = "\r\n";
+
+    /**
+     * Send an email independent from the used transport
+     *
+     * The requisite information for the email will be found in the following
+     * properties:
+     *
+     * - {@link $recipients} - list of recipients (string)
+     * - {@link $header} - message header
+     * - {@link $body} - message body
+     */
+    abstract protected function _sendMail();
+
+    /**
+     * Return all mail headers as an array
+     *
+     * If a boundary is given, a multipart header is generated with a
+     * Content-Type of either multipart/alternative or multipart/mixed depending
+     * on the mail parts present in the {@link $_mail Zend_Mail object} present.
+     *
+     * @param string $boundary
+     * @return array
+     */
+    protected function _getHeaders($boundary)
+    {
+        if (null !== $boundary) {
+            // Build multipart mail
+            $type = $this->_mail->getType();
+            if (!$type) {
+                if ($this->_mail->hasAttachments) {
+                    $type = Zend_Mime::MULTIPART_MIXED;
+                } elseif ($this->_mail->getBodyText() && $this->_mail->getBodyHtml()) {
+                    $type = Zend_Mime::MULTIPART_ALTERNATIVE;
+                } else {
+                    $type = Zend_Mime::MULTIPART_MIXED;
+                }
+            }
+
+            $this->_headers['Content-Type'] = array(
+                $type . ';'
+                . $this->EOL
+                . " " . 'boundary="' . $boundary . '"'
+            );
+            $this->boundary = $boundary;
+        }
+
+        $this->_headers['MIME-Version'] = array('1.0');
+
+        return $this->_headers;
+    }
+
+    /**
+     * Prepend header name to header value
+     *
+     * @param string $item
+     * @param string $key
+     * @param string $prefix
+     * @static
+     * @access protected
+     * @return void
+     */
+    protected static function _formatHeader(&$item, $key, $prefix)
+    {
+        $item = $prefix . ': ' . $item;
+    }
+
+    /**
+     * Prepare header string for use in transport
+     *
+     * Prepares and generates {@link $header} based on the headers provided.
+     *
+     * @param mixed $headers
+     * @access protected
+     * @return void
+     * @throws Zend_Mail_Transport_Exception if any header lines exceed 998
+     * characters
+     */
+    protected function _prepareHeaders($headers)
+    {
+        if (!$this->_mail) {
+            /**
+             * @see Zend_Mail_Transport_Exception
+             */
+            require_once 'Zend/Mail/Transport/Exception.php';
+            throw new Zend_Mail_Transport_Exception('Missing Zend_Mail object in _mail property');
+        }
+
+        $this->header = '';
+
+        foreach ($headers as $header => $content) {
+            if (isset($content['append'])) {
+                unset($content['append']);
+                $value = implode(',' . $this->EOL . ' ', $content);
+                $this->header .= $header . ': ' . $value . $this->EOL;
+            } else {
+                array_walk($content, array(get_class($this), '_formatHeader'), $header);
+                $this->header .= implode($this->EOL, $content) . $this->EOL;
+            }
+        }
+
+        // Sanity check on headers -- should not be > 998 characters
+        $sane = true;
+        foreach (explode($this->EOL, $this->header) as $line) {
+            if (strlen(trim($line)) > 998) {
+                $sane = false;
+                break;
+            }
+        }
+        if (!$sane) {
+            /**
+             * @see Zend_Mail_Transport_Exception
+             */
+            require_once 'Zend/Mail/Transport/Exception.php';
+            throw new Zend_Mail_Exception('At least one mail header line is too long');
+        }
+    }
+
+    /**
+     * Generate MIME compliant message from the current configuration
+     *
+     * If both a text and HTML body are present, generates a
+     * multipart/alternative Zend_Mime_Part containing the headers and contents
+     * of each. Otherwise, uses whichever of the text or HTML parts present.
+     *
+     * The content part is then prepended to the list of Zend_Mime_Parts for
+     * this message.
+     *
+     * @return void
+     */
+    protected function _buildBody()
+    {
+        if (($text = $this->_mail->getBodyText())
+            && ($html = $this->_mail->getBodyHtml()))
+        {
+            // Generate unique boundary for multipart/alternative
+            $mime = new Zend_Mime(null);
+            $boundaryLine = $mime->boundaryLine($this->EOL);
+            $boundaryEnd  = $mime->mimeEnd($this->EOL);
+
+            $text->disposition = false;
+            $html->disposition = false;
+
+            $body = $boundaryLine
+                  . $text->getHeaders($this->EOL)
+                  . $this->EOL
+                  . $text->getContent($this->EOL)
+                  . $this->EOL
+                  . $boundaryLine
+                  . $html->getHeaders($this->EOL)
+                  . $this->EOL
+                  . $html->getContent($this->EOL)
+                  . $this->EOL
+                  . $boundaryEnd;
+
+            $mp           = new Zend_Mime_Part($body);
+            $mp->type     = Zend_Mime::MULTIPART_ALTERNATIVE;
+            $mp->boundary = $mime->boundary();
+
+            $this->_isMultipart = true;
+
+            // Ensure first part contains text alternatives
+            array_unshift($this->_parts, $mp);
+
+            // Get headers
+            $this->_headers = $this->_mail->getHeaders();
+            return;
+        }
+
+        // If not multipart, then get the body
+        if (false !== ($body = $this->_mail->getBodyHtml())) {
+            array_unshift($this->_parts, $body);
+        } elseif (false !== ($body = $this->_mail->getBodyText())) {
+            array_unshift($this->_parts, $body);
+        }
+
+        if (!$body) {
+            /**
+             * @see Zend_Mail_Transport_Exception
+             */
+            require_once 'Zend/Mail/Transport/Exception.php';
+            throw new Zend_Mail_Transport_Exception('No body specified');
+        }
+
+        // Get headers
+        $this->_headers = $this->_mail->getHeaders();
+        $headers = $body->getHeadersArray($this->EOL);
+        foreach ($headers as $header) {
+            // Headers in Zend_Mime_Part are kept as arrays with two elements, a
+            // key and a value
+            $this->_headers[$header[0]] = array($header[1]);
+        }
+    }
+
+    /**
+     * Send a mail using this transport
+     *
+     * @param  Zend_Mail $mail
+     * @access public
+     * @return void
+     * @throws Zend_Mail_Transport_Exception if mail is empty
+     */
+    public function send(Zend_Mail $mail)
+    {
+        $this->_isMultipart = false;
+        $this->_mail        = $mail;
+        $this->_parts       = $mail->getParts();
+        $mime               = $mail->getMime();
+
+        // Build body content
+        $this->_buildBody();
+
+        // Determine number of parts and boundary
+        $count    = count($this->_parts);
+        $boundary = null;
+        if ($count < 1) {
+            /**
+             * @see Zend_Mail_Transport_Exception
+             */
+            require_once 'Zend/Mail/Transport/Exception.php';
+            throw new Zend_Mail_Transport_Exception('Empty mail cannot be sent');
+        }
+
+        if ($count > 1) {
+            // Multipart message; create new MIME object and boundary
+            $mime     = new Zend_Mime($this->_mail->getMimeBoundary());
+            $boundary = $mime->boundary();
+        } elseif ($this->_isMultipart) {
+            // multipart/alternative -- grab boundary
+            $boundary = $this->_parts[0]->boundary;
+        }
+
+        // Determine recipients, and prepare headers
+        $this->recipients = implode(',', $mail->getRecipients());
+        $this->_prepareHeaders($this->_getHeaders($boundary));
+
+        // Create message body
+        // This is done so that the same Zend_Mail object can be used in
+        // multiple transports
+        $message = new Zend_Mime_Message();
+        $message->setParts($this->_parts);
+        $message->setMime($mime);
+        $this->body = $message->generateMessage($this->EOL);
+
+        // Send to transport!
+        $this->_sendMail();
+    }
+}
Index: trunk/library/Zend/Mail/Transport/Smtp.php
===================================================================
--- trunk/library/Zend/Mail/Transport/Smtp.php	(revision 5146)
+++ trunk/library/Zend/Mail/Transport/Smtp.php	(revision 5146)
@@ -0,0 +1,243 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Transport
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Smtp.php 23484 2010-12-10 03:57:59Z mjh_ca $
+ */
+
+
+/**
+ * @see Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+/**
+ * @see Zend_Mail_Protocol_Smtp
+ */
+require_once 'Zend/Mail/Protocol/Smtp.php';
+
+/**
+ * @see Zend_Mail_Transport_Abstract
+ */
+require_once 'Zend/Mail/Transport/Abstract.php';
+
+
+/**
+ * SMTP connection object
+ *
+ * Loads an instance of Zend_Mail_Protocol_Smtp and forwards smtp transactions
+ *
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Transport
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Mail_Transport_Smtp extends Zend_Mail_Transport_Abstract
+{
+    /**
+     * EOL character string used by transport
+     * @var string
+     * @access public
+     */
+    public $EOL = "\n";
+
+    /**
+     * Remote smtp hostname or i.p.
+     *
+     * @var string
+     */
+    protected $_host;
+
+
+    /**
+     * Port number
+     *
+     * @var integer|null
+     */
+    protected $_port;
+
+
+    /**
+     * Local client hostname or i.p.
+     *
+     * @var string
+     */
+    protected $_name = 'localhost';
+
+
+    /**
+     * Authentication type OPTIONAL
+     *
+     * @var string
+     */
+    protected $_auth;
+
+
+    /**
+     * Config options for authentication
+     *
+     * @var array
+     */
+    protected $_config;
+
+
+    /**
+     * Instance of Zend_Mail_Protocol_Smtp
+     *
+     * @var Zend_Mail_Protocol_Smtp
+     */
+    protected $_connection;
+
+
+    /**
+     * Constructor.
+     *
+     * @param  string $host OPTIONAL (Default: 127.0.0.1)
+     * @param  array|null $config OPTIONAL (Default: null)
+     * @return void
+     *
+     * @todo Someone please make this compatible
+     *       with the SendMail transport class.
+     */
+    public function __construct($host = '127.0.0.1', Array $config = array())
+    {
+        if (isset($config['name'])) {
+            $this->_name = $config['name'];
+        }
+        if (isset($config['port'])) {
+            $this->_port = $config['port'];
+        }
+        if (isset($config['auth'])) {
+            $this->_auth = $config['auth'];
+        }
+
+        $this->_host = $host;
+        $this->_config = $config;
+    }
+
+
+    /**
+     * Class destructor to ensure all open connections are closed
+     *
+     * @return void
+     */
+    public function __destruct()
+    {
+        if ($this->_connection instanceof Zend_Mail_Protocol_Smtp) {
+            try {
+                $this->_connection->quit();
+            } catch (Zend_Mail_Protocol_Exception $e) {
+                // ignore
+            }
+            $this->_connection->disconnect();
+        }
+    }
+
+
+    /**
+     * Sets the connection protocol instance
+     *
+     * @param Zend_Mail_Protocol_Abstract $client
+     *
+     * @return void
+     */
+    public function setConnection(Zend_Mail_Protocol_Abstract $connection)
+    {
+        $this->_connection = $connection;
+    }
+
+
+    /**
+     * Gets the connection protocol instance
+     *
+     * @return Zend_Mail_Protocol|null
+     */
+    public function getConnection()
+    {
+        return $this->_connection;
+    }
+
+    /**
+     * Send an email via the SMTP connection protocol
+     *
+     * The connection via the protocol adapter is made just-in-time to allow a
+     * developer to add a custom adapter if required before mail is sent.
+     *
+     * @return void
+     * @todo Rename this to sendMail, it's a public method...
+     */
+    public function _sendMail()
+    {
+        // If sending multiple messages per session use existing adapter
+        if (!($this->_connection instanceof Zend_Mail_Protocol_Smtp)) {
+            // Check if authentication is required and determine required class
+            $connectionClass = 'Zend_Mail_Protocol_Smtp';
+            if ($this->_auth) {
+                $connectionClass .= '_Auth_' . ucwords($this->_auth);
+            }
+            if (!class_exists($connectionClass)) {
+                require_once 'Zend/Loader.php';
+                Zend_Loader::loadClass($connectionClass);
+            }
+            $this->setConnection(new $connectionClass($this->_host, $this->_port, $this->_config));
+            $this->_connection->connect();
+            $this->_connection->helo($this->_name);
+        } else {
+            // Reset connection to ensure reliable transaction
+            $this->_connection->rset();
+        }
+
+        // Set sender email address
+        $this->_connection->mail($this->_mail->getReturnPath());
+
+        // Set recipient forward paths
+        foreach ($this->_mail->getRecipients() as $recipient) {
+            $this->_connection->rcpt($recipient);
+        }
+
+        // Issue DATA command to client
+        $this->_connection->data($this->header . Zend_Mime::LINEEND . $this->body);
+    }
+
+    /**
+     * Format and fix headers
+     *
+     * Some SMTP servers do not strip BCC headers. Most clients do it themselves as do we.
+     *
+     * @access  protected
+     * @param   array $headers
+     * @return  void
+     * @throws  Zend_Transport_Exception
+     */
+    protected function _prepareHeaders($headers)
+    {
+        if (!$this->_mail) {
+            /**
+             * @see Zend_Mail_Transport_Exception
+             */
+            require_once 'Zend/Mail/Transport/Exception.php';
+            throw new Zend_Mail_Transport_Exception('_prepareHeaders requires a registered Zend_Mail object');
+        }
+
+        unset($headers['Bcc']);
+
+        // Prepare headers
+        parent::_prepareHeaders($headers);
+    }
+}
Index: trunk/library/Zend/Loader/Exception.php
===================================================================
--- trunk/library/Zend/Loader/Exception.php	(revision 5146)
+++ trunk/library/Zend/Loader/Exception.php	(revision 5146)
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Loader
+ * @uses       Zend_Exception
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Loader_Exception extends Zend_Exception
+{}
Index: trunk/library/Zend/Loader/Autoloader.php
===================================================================
--- trunk/library/Zend/Loader/Autoloader.php	(revision 5146)
+++ trunk/library/Zend/Loader/Autoloader.php	(revision 5146)
@@ -0,0 +1,582 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage Autoloader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @version    $Id: Autoloader.php 23161 2010-10-19 16:08:36Z matthew $
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/** Zend_Loader */
+require_once 'Zend/Loader.php';
+
+/**
+ * Autoloader stack and namespace autoloader
+ *
+ * @uses       Zend_Loader_Autoloader
+ * @package    Zend_Loader
+ * @subpackage Autoloader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Loader_Autoloader
+{
+    /**
+     * @var Zend_Loader_Autoloader Singleton instance
+     */
+    protected static $_instance;
+
+    /**
+     * @var array Concrete autoloader callback implementations
+     */
+    protected $_autoloaders = array();
+
+    /**
+     * @var array Default autoloader callback
+     */
+    protected $_defaultAutoloader = array('Zend_Loader', 'loadClass');
+
+    /**
+     * @var bool Whether or not to act as a fallback autoloader
+     */
+    protected $_fallbackAutoloader = false;
+
+    /**
+     * @var array Callback for internal autoloader implementation
+     */
+    protected $_internalAutoloader;
+
+    /**
+     * @var array Supported namespaces 'Zend' and 'ZendX' by default.
+     */
+    protected $_namespaces = array(
+        'Zend_'  => true,
+        'ZendX_' => true,
+    );
+
+    /**
+     * @var array Namespace-specific autoloaders
+     */
+    protected $_namespaceAutoloaders = array();
+
+    /**
+     * @var bool Whether or not to suppress file not found warnings
+     */
+    protected $_suppressNotFoundWarnings = false;
+
+    /**
+     * @var null|string
+     */
+    protected $_zfPath;
+
+    /**
+     * Retrieve singleton instance
+     *
+     * @return Zend_Loader_Autoloader
+     */
+    public static function getInstance()
+    {
+        if (null === self::$_instance) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    /**
+     * Reset the singleton instance
+     *
+     * @return void
+     */
+    public static function resetInstance()
+    {
+        self::$_instance = null;
+    }
+
+    /**
+     * Autoload a class
+     *
+     * @param  string $class
+     * @return bool
+     */
+    public static function autoload($class)
+    {
+        $self = self::getInstance();
+
+        foreach ($self->getClassAutoloaders($class) as $autoloader) {
+            if ($autoloader instanceof Zend_Loader_Autoloader_Interface) {
+                if ($autoloader->autoload($class)) {
+                    return true;
+                }
+            } elseif (is_array($autoloader)) {
+                if (call_user_func($autoloader, $class)) {
+                    return true;
+                }
+            } elseif (is_string($autoloader) || is_callable($autoloader)) {
+                if ($autoloader($class)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Set the default autoloader implementation
+     *
+     * @param  string|array $callback PHP callback
+     * @return void
+     */
+    public function setDefaultAutoloader($callback)
+    {
+        if (!is_callable($callback)) {
+            throw new Zend_Loader_Exception('Invalid callback specified for default autoloader');
+        }
+
+        $this->_defaultAutoloader = $callback;
+        return $this;
+    }
+
+    /**
+     * Retrieve the default autoloader callback
+     *
+     * @return string|array PHP Callback
+     */
+    public function getDefaultAutoloader()
+    {
+        return $this->_defaultAutoloader;
+    }
+
+    /**
+     * Set several autoloader callbacks at once
+     *
+     * @param  array $autoloaders Array of PHP callbacks (or Zend_Loader_Autoloader_Interface implementations) to act as autoloaders
+     * @return Zend_Loader_Autoloader
+     */
+    public function setAutoloaders(array $autoloaders)
+    {
+        $this->_autoloaders = $autoloaders;
+        return $this;
+    }
+
+    /**
+     * Get attached autoloader implementations
+     *
+     * @return array
+     */
+    public function getAutoloaders()
+    {
+        return $this->_autoloaders;
+    }
+
+    /**
+     * Return all autoloaders for a given namespace
+     *
+     * @param  string $namespace
+     * @return array
+     */
+    public function getNamespaceAutoloaders($namespace)
+    {
+        $namespace = (string) $namespace;
+        if (!array_key_exists($namespace, $this->_namespaceAutoloaders)) {
+            return array();
+        }
+        return $this->_namespaceAutoloaders[$namespace];
+    }
+
+    /**
+     * Register a namespace to autoload
+     *
+     * @param  string|array $namespace
+     * @return Zend_Loader_Autoloader
+     */
+    public function registerNamespace($namespace)
+    {
+        if (is_string($namespace)) {
+            $namespace = (array) $namespace;
+        } elseif (!is_array($namespace)) {
+            throw new Zend_Loader_Exception('Invalid namespace provided');
+        }
+
+        foreach ($namespace as $ns) {
+            if (!isset($this->_namespaces[$ns])) {
+                $this->_namespaces[$ns] = true;
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Unload a registered autoload namespace
+     *
+     * @param  string|array $namespace
+     * @return Zend_Loader_Autoloader
+     */
+    public function unregisterNamespace($namespace)
+    {
+        if (is_string($namespace)) {
+            $namespace = (array) $namespace;
+        } elseif (!is_array($namespace)) {
+            throw new Zend_Loader_Exception('Invalid namespace provided');
+        }
+
+        foreach ($namespace as $ns) {
+            if (isset($this->_namespaces[$ns])) {
+                unset($this->_namespaces[$ns]);
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Get a list of registered autoload namespaces
+     *
+     * @return array
+     */
+    public function getRegisteredNamespaces()
+    {
+        return array_keys($this->_namespaces);
+    }
+
+    public function setZfPath($spec, $version = 'latest')
+    {
+        $path = $spec;
+        if (is_array($spec)) {
+            if (!isset($spec['path'])) {
+                throw new Zend_Loader_Exception('No path specified for ZF');
+            }
+            $path = $spec['path'];
+            if (isset($spec['version'])) {
+                $version = $spec['version'];
+            }
+        }
+
+        $this->_zfPath = $this->_getVersionPath($path, $version);
+        set_include_path(implode(PATH_SEPARATOR, array(
+            $this->_zfPath,
+            get_include_path(),
+        )));
+        return $this;
+    }
+
+    public function getZfPath()
+    {
+        return $this->_zfPath;
+    }
+
+    /**
+     * Get or set the value of the "suppress not found warnings" flag
+     *
+     * @param  null|bool $flag
+     * @return bool|Zend_Loader_Autoloader Returns boolean if no argument is passed, object instance otherwise
+     */
+    public function suppressNotFoundWarnings($flag = null)
+    {
+        if (null === $flag) {
+            return $this->_suppressNotFoundWarnings;
+        }
+        $this->_suppressNotFoundWarnings = (bool) $flag;
+        return $this;
+    }
+
+    /**
+     * Indicate whether or not this autoloader should be a fallback autoloader
+     *
+     * @param  bool $flag
+     * @return Zend_Loader_Autoloader
+     */
+    public function setFallbackAutoloader($flag)
+    {
+        $this->_fallbackAutoloader = (bool) $flag;
+        return $this;
+    }
+
+    /**
+     * Is this instance acting as a fallback autoloader?
+     *
+     * @return bool
+     */
+    public function isFallbackAutoloader()
+    {
+        return $this->_fallbackAutoloader;
+    }
+
+    /**
+     * Get autoloaders to use when matching class
+     *
+     * Determines if the class matches a registered namespace, and, if so,
+     * returns only the autoloaders for that namespace. Otherwise, it returns
+     * all non-namespaced autoloaders.
+     *
+     * @param  string $class
+     * @return array Array of autoloaders to use
+     */
+    public function getClassAutoloaders($class)
+    {
+        $namespace   = false;
+        $autoloaders = array();
+
+        // Add concrete namespaced autoloaders
+        foreach (array_keys($this->_namespaceAutoloaders) as $ns) {
+            if ('' == $ns) {
+                continue;
+            }
+            if (0 === strpos($class, $ns)) {
+                $namespace   = $ns;
+                $autoloaders = $autoloaders + $this->getNamespaceAutoloaders($ns);
+                break;
+            }
+        }
+
+        // Add internal namespaced autoloader
+        foreach ($this->getRegisteredNamespaces() as $ns) {
+            if (0 === strpos($class, $ns)) {
+                $namespace     = $ns;
+                $autoloaders[] = $this->_internalAutoloader;
+                break;
+            }
+        }
+
+        // Add non-namespaced autoloaders
+        $autoloaders = $autoloaders + $this->getNamespaceAutoloaders('');
+
+        // Add fallback autoloader
+        if (!$namespace && $this->isFallbackAutoloader()) {
+            $autoloaders[] = $this->_internalAutoloader;
+        }
+
+        return $autoloaders;
+    }
+
+    /**
+     * Add an autoloader to the beginning of the stack
+     *
+     * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
+     * @param  string|array $namespace Specific namespace(s) under which to register callback
+     * @return Zend_Loader_Autoloader
+     */
+    public function unshiftAutoloader($callback, $namespace = '')
+    {
+        $autoloaders = $this->getAutoloaders();
+        array_unshift($autoloaders, $callback);
+        $this->setAutoloaders($autoloaders);
+
+        $namespace = (array) $namespace;
+        foreach ($namespace as $ns) {
+            $autoloaders = $this->getNamespaceAutoloaders($ns);
+            array_unshift($autoloaders, $callback);
+            $this->_setNamespaceAutoloaders($autoloaders, $ns);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Append an autoloader to the autoloader stack
+     *
+     * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
+     * @param  string|array $namespace Specific namespace(s) under which to register callback
+     * @return Zend_Loader_Autoloader
+     */
+    public function pushAutoloader($callback, $namespace = '')
+    {
+        $autoloaders = $this->getAutoloaders();
+        array_push($autoloaders, $callback);
+        $this->setAutoloaders($autoloaders);
+
+        $namespace = (array) $namespace;
+        foreach ($namespace as $ns) {
+            $autoloaders = $this->getNamespaceAutoloaders($ns);
+            array_push($autoloaders, $callback);
+            $this->_setNamespaceAutoloaders($autoloaders, $ns);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Remove an autoloader from the autoloader stack
+     *
+     * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
+     * @param  null|string|array $namespace Specific namespace(s) from which to remove autoloader
+     * @return Zend_Loader_Autoloader
+     */
+    public function removeAutoloader($callback, $namespace = null)
+    {
+        if (null === $namespace) {
+            $autoloaders = $this->getAutoloaders();
+            if (false !== ($index = array_search($callback, $autoloaders, true))) {
+                unset($autoloaders[$index]);
+                $this->setAutoloaders($autoloaders);
+            }
+
+            foreach ($this->_namespaceAutoloaders as $ns => $autoloaders) {
+                if (false !== ($index = array_search($callback, $autoloaders, true))) {
+                    unset($autoloaders[$index]);
+                    $this->_setNamespaceAutoloaders($autoloaders, $ns);
+                }
+            }
+        } else {
+            $namespace = (array) $namespace;
+            foreach ($namespace as $ns) {
+                $autoloaders = $this->getNamespaceAutoloaders($ns);
+                if (false !== ($index = array_search($callback, $autoloaders, true))) {
+                    unset($autoloaders[$index]);
+                    $this->_setNamespaceAutoloaders($autoloaders, $ns);
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Constructor
+     *
+     * Registers instance with spl_autoload stack
+     *
+     * @return void
+     */
+    protected function __construct()
+    {
+        spl_autoload_register(array(__CLASS__, 'autoload'));
+        $this->_internalAutoloader = array($this, '_autoload');
+    }
+
+    /**
+     * Internal autoloader implementation
+     *
+     * @param  string $class
+     * @return bool
+     */
+    protected function _autoload($class)
+    {
+        $callback = $this->getDefaultAutoloader();
+        try {
+            if ($this->suppressNotFoundWarnings()) {
+                @call_user_func($callback, $class);
+            } else {
+                call_user_func($callback, $class);
+            }
+            return $class;
+        } catch (Zend_Exception $e) {
+            return false;
+        }
+    }
+
+    /**
+     * Set autoloaders for a specific namespace
+     *
+     * @param  array $autoloaders
+     * @param  string $namespace
+     * @return Zend_Loader_Autoloader
+     */
+    protected function _setNamespaceAutoloaders(array $autoloaders, $namespace = '')
+    {
+        $namespace = (string) $namespace;
+        $this->_namespaceAutoloaders[$namespace] = $autoloaders;
+        return $this;
+    }
+
+    /**
+     * Retrieve the filesystem path for the requested ZF version
+     *
+     * @param  string $path
+     * @param  string $version
+     * @return void
+     */
+    protected function _getVersionPath($path, $version)
+    {
+        $type = $this->_getVersionType($version);
+
+        if ($type == 'latest') {
+            $version = 'latest';
+        }
+
+        $availableVersions = $this->_getAvailableVersions($path, $version);
+        if (empty($availableVersions)) {
+            throw new Zend_Loader_Exception('No valid ZF installations discovered');
+        }
+
+        $matchedVersion = array_pop($availableVersions);
+        return $matchedVersion;
+    }
+
+    /**
+     * Retrieve the ZF version type
+     *
+     * @param  string $version
+     * @return string "latest", "major", "minor", or "specific"
+     * @throws Zend_Loader_Exception if version string contains too many dots
+     */
+    protected function _getVersionType($version)
+    {
+        if (strtolower($version) == 'latest') {
+            return 'latest';
+        }
+
+        $parts = explode('.', $version);
+        $count = count($parts);
+        if (1 == $count) {
+            return 'major';
+        }
+        if (2 == $count) {
+            return 'minor';
+        }
+        if (3 < $count) {
+            throw new Zend_Loader_Exception('Invalid version string provided');
+        }
+        return 'specific';
+    }
+
+    /**
+     * Get available versions for the version type requested
+     *
+     * @param  string $path
+     * @param  string $version
+     * @return array
+     */
+    protected function _getAvailableVersions($path, $version)
+    {
+        if (!is_dir($path)) {
+            throw new Zend_Loader_Exception('Invalid ZF path provided');
+        }
+
+        $path       = rtrim($path, '/');
+        $path       = rtrim($path, '\\');
+        $versionLen = strlen($version);
+        $versions   = array();
+        $dirs       = glob("$path/*", GLOB_ONLYDIR);
+        foreach ((array) $dirs as $dir) {
+            $dirName = substr($dir, strlen($path) + 1);
+            if (!preg_match('/^(?:ZendFramework-)?(\d+\.\d+\.\d+((a|b|pl|pr|p|rc)\d+)?)(?:-minimal)?$/i', $dirName, $matches)) {
+                continue;
+            }
+
+            $matchedVersion = $matches[1];
+
+            if (('latest' == $version)
+                || ((strlen($matchedVersion) >= $versionLen)
+                    && (0 === strpos($matchedVersion, $version)))
+            ) {
+                $versions[$matchedVersion] = $dir . '/library';
+            }
+        }
+
+        uksort($versions, 'version_compare');
+        return $versions;
+    }
+}
Index: trunk/library/Zend/Loader/PluginLoader/Exception.php
===================================================================
--- trunk/library/Zend/Loader/PluginLoader/Exception.php	(revision 5146)
+++ trunk/library/Zend/Loader/PluginLoader/Exception.php	(revision 5146)
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Loader_Exception
+ */
+require_once 'Zend/Loader/Exception.php';
+
+/**
+ * Plugin class loader exceptions
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Loader_PluginLoader_Exception extends Zend_Loader_Exception
+{
+}
Index: trunk/library/Zend/Loader/PluginLoader/Interface.php
===================================================================
--- trunk/library/Zend/Loader/PluginLoader/Interface.php	(revision 5146)
+++ trunk/library/Zend/Loader/PluginLoader/Interface.php	(revision 5146)
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Plugin class loader interface
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Loader_PluginLoader_Interface
+{
+    /**
+     * Add prefixed paths to the registry of paths
+     *
+     * @param string $prefix
+     * @param string $path
+     * @return Zend_Loader_PluginLoader
+     */
+    public function addPrefixPath($prefix, $path);
+
+    /**
+     * Remove a prefix (or prefixed-path) from the registry
+     *
+     * @param string $prefix
+     * @param string $path OPTIONAL
+     * @return Zend_Loader_PluginLoader
+     */
+    public function removePrefixPath($prefix, $path = null);
+
+    /**
+     * Whether or not a Helper by a specific name
+     *
+     * @param string $name
+     * @return Zend_Loader_PluginLoader
+     */
+    public function isLoaded($name);
+
+    /**
+     * Return full class name for a named helper
+     *
+     * @param string $name
+     * @return string
+     */
+    public function getClassName($name);
+
+    /**
+     * Load a helper via the name provided
+     *
+     * @param string $name
+     * @return string
+     */
+    public function load($name);
+}
Index: trunk/library/Zend/Loader/PluginLoader.php
===================================================================
--- trunk/library/Zend/Loader/PluginLoader.php	(revision 5146)
+++ trunk/library/Zend/Loader/PluginLoader.php	(revision 5146)
@@ -0,0 +1,484 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: PluginLoader.php 22603 2010-07-17 00:02:10Z ramon $
+ */
+
+/** Zend_Loader_PluginLoader_Interface */
+require_once 'Zend/Loader/PluginLoader/Interface.php';
+
+/** Zend_Loader */
+require_once 'Zend/Loader.php';
+
+/**
+ * Generic plugin class loader
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Loader_PluginLoader implements Zend_Loader_PluginLoader_Interface
+{
+    /**
+     * Class map cache file
+     * @var string
+     */
+    protected static $_includeFileCache;
+
+    /**
+     * Instance loaded plugin paths
+     *
+     * @var array
+     */
+    protected $_loadedPluginPaths = array();
+
+    /**
+     * Instance loaded plugins
+     *
+     * @var array
+     */
+    protected $_loadedPlugins = array();
+
+    /**
+     * Instance registry property
+     *
+     * @var array
+     */
+    protected $_prefixToPaths = array();
+
+    /**
+     * Statically loaded plugin path mappings
+     *
+     * @var array
+     */
+    protected static $_staticLoadedPluginPaths = array();
+
+    /**
+     * Statically loaded plugins
+     *
+     * @var array
+     */
+    protected static $_staticLoadedPlugins = array();
+
+    /**
+     * Static registry property
+     *
+     * @var array
+     */
+    protected static $_staticPrefixToPaths = array();
+
+    /**
+     * Whether to use a statically named registry for loading plugins
+     *
+     * @var string|null
+     */
+    protected $_useStaticRegistry = null;
+
+    /**
+     * Constructor
+     *
+     * @param array $prefixToPaths
+     * @param string $staticRegistryName OPTIONAL
+     */
+    public function __construct(Array $prefixToPaths = array(), $staticRegistryName = null)
+    {
+        if (is_string($staticRegistryName) && !empty($staticRegistryName)) {
+            $this->_useStaticRegistry = $staticRegistryName;
+            if(!isset(self::$_staticPrefixToPaths[$staticRegistryName])) {
+                self::$_staticPrefixToPaths[$staticRegistryName] = array();
+            }
+            if(!isset(self::$_staticLoadedPlugins[$staticRegistryName])) {
+                self::$_staticLoadedPlugins[$staticRegistryName] = array();
+            }
+        }
+
+        foreach ($prefixToPaths as $prefix => $path) {
+            $this->addPrefixPath($prefix, $path);
+        }
+    }
+
+    /**
+     * Format prefix for internal use
+     *
+     * @param  string $prefix
+     * @return string
+     */
+    protected function _formatPrefix($prefix)
+    {
+        if($prefix == "") {
+            return $prefix;
+        }
+
+        $last = strlen($prefix) - 1;
+        if ($prefix{$last} == '\\') {
+            return $prefix;
+        }
+
+        return rtrim($prefix, '_') . '_';
+    }
+
+    /**
+     * Add prefixed paths to the registry of paths
+     *
+     * @param string $prefix
+     * @param string $path
+     * @return Zend_Loader_PluginLoader
+     */
+    public function addPrefixPath($prefix, $path)
+    {
+        if (!is_string($prefix) || !is_string($path)) {
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception('Zend_Loader_PluginLoader::addPrefixPath() method only takes strings for prefix and path.');
+        }
+
+        $prefix = $this->_formatPrefix($prefix);
+        $path   = rtrim($path, '/\\') . '/';
+
+        if ($this->_useStaticRegistry) {
+            self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix][] = $path;
+        } else {
+            if (!isset($this->_prefixToPaths[$prefix])) {
+                $this->_prefixToPaths[$prefix] = array();
+            }
+            if (!in_array($path, $this->_prefixToPaths[$prefix])) {
+                $this->_prefixToPaths[$prefix][] = $path;
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Get path stack
+     *
+     * @param  string $prefix
+     * @return false|array False if prefix does not exist, array otherwise
+     */
+    public function getPaths($prefix = null)
+    {
+        if ((null !== $prefix) && is_string($prefix)) {
+            $prefix = $this->_formatPrefix($prefix);
+            if ($this->_useStaticRegistry) {
+                if (isset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix])) {
+                    return self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix];
+                }
+
+                return false;
+            }
+
+            if (isset($this->_prefixToPaths[$prefix])) {
+                return $this->_prefixToPaths[$prefix];
+            }
+
+            return false;
+        }
+
+        if ($this->_useStaticRegistry) {
+            return self::$_staticPrefixToPaths[$this->_useStaticRegistry];
+        }
+
+        return $this->_prefixToPaths;
+    }
+
+    /**
+     * Clear path stack
+     *
+     * @param  string $prefix
+     * @return bool False only if $prefix does not exist
+     */
+    public function clearPaths($prefix = null)
+    {
+        if ((null !== $prefix) && is_string($prefix)) {
+            $prefix = $this->_formatPrefix($prefix);
+            if ($this->_useStaticRegistry) {
+                if (isset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix])) {
+                    unset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix]);
+                    return true;
+                }
+
+                return false;
+            }
+
+            if (isset($this->_prefixToPaths[$prefix])) {
+                unset($this->_prefixToPaths[$prefix]);
+                return true;
+            }
+
+            return false;
+        }
+
+        if ($this->_useStaticRegistry) {
+            self::$_staticPrefixToPaths[$this->_useStaticRegistry] = array();
+        } else {
+            $this->_prefixToPaths = array();
+        }
+
+        return true;
+    }
+
+    /**
+     * Remove a prefix (or prefixed-path) from the registry
+     *
+     * @param string $prefix
+     * @param string $path OPTIONAL
+     * @return Zend_Loader_PluginLoader
+     */
+    public function removePrefixPath($prefix, $path = null)
+    {
+        $prefix = $this->_formatPrefix($prefix);
+        if ($this->_useStaticRegistry) {
+            $registry =& self::$_staticPrefixToPaths[$this->_useStaticRegistry];
+        } else {
+            $registry =& $this->_prefixToPaths;
+        }
+
+        if (!isset($registry[$prefix])) {
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception('Prefix ' . $prefix . ' was not found in the PluginLoader.');
+        }
+
+        if ($path != null) {
+            $pos = array_search($path, $registry[$prefix]);
+            if (false === $pos) {
+                require_once 'Zend/Loader/PluginLoader/Exception.php';
+                throw new Zend_Loader_PluginLoader_Exception('Prefix ' . $prefix . ' / Path ' . $path . ' was not found in the PluginLoader.');
+            }
+            unset($registry[$prefix][$pos]);
+        } else {
+            unset($registry[$prefix]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Normalize plugin name
+     *
+     * @param  string $name
+     * @return string
+     */
+    protected function _formatName($name)
+    {
+        return ucfirst((string) $name);
+    }
+
+    /**
+     * Whether or not a Plugin by a specific name is loaded
+     *
+     * @param string $name
+     * @return Zend_Loader_PluginLoader
+     */
+    public function isLoaded($name)
+    {
+        $name = $this->_formatName($name);
+        if ($this->_useStaticRegistry) {
+            return isset(self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name]);
+        }
+
+        return isset($this->_loadedPlugins[$name]);
+    }
+
+    /**
+     * Return full class name for a named plugin
+     *
+     * @param string $name
+     * @return string|false False if class not found, class name otherwise
+     */
+    public function getClassName($name)
+    {
+        $name = $this->_formatName($name);
+        if ($this->_useStaticRegistry
+            && isset(self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name])
+        ) {
+            return self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name];
+        } elseif (isset($this->_loadedPlugins[$name])) {
+            return $this->_loadedPlugins[$name];
+        }
+
+        return false;
+    }
+
+    /**
+     * Get path to plugin class
+     *
+     * @param  mixed $name
+     * @return string|false False if not found
+     */
+    public function getClassPath($name)
+    {
+        $name = $this->_formatName($name);
+        if ($this->_useStaticRegistry
+            && !empty(self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name])
+        ) {
+            return self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name];
+        } elseif (!empty($this->_loadedPluginPaths[$name])) {
+            return $this->_loadedPluginPaths[$name];
+        }
+
+        if ($this->isLoaded($name)) {
+            $class = $this->getClassName($name);
+            $r     = new ReflectionClass($class);
+            $path  = $r->getFileName();
+            if ($this->_useStaticRegistry) {
+                self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name] = $path;
+            } else {
+                $this->_loadedPluginPaths[$name] = $path;
+            }
+            return $path;
+        }
+
+        return false;
+    }
+
+    /**
+     * Load a plugin via the name provided
+     *
+     * @param  string $name
+     * @param  bool $throwExceptions Whether or not to throw exceptions if the
+     * class is not resolved
+     * @return string|false Class name of loaded class; false if $throwExceptions
+     * if false and no class found
+     * @throws Zend_Loader_Exception if class not found
+     */
+    public function load($name, $throwExceptions = true)
+    {
+        $name = $this->_formatName($name);
+        if ($this->isLoaded($name)) {
+            return $this->getClassName($name);
+        }
+
+        if ($this->_useStaticRegistry) {
+            $registry = self::$_staticPrefixToPaths[$this->_useStaticRegistry];
+        } else {
+            $registry = $this->_prefixToPaths;
+        }
+
+        $registry  = array_reverse($registry, true);
+        $found     = false;
+        $classFile = str_replace('_', DIRECTORY_SEPARATOR, $name) . '.php';
+        $incFile   = self::getIncludeFileCache();
+        foreach ($registry as $prefix => $paths) {
+            $className = $prefix . $name;
+
+            if (class_exists($className, false)) {
+                $found = true;
+                break;
+            }
+
+            $paths     = array_reverse($paths, true);
+
+            foreach ($paths as $path) {
+                $loadFile = $path . $classFile;
+                if (Zend_Loader::isReadable($loadFile)) {
+                    include_once $loadFile;
+                    if (class_exists($className, false)) {
+                        if (null !== $incFile) {
+                            self::_appendIncFile($loadFile);
+                        }
+                        $found = true;
+                        break 2;
+                    }
+                }
+            }
+        }
+
+        if (!$found) {
+            if (!$throwExceptions) {
+                return false;
+            }
+
+            $message = "Plugin by name '$name' was not found in the registry; used paths:";
+            foreach ($registry as $prefix => $paths) {
+                $message .= "\n$prefix: " . implode(PATH_SEPARATOR, $paths);
+            }
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception($message);
+       }
+
+        if ($this->_useStaticRegistry) {
+            self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name]     = $className;
+        } else {
+            $this->_loadedPlugins[$name]     = $className;
+        }
+        return $className;
+    }
+
+    /**
+     * Set path to class file cache
+     *
+     * Specify a path to a file that will add include_once statements for each
+     * plugin class loaded. This is an opt-in feature for performance purposes.
+     *
+     * @param  string $file
+     * @return void
+     * @throws Zend_Loader_PluginLoader_Exception if file is not writeable or path does not exist
+     */
+    public static function setIncludeFileCache($file)
+    {
+        if (null === $file) {
+            self::$_includeFileCache = null;
+            return;
+        }
+
+        if (!file_exists($file) && !file_exists(dirname($file))) {
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception('Specified file does not exist and/or directory does not exist (' . $file . ')');
+        }
+        if (file_exists($file) && !is_writable($file)) {
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
+        }
+        if (!file_exists($file) && file_exists(dirname($file)) && !is_writable(dirname($file))) {
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
+        }
+
+        self::$_includeFileCache = $file;
+    }
+
+    /**
+     * Retrieve class file cache path
+     *
+     * @return string|null
+     */
+    public static function getIncludeFileCache()
+    {
+        return self::$_includeFileCache;
+    }
+
+    /**
+     * Append an include_once statement to the class file cache
+     *
+     * @param  string $incFile
+     * @return void
+     */
+    protected static function _appendIncFile($incFile)
+    {
+        if (!file_exists(self::$_includeFileCache)) {
+            $file = '<?php';
+        } else {
+            $file = file_get_contents(self::$_includeFileCache);
+        }
+        if (!strstr($file, $incFile)) {
+            $file .= "\ninclude_once '$incFile';";
+            file_put_contents(self::$_includeFileCache, $file);
+        }
+    }
+}
Index: trunk/library/Zend/Validate/File/MimeType.php
===================================================================
--- trunk/library/Zend/Validate/File/MimeType.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/MimeType.php	(revision 5146)
@@ -0,0 +1,392 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: MimeType.php 22832 2010-08-12 18:02:41Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the mime type of a file
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_MimeType extends Zend_Validate_Abstract
+{
+    /**#@+
+     * @const Error type constants
+     */
+    const FALSE_TYPE   = 'fileMimeTypeFalse';
+    const NOT_DETECTED = 'fileMimeTypeNotDetected';
+    const NOT_READABLE = 'fileMimeTypeNotReadable';
+    /**#@-*/
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::FALSE_TYPE   => "File '%value%' has a false mimetype of '%type%'",
+        self::NOT_DETECTED => "The mimetype of file '%value%' could not be detected",
+        self::NOT_READABLE => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'type' => '_type'
+    );
+
+    /**
+     * @var string
+     */
+    protected $_type;
+
+    /**
+     * Mimetypes
+     *
+     * If null, there is no mimetype
+     *
+     * @var string|null
+     */
+    protected $_mimetype;
+
+    /**
+     * Magicfile to use
+     *
+     * @var string|null
+     */
+    protected $_magicfile;
+
+    /**
+     * Finfo object to use
+     *
+     * @var resource
+     */
+    protected $_finfo;
+
+    /**
+     * If no $_ENV['MAGIC'] is set, try and autodiscover it based on common locations
+     * @var array
+     */
+    protected $_magicFiles = array(
+        '/usr/share/misc/magic',
+        '/usr/share/misc/magic.mime',
+        '/usr/share/misc/magic.mgc',
+        '/usr/share/mime/magic',
+        '/usr/share/mime/magic.mime',
+        '/usr/share/mime/magic.mgc',
+        '/usr/share/file/magic',
+        '/usr/share/file/magic.mime',
+        '/usr/share/file/magic.mgc',
+    );
+
+    /**
+     * Option to allow header check
+     *
+     * @var boolean
+     */
+    protected $_headerCheck = false;
+
+    /**
+     * Sets validator options
+     *
+     * Mimetype to accept
+     *
+     * @param  string|array $mimetype MimeType
+     * @return void
+     */
+    public function __construct($mimetype)
+    {
+        if ($mimetype instanceof Zend_Config) {
+            $mimetype = $mimetype->toArray();
+        } elseif (is_string($mimetype)) {
+            $mimetype = explode(',', $mimetype);
+        } elseif (!is_array($mimetype)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("Invalid options to validator provided");
+        }
+
+        if (isset($mimetype['magicfile'])) {
+            $this->setMagicFile($mimetype['magicfile']);
+            unset($mimetype['magicfile']);
+        }
+
+        if (isset($mimetype['headerCheck'])) {
+            $this->enableHeaderCheck($mimetype['headerCheck']);
+            unset($mimetype['headerCheck']);
+        }
+
+        $this->setMimeType($mimetype);
+    }
+
+    /**
+     * Returns the actual set magicfile
+     *
+     * @return string
+     */
+    public function getMagicFile()
+    {
+        if (null === $this->_magicfile) {
+            if (!empty($_ENV['MAGIC'])) {
+                $this->setMagicFile($_ENV['MAGIC']);
+            } elseif (!(@ini_get("safe_mode") == 'On' || @ini_get("safe_mode") === 1)) {
+                require_once 'Zend/Validate/Exception.php';
+                foreach ($this->_magicFiles as $file) {
+                    // supressing errors which are thrown due to openbase_dir restrictions
+                    try {
+                        $this->setMagicFile($file);
+                        if ($this->_magicfile !== null) {
+                            break;
+                        }
+                    } catch (Zend_Validate_Exception $e) {
+                        // Intentionally, catch and fall through
+                    }
+                }
+            }
+
+            if ($this->_magicfile === null) {
+                $this->_magicfile = false;
+            }
+        }
+
+        return $this->_magicfile;
+    }
+
+    /**
+     * Sets the magicfile to use
+     * if null, the MAGIC constant from php is used
+     * if the MAGIC file is errorous, no file will be set
+     *
+     * @param  string $file
+     * @throws Zend_Validate_Exception When finfo can not read the magicfile
+     * @return Zend_Validate_File_MimeType Provides fluid interface
+     */
+    public function setMagicFile($file)
+    {
+        if (empty($file)) {
+            $this->_magicfile = null;
+        } else if (!(class_exists('finfo', false))) {
+            $this->_magicfile = null;
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Magicfile can not be set. There is no finfo extension installed');
+        } else if (!is_file($file) || !is_readable($file)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('The given magicfile can not be read');
+        } else {
+            $const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME;
+            $this->_finfo = @finfo_open($const, $file);
+            if (empty($this->_finfo)) {
+                $this->_finfo = null;
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception('The given magicfile is not accepted by finfo');
+            } else {
+                $this->_magicfile = $file;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the Header Check option
+     *
+     * @return boolean
+     */
+    public function getHeaderCheck()
+    {
+        return $this->_headerCheck;
+    }
+
+    /**
+     * Defines if the http header should be used
+     * Note that this is unsave and therefor the default value is false
+     *
+     * @param  boolean $checkHeader
+     * @return Zend_Validate_File_MimeType Provides fluid interface
+     */
+    public function enableHeaderCheck($headerCheck = true)
+    {
+        $this->_headerCheck = (boolean) $headerCheck;
+        return $this;
+    }
+
+    /**
+     * Returns the set mimetypes
+     *
+     * @param  boolean $asArray Returns the values as array, when false an concated string is returned
+     * @return string|array
+     */
+    public function getMimeType($asArray = false)
+    {
+        $asArray   = (bool) $asArray;
+        $mimetype = (string) $this->_mimetype;
+        if ($asArray) {
+            $mimetype = explode(',', $mimetype);
+        }
+
+        return $mimetype;
+    }
+
+    /**
+     * Sets the mimetypes
+     *
+     * @param  string|array $mimetype The mimetypes to validate
+     * @return Zend_Validate_File_Extension Provides a fluent interface
+     */
+    public function setMimeType($mimetype)
+    {
+        $this->_mimetype = null;
+        $this->addMimeType($mimetype);
+        return $this;
+    }
+
+    /**
+     * Adds the mimetypes
+     *
+     * @param  string|array $mimetype The mimetypes to add for validation
+     * @return Zend_Validate_File_Extension Provides a fluent interface
+     */
+    public function addMimeType($mimetype)
+    {
+        $mimetypes = $this->getMimeType(true);
+
+        if (is_string($mimetype)) {
+            $mimetype = explode(',', $mimetype);
+        } elseif (!is_array($mimetype)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("Invalid options to validator provided");
+        }
+
+        if (isset($mimetype['magicfile'])) {
+            unset($mimetype['magicfile']);
+        }
+
+        foreach ($mimetype as $content) {
+            if (empty($content) || !is_string($content)) {
+                continue;
+            }
+            $mimetypes[] = trim($content);
+        }
+        $mimetypes = array_unique($mimetypes);
+
+        // Sanity check to ensure no empty values
+        foreach ($mimetypes as $key => $mt) {
+            if (empty($mt)) {
+                unset($mimetypes[$key]);
+            }
+        }
+
+        $this->_mimetype = implode(',', $mimetypes);
+
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if the mimetype of the file matches the given ones. Also parts
+     * of mimetypes can be checked. If you give for example "image" all image
+     * mime types will be accepted like "image/gif", "image/jpeg" and so on.
+     *
+     * @param  string $value Real file to check for mimetype
+     * @param  array  $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        if ($file === null) {
+            $file = array(
+                'type' => null,
+                'name' => $value
+            );
+        }
+
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_READABLE);
+        }
+
+        $mimefile = $this->getMagicFile();
+        if (class_exists('finfo', false)) {
+            $const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME;
+            if (!empty($mimefile) && empty($this->_finfo)) {
+                $this->_finfo = @finfo_open($const, $mimefile);
+            }
+
+            if (empty($this->_finfo)) {
+                $this->_finfo = @finfo_open($const);
+            }
+
+            $this->_type = null;
+            if (!empty($this->_finfo)) {
+                $this->_type = finfo_file($this->_finfo, $value);
+            }
+        }
+
+        if (empty($this->_type) &&
+            (function_exists('mime_content_type') && ini_get('mime_magic.magicfile'))) {
+                $this->_type = mime_content_type($value);
+        }
+
+        if (empty($this->_type) && $this->_headerCheck) {
+            $this->_type = $file['type'];
+        }
+
+        if (empty($this->_type)) {
+            return $this->_throw($file, self::NOT_DETECTED);
+        }
+
+        $mimetype = $this->getMimeType(true);
+        if (in_array($this->_type, $mimetype)) {
+            return true;
+        }
+
+        $types = explode('/', $this->_type);
+        $types = array_merge($types, explode('-', $this->_type));
+        $types = array_merge($types, explode(';', $this->_type));
+        foreach($mimetype as $mime) {
+            if (in_array($mime, $types)) {
+                return true;
+            }
+        }
+
+        return $this->_throw($file, self::FALSE_TYPE);
+    }
+
+    /**
+     * Throws an error of the given type
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        $this->_value = $file['name'];
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/File/Sha1.php
===================================================================
--- trunk/library/Zend/Validate/File/Sha1.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/Sha1.php	(revision 5146)
@@ -0,0 +1,181 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: Sha1.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_File_Hash
+ */
+require_once 'Zend/Validate/File/Hash.php';
+
+/**
+ * Validator for the sha1 hash of given files
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_Sha1 extends Zend_Validate_File_Hash
+{
+    /**
+     * @const string Error constants
+     */
+    const DOES_NOT_MATCH = 'fileSha1DoesNotMatch';
+    const NOT_DETECTED   = 'fileSha1NotDetected';
+    const NOT_FOUND      = 'fileSha1NotFound';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::DOES_NOT_MATCH => "File '%value%' does not match the given sha1 hashes",
+        self::NOT_DETECTED   => "A sha1 hash could not be evaluated for the given file",
+        self::NOT_FOUND      => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * Hash of the file
+     *
+     * @var string
+     */
+    protected $_hash;
+
+    /**
+     * Sets validator options
+     *
+     * $hash is the hash we accept for the file $file
+     *
+     * @param  string|array $options
+     * @return void
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (is_scalar($options)) {
+            $options = array('hash1' => $options);
+        } elseif (!is_array($options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid options to validator provided');
+        }
+
+        $this->setHash($options);
+    }
+
+    /**
+     * Returns all set sha1 hashes
+     *
+     * @return array
+     */
+    public function getSha1()
+    {
+        return $this->getHash();
+    }
+
+    /**
+     * Sets the sha1 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function setHash($options)
+    {
+        if (!is_array($options)) {
+            $options = (array) $options;
+        }
+
+        $options['algorithm'] = 'sha1';
+        parent::setHash($options);
+        return $this;
+    }
+
+    /**
+     * Sets the sha1 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function setSha1($options)
+    {
+        $this->setHash($options);
+        return $this;
+    }
+
+    /**
+     * Adds the sha1 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function addHash($options)
+    {
+        if (!is_array($options)) {
+            $options = (array) $options;
+        }
+
+        $options['algorithm'] = 'sha1';
+        parent::addHash($options);
+        return $this;
+    }
+
+    /**
+     * Adds the sha1 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function addSha1($options)
+    {
+        $this->addHash($options);
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the given file confirms the set hash
+     *
+     * @param  string $value Filename to check for hash
+     * @param  array  $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_FOUND);
+        }
+
+        $hashes = array_unique(array_keys($this->_hash));
+        $filehash = hash_file('sha1', $value);
+        if ($filehash === false) {
+            return $this->_throw($file, self::NOT_DETECTED);
+        }
+
+        foreach ($hashes as $hash) {
+            if ($filehash === $hash) {
+                return true;
+            }
+        }
+
+        return $this->_throw($file, self::DOES_NOT_MATCH);
+    }
+}
Index: trunk/library/Zend/Validate/File/FilesSize.php
===================================================================
--- trunk/library/Zend/Validate/File/FilesSize.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/FilesSize.php	(revision 5146)
@@ -0,0 +1,164 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: FilesSize.php 20454 2010-01-20 22:50:59Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_File_Size
+ */
+require_once 'Zend/Validate/File/Size.php';
+
+/**
+ * Validator for the size of all files which will be validated in sum
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_FilesSize extends Zend_Validate_File_Size
+{
+    /**
+     * @const string Error constants
+     */
+    const TOO_BIG      = 'fileFilesSizeTooBig';
+    const TOO_SMALL    = 'fileFilesSizeTooSmall';
+    const NOT_READABLE = 'fileFilesSizeNotReadable';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::TOO_BIG      => "All files in sum should have a maximum size of '%max%' but '%size%' were detected",
+        self::TOO_SMALL    => "All files in sum should have a minimum size of '%min%' but '%size%' were detected",
+        self::NOT_READABLE => "One or more files can not be read",
+    );
+
+    /**
+     * Internal file array
+     *
+     * @var array
+     */
+    protected $_files;
+
+    /**
+     * Sets validator options
+     *
+     * Min limits the used diskspace for all files, when used with max=null it is the maximum filesize
+     * It also accepts an array with the keys 'min' and 'max'
+     *
+     * @param  integer|array|Zend_Config $options Options for this validator
+     * @return void
+     */
+    public function __construct($options)
+    {
+        $this->_files = array();
+        $this->_setSize(0);
+
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (is_scalar($options)) {
+            $options = array('max' => $options);
+        } elseif (!is_array($options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid options to validator provided');
+        }
+
+        if (1 < func_num_args()) {
+            $argv = func_get_args();
+            array_shift($argv);
+            $options['max'] = array_shift($argv);
+            if (!empty($argv)) {
+                $options['bytestring'] = array_shift($argv);
+            }
+        }
+
+        parent::__construct($options);
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the disk usage of all files is at least min and
+     * not bigger than max (when max is not null).
+     *
+     * @param  string|array $value Real file to check for size
+     * @param  array        $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        require_once 'Zend/Loader.php';
+        if (is_string($value)) {
+            $value = array($value);
+        }
+
+        $min  = $this->getMin(true);
+        $max  = $this->getMax(true);
+        $size = $this->_getSize();
+        foreach ($value as $files) {
+            // Is file readable ?
+            if (!Zend_Loader::isReadable($files)) {
+                $this->_throw($file, self::NOT_READABLE);
+                continue;
+            }
+
+            if (!isset($this->_files[$files])) {
+                $this->_files[$files] = $files;
+            } else {
+                // file already counted... do not count twice
+                continue;
+            }
+
+            // limited to 2GB files
+            $size += @filesize($files);
+            $this->_size = $size;
+            if (($max !== null) && ($max < $size)) {
+                if ($this->useByteString()) {
+                    $this->_max  = $this->_toByteString($max);
+                    $this->_size = $this->_toByteString($size);
+                    $this->_throw($file, self::TOO_BIG);
+                    $this->_max  = $max;
+                    $this->_size = $size;
+                } else {
+                    $this->_throw($file, self::TOO_BIG);
+                }
+            }
+        }
+
+        // Check that aggregate files are >= minimum size
+        if (($min !== null) && ($size < $min)) {
+            if ($this->useByteString()) {
+                $this->_min  = $this->_toByteString($min);
+                $this->_size = $this->_toByteString($size);
+                $this->_throw($file, self::TOO_SMALL);
+                $this->_min  = $min;
+                $this->_size = $size;
+            } else {
+                $this->_throw($file, self::TOO_SMALL);
+            }
+        }
+
+        if (count($this->_messages) > 0) {
+            return false;
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/File/IsCompressed.php
===================================================================
--- trunk/library/Zend/Validate/File/IsCompressed.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/IsCompressed.php	(revision 5146)
@@ -0,0 +1,149 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: IsCompressed.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_File_MimeType
+ */
+require_once 'Zend/Validate/File/MimeType.php';
+
+/**
+ * Validator which checks if the file already exists in the directory
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_IsCompressed extends Zend_Validate_File_MimeType
+{
+    /**
+     * @const string Error constants
+     */
+    const FALSE_TYPE   = 'fileIsCompressedFalseType';
+    const NOT_DETECTED = 'fileIsCompressedNotDetected';
+    const NOT_READABLE = 'fileIsCompressedNotReadable';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::FALSE_TYPE   => "File '%value%' is not compressed, '%type%' detected",
+        self::NOT_DETECTED => "The mimetype of file '%value%' could not be detected",
+        self::NOT_READABLE => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * Sets validator options
+     *
+     * @param  string|array|Zend_Config $compression
+     * @return void
+     */
+    public function __construct($mimetype = array())
+    {
+        if ($mimetype instanceof Zend_Config) {
+            $mimetype = $mimetype->toArray();
+        }
+
+        $temp    = array();
+        // http://de.wikipedia.org/wiki/Liste_von_Dateiendungen
+            $default = array(
+            'application/arj',
+            'application/gnutar',
+            'application/lha',
+            'application/lzx',
+            'application/vnd.ms-cab-compressed',
+            'application/x-ace-compressed',
+            'application/x-arc',
+            'application/x-archive',
+            'application/x-arj',
+            'application/x-bzip',
+            'application/x-bzip2',
+            'application/x-cab-compressed',
+            'application/x-compress',
+            'application/x-compressed',
+            'application/x-cpio',
+            'application/x-debian-package',
+            'application/x-eet',
+            'application/x-gzip',
+            'application/x-java-pack200',
+            'application/x-lha',
+            'application/x-lharc',
+            'application/x-lzh',
+            'application/x-lzma',
+            'application/x-lzx',
+            'application/x-rar',
+            'application/x-sit',
+            'application/x-stuffit',
+            'application/x-tar',
+            'application/zip',
+            'application/zoo',
+            'multipart/x-gzip',
+        );
+
+        if (is_array($mimetype)) {
+            $temp = $mimetype;
+            if (array_key_exists('magicfile', $temp)) {
+                unset($temp['magicfile']);
+            }
+
+            if (array_key_exists('headerCheck', $temp)) {
+                unset($temp['headerCheck']);
+            }
+
+            if (empty($temp)) {
+                $mimetype += $default;
+            }
+        }
+
+        if (empty($mimetype)) {
+            $mimetype = $default;
+        }
+
+        parent::__construct($mimetype);
+    }
+
+    /**
+     * Throws an error of the given type
+     * Duplicates parent method due to OOP Problem with late static binding in PHP 5.2
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        $this->_value = $file['name'];
+        switch($errorType) {
+            case Zend_Validate_File_MimeType::FALSE_TYPE :
+                $errorType = self::FALSE_TYPE;
+                break;
+            case Zend_Validate_File_MimeType::NOT_DETECTED :
+                $errorType = self::NOT_DETECTED;
+                break;
+            case Zend_Validate_File_MimeType::NOT_READABLE :
+                $errorType = self::NOT_READABLE;
+                break;
+        }
+
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/File/NotExists.php
===================================================================
--- trunk/library/Zend/Validate/File/NotExists.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/NotExists.php	(revision 5146)
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: NotExists.php 20352 2010-01-17 17:55:38Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_File_Exists
+ */
+require_once 'Zend/Validate/File/Exists.php';
+
+/**
+ * Validator which checks if the destination file does not exist
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_NotExists extends Zend_Validate_File_Exists
+{
+    /**
+     * @const string Error constants
+     */
+    const DOES_EXIST = 'fileNotExistsDoesExist';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::DOES_EXIST => "File '%value%' exists",
+    );
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the file does not exist in the set destinations
+     *
+     * @param  string  $value Real file to check for
+     * @param  array   $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        $directories = $this->getDirectory(true);
+        if (($file !== null) and (!empty($file['destination']))) {
+            $directories[] = $file['destination'];
+        } else if (!isset($file['name'])) {
+            $file['name'] = $value;
+        }
+
+        foreach ($directories as $directory) {
+            if (empty($directory)) {
+                continue;
+            }
+
+            $check = true;
+            if (file_exists($directory . DIRECTORY_SEPARATOR . $file['name'])) {
+                return $this->_throw($file, self::DOES_EXIST);
+            }
+        }
+
+        if (!isset($check)) {
+            return $this->_throw($file, self::DOES_EXIST);
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/File/ExcludeMimeType.php
===================================================================
--- trunk/library/Zend/Validate/File/ExcludeMimeType.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/ExcludeMimeType.php	(revision 5146)
@@ -0,0 +1,109 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: ExcludeMimeType.php 21935 2010-04-18 16:21:35Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_File_MimeType
+ */
+require_once 'Zend/Validate/File/MimeType.php';
+
+/**
+ * Validator for the mime type of a file
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_ExcludeMimeType extends Zend_Validate_File_MimeType
+{
+    const FALSE_TYPE   = 'fileExcludeMimeTypeFalse';
+    const NOT_DETECTED = 'fileExcludeMimeTypeNotDetected';
+    const NOT_READABLE = 'fileExcludeMimeTypeNotReadable';
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if the mimetype of the file does not matche the given ones. Also parts
+     * of mimetypes can be checked. If you give for example "image" all image
+     * mime types will not be accepted like "image/gif", "image/jpeg" and so on.
+     *
+     * @param  string $value Real file to check for mimetype
+     * @param  array  $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        if ($file === null) {
+            $file = array(
+                'type' => null,
+                'name' => $value
+            );
+        }
+
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_READABLE);
+        }
+
+        $mimefile = $this->getMagicFile();
+        if (class_exists('finfo', false)) {
+            $const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME;
+            if (!empty($mimefile)) {
+                $mime = new finfo($const, $mimefile);
+            } else {
+                $mime = new finfo($const);
+            }
+
+            if (!empty($mime)) {
+                $this->_type = $mime->file($value);
+            }
+            unset($mime);
+        }
+
+        if (empty($this->_type)) {
+            if (function_exists('mime_content_type') && ini_get('mime_magic.magicfile')) {
+                $this->_type = mime_content_type($value);
+            } elseif ($this->_headerCheck) {
+                $this->_type = $file['type'];
+            }
+        }
+
+        if (empty($this->_type)) {
+            return $this->_throw($file, self::NOT_DETECTED);
+        }
+
+        $mimetype = $this->getMimeType(true);
+        if (in_array($this->_type, $mimetype)) {
+            return $this->_throw($file, self::FALSE_TYPE);
+        }
+
+        $types = explode('/', $this->_type);
+        $types = array_merge($types, explode('-', $this->_type));
+        foreach($mimetype as $mime) {
+            if (in_array($mime, $types)) {
+                return $this->_throw($file, self::FALSE_TYPE);
+            }
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/File/Size.php
===================================================================
--- trunk/library/Zend/Validate/File/Size.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/Size.php	(revision 5146)
@@ -0,0 +1,404 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: Size.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the maximum size of a file up to a max of 2GB
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_Size extends Zend_Validate_Abstract
+{
+    /**#@+
+     * @const string Error constants
+     */
+    const TOO_BIG   = 'fileSizeTooBig';
+    const TOO_SMALL = 'fileSizeTooSmall';
+    const NOT_FOUND = 'fileSizeNotFound';
+    /**#@-*/
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::TOO_BIG   => "Maximum allowed size for file '%value%' is '%max%' but '%size%' detected",
+        self::TOO_SMALL => "Minimum expected size for file '%value%' is '%min%' but '%size%' detected",
+        self::NOT_FOUND => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * @var array Error message template variables
+     */
+    protected $_messageVariables = array(
+        'min'  => '_min',
+        'max'  => '_max',
+        'size' => '_size',
+    );
+
+    /**
+     * Minimum filesize
+     * @var integer
+     */
+    protected $_min;
+
+    /**
+     * Maximum filesize
+     *
+     * If null, there is no maximum filesize
+     *
+     * @var integer|null
+     */
+    protected $_max;
+
+    /**
+     * Detected size
+     *
+     * @var integer
+     */
+    protected $_size;
+
+    /**
+     * Use bytestring ?
+     *
+     * @var boolean
+     */
+    protected $_useByteString = true;
+
+    /**
+     * Sets validator options
+     *
+     * If $options is a integer, it will be used as maximum filesize
+     * As Array is accepts the following keys:
+     * 'min': Minimum filesize
+     * 'max': Maximum filesize
+     * 'bytestring': Use bytestring or real size for messages
+     *
+     * @param  integer|array $options Options for the adapter
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (is_string($options) || is_numeric($options)) {
+            $options = array('max' => $options);
+        } elseif (!is_array($options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception ('Invalid options to validator provided');
+        }
+
+        if (1 < func_num_args()) {
+            $argv = func_get_args();
+            array_shift($argv);
+            $options['max'] = array_shift($argv);
+            if (!empty($argv)) {
+                $options['bytestring'] = array_shift($argv);
+            }
+        }
+
+        if (isset($options['bytestring'])) {
+            $this->setUseByteString($options['bytestring']);
+        }
+
+        if (isset($options['min'])) {
+            $this->setMin($options['min']);
+        }
+
+        if (isset($options['max'])) {
+            $this->setMax($options['max']);
+        }
+    }
+
+    /**
+     * Returns the minimum filesize
+     *
+     * @param  boolean $byteString Use bytestring ?
+     * @return integer
+     */
+    public function setUseByteString($byteString = true)
+    {
+        $this->_useByteString = (bool) $byteString;
+        return $this;
+    }
+
+    /**
+     * Will bytestring be used?
+     *
+     * @return boolean
+     */
+    public function useByteString()
+    {
+        return $this->_useByteString;
+    }
+
+    /**
+     * Returns the minimum filesize
+     *
+     * @param  bool $raw Whether or not to force return of the raw value (defaults off)
+     * @return integer|string
+     */
+    public function getMin($raw = false)
+    {
+        $min = $this->_min;
+        if (!$raw && $this->useByteString()) {
+            $min = $this->_toByteString($min);
+        }
+
+        return $min;
+    }
+
+    /**
+     * Sets the minimum filesize
+     *
+     * @param  integer $min The minimum filesize
+     * @throws Zend_Validate_Exception When min is greater than max
+     * @return Zend_Validate_File_Size Provides a fluent interface
+     */
+    public function setMin($min)
+    {
+        if (!is_string($min) and !is_numeric($min)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception ('Invalid options to validator provided');
+        }
+
+        $min = (integer) $this->_fromByteString($min);
+        $max = $this->getMax(true);
+        if (($max !== null) && ($min > $max)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("The minimum must be less than or equal to the maximum filesize, but $min >"
+                                            . " $max");
+        }
+
+        $this->_min = $min;
+        return $this;
+    }
+
+    /**
+     * Returns the maximum filesize
+     *
+     * @param  bool $raw Whether or not to force return of the raw value (defaults off)
+     * @return integer|string
+     */
+    public function getMax($raw = false)
+    {
+        $max = $this->_max;
+        if (!$raw && $this->useByteString()) {
+            $max = $this->_toByteString($max);
+        }
+
+        return $max;
+    }
+
+    /**
+     * Sets the maximum filesize
+     *
+     * @param  integer $max The maximum filesize
+     * @throws Zend_Validate_Exception When max is smaller than min
+     * @return Zend_Validate_StringLength Provides a fluent interface
+     */
+    public function setMax($max)
+    {
+        if (!is_string($max) && !is_numeric($max)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception ('Invalid options to validator provided');
+        }
+
+        $max = (integer) $this->_fromByteString($max);
+        $min = $this->getMin(true);
+        if (($min !== null) && ($max < $min)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("The maximum must be greater than or equal to the minimum filesize, but "
+                                            . "$max < $min");
+        }
+
+        $this->_max = $max;
+        return $this;
+    }
+
+    /**
+     * Retrieve current detected file size
+     *
+     * @return int
+     */
+    protected function _getSize()
+    {
+        return $this->_size;
+    }
+
+    /**
+     * Set current size
+     *
+     * @param  int $size
+     * @return Zend_Validate_File_Size
+     */
+    protected function _setSize($size)
+    {
+        $this->_size = $size;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the filesize of $value is at least min and
+     * not bigger than max (when max is not null).
+     *
+     * @param  string $value Real file to check for size
+     * @param  array  $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_FOUND);
+        }
+
+        // limited to 4GB files
+        $size        = sprintf("%u", @filesize($value));
+        $this->_size = $size;
+
+        // Check to see if it's smaller than min size
+        $min = $this->getMin(true);
+        $max = $this->getMax(true);
+        if (($min !== null) && ($size < $min)) {
+            if ($this->useByteString()) {
+                $this->_min  = $this->_toByteString($min);
+                $this->_size = $this->_toByteString($size);
+                $this->_throw($file, self::TOO_SMALL);
+                $this->_min  = $min;
+                $this->_size = $size;
+            } else {
+                $this->_throw($file, self::TOO_SMALL);
+            }
+        }
+
+        // Check to see if it's larger than max size
+        if (($max !== null) && ($max < $size)) {
+            if ($this->useByteString()) {
+                $this->_max  = $this->_toByteString($max);
+                $this->_size = $this->_toByteString($size);
+                $this->_throw($file, self::TOO_BIG);
+                $this->_max  = $max;
+                $this->_size = $size;
+            } else {
+                $this->_throw($file, self::TOO_BIG);
+            }
+        }
+
+        if (count($this->_messages) > 0) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the formatted size
+     *
+     * @param  integer $size
+     * @return string
+     */
+    protected function _toByteString($size)
+    {
+        $sizes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
+        for ($i=0; $size >= 1024 && $i < 9; $i++) {
+            $size /= 1024;
+        }
+
+        return round($size, 2) . $sizes[$i];
+    }
+
+    /**
+     * Returns the unformatted size
+     *
+     * @param  string $size
+     * @return integer
+     */
+    protected function _fromByteString($size)
+    {
+        if (is_numeric($size)) {
+            return (integer) $size;
+        }
+
+        $type  = trim(substr($size, -2, 1));
+
+        $value = substr($size, 0, -1);
+        if (!is_numeric($value)) {
+            $value = substr($value, 0, -1);
+        }
+
+        switch (strtoupper($type)) {
+            case 'Y':
+                $value *= (1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024);
+                break;
+            case 'Z':
+                $value *= (1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024);
+                break;
+            case 'E':
+                $value *= (1024 * 1024 * 1024 * 1024 * 1024 * 1024);
+                break;
+            case 'P':
+                $value *= (1024 * 1024 * 1024 * 1024 * 1024);
+                break;
+            case 'T':
+                $value *= (1024 * 1024 * 1024 * 1024);
+                break;
+            case 'G':
+                $value *= (1024 * 1024 * 1024);
+                break;
+            case 'M':
+                $value *= (1024 * 1024);
+                break;
+            case 'K':
+                $value *= 1024;
+                break;
+            default:
+                break;
+        }
+
+        return $value;
+    }
+
+    /**
+     * Throws an error of the given type
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        if ($file !== null) {
+            $this->_value = $file['name'];
+        }
+
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/File/Crc32.php
===================================================================
--- trunk/library/Zend/Validate/File/Crc32.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/Crc32.php	(revision 5146)
@@ -0,0 +1,179 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: Crc32.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_File_Hash
+ */
+require_once 'Zend/Validate/File/Hash.php';
+
+/**
+ * Validator for the crc32 hash of given files
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_Crc32 extends Zend_Validate_File_Hash
+{
+    /**
+     * @const string Error constants
+     */
+    const DOES_NOT_MATCH = 'fileCrc32DoesNotMatch';
+    const NOT_DETECTED   = 'fileCrc32NotDetected';
+    const NOT_FOUND      = 'fileCrc32NotFound';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::DOES_NOT_MATCH => "File '%value%' does not match the given crc32 hashes",
+        self::NOT_DETECTED   => "A crc32 hash could not be evaluated for the given file",
+        self::NOT_FOUND      => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * Hash of the file
+     *
+     * @var string
+     */
+    protected $_hash;
+
+    /**
+     * Sets validator options
+     *
+     * @param  string|array|Zend_Config $options
+     * @return void
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (is_scalar($options)) {
+            $options = array('hash1' => $options);
+        } elseif (!is_array($options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid options to validator provided');
+        }
+
+        $this->setCrc32($options);
+    }
+
+    /**
+     * Returns all set crc32 hashes
+     *
+     * @return array
+     */
+    public function getCrc32()
+    {
+        return $this->getHash();
+    }
+
+    /**
+     * Sets the crc32 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function setHash($options)
+    {
+        if (!is_array($options)) {
+            $options = array($options);
+        }
+
+        $options['algorithm'] = 'crc32';
+        parent::setHash($options);
+        return $this;
+    }
+
+    /**
+     * Sets the crc32 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function setCrc32($options)
+    {
+        $this->setHash($options);
+        return $this;
+    }
+
+    /**
+     * Adds the crc32 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function addHash($options)
+    {
+        if (!is_array($options)) {
+            $options = array($options);
+        }
+
+        $options['algorithm'] = 'crc32';
+        parent::addHash($options);
+        return $this;
+    }
+
+    /**
+     * Adds the crc32 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function addCrc32($options)
+    {
+        $this->addHash($options);
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the given file confirms the set hash
+     *
+     * @param  string $value Filename to check for hash
+     * @param  array  $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_FOUND);
+        }
+
+        $hashes = array_unique(array_keys($this->_hash));
+        $filehash = hash_file('crc32', $value);
+        if ($filehash === false) {
+            return $this->_throw($file, self::NOT_DETECTED);
+        }
+
+        foreach($hashes as $hash) {
+            if ($filehash === $hash) {
+                return true;
+            }
+        }
+
+        return $this->_throw($file, self::DOES_NOT_MATCH);
+    }
+}
Index: trunk/library/Zend/Validate/File/Extension.php
===================================================================
--- trunk/library/Zend/Validate/File/Extension.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/Extension.php	(revision 5146)
@@ -0,0 +1,232 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: Extension.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the file extension of a file
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_Extension extends Zend_Validate_Abstract
+{
+    /**
+     * @const string Error constants
+     */
+    const FALSE_EXTENSION = 'fileExtensionFalse';
+    const NOT_FOUND       = 'fileExtensionNotFound';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::FALSE_EXTENSION => "File '%value%' has a false extension",
+        self::NOT_FOUND       => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * Internal list of extensions
+     * @var string
+     */
+    protected $_extension = '';
+
+    /**
+     * Validate case sensitive
+     *
+     * @var boolean
+     */
+    protected $_case = false;
+
+    /**
+     * @var array Error message template variables
+     */
+    protected $_messageVariables = array(
+        'extension' => '_extension'
+    );
+
+    /**
+     * Sets validator options
+     *
+     * @param  string|array|Zend_Config $options
+     * @return void
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        }
+
+        if (1 < func_num_args()) {
+            $case = func_get_arg(1);
+            $this->setCase($case);
+        }
+
+        if (is_array($options) and isset($options['case'])) {
+            $this->setCase($options['case']);
+            unset($options['case']);
+        }
+
+        $this->setExtension($options);
+    }
+
+    /**
+     * Returns the case option
+     *
+     * @return boolean
+     */
+    public function getCase()
+    {
+        return $this->_case;
+    }
+
+    /**
+     * Sets the case to use
+     *
+     * @param  boolean $case
+     * @return Zend_Validate_File_Extension Provides a fluent interface
+     */
+    public function setCase($case)
+    {
+        $this->_case = (boolean) $case;
+        return $this;
+    }
+
+    /**
+     * Returns the set file extension
+     *
+     * @return array
+     */
+    public function getExtension()
+    {
+        $extension = explode(',', $this->_extension);
+
+        return $extension;
+    }
+
+    /**
+     * Sets the file extensions
+     *
+     * @param  string|array $extension The extensions to validate
+     * @return Zend_Validate_File_Extension Provides a fluent interface
+     */
+    public function setExtension($extension)
+    {
+        $this->_extension = null;
+        $this->addExtension($extension);
+        return $this;
+    }
+
+    /**
+     * Adds the file extensions
+     *
+     * @param  string|array $extension The extensions to add for validation
+     * @return Zend_Validate_File_Extension Provides a fluent interface
+     */
+    public function addExtension($extension)
+    {
+        $extensions = $this->getExtension();
+        if (is_string($extension)) {
+            $extension = explode(',', $extension);
+        }
+
+        foreach ($extension as $content) {
+            if (empty($content) || !is_string($content)) {
+                continue;
+            }
+
+            $extensions[] = trim($content);
+        }
+        $extensions = array_unique($extensions);
+
+        // Sanity check to ensure no empty values
+        foreach ($extensions as $key => $ext) {
+            if (empty($ext)) {
+                unset($extensions[$key]);
+            }
+        }
+
+        $this->_extension = implode(',', $extensions);
+
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the fileextension of $value is included in the
+     * set extension list
+     *
+     * @param  string  $value Real file to check for extension
+     * @param  array   $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_FOUND);
+        }
+
+        if ($file !== null) {
+            $info['extension'] = substr($file['name'], strrpos($file['name'], '.') + 1);
+        } else {
+            $info = pathinfo($value);
+        }
+
+        $extensions = $this->getExtension();
+
+        if ($this->_case && (in_array($info['extension'], $extensions))) {
+            return true;
+        } else if (!$this->getCase()) {
+            foreach ($extensions as $extension) {
+                if (strtolower($extension) == strtolower($info['extension'])) {
+                    return true;
+                }
+            }
+        }
+
+        return $this->_throw($file, self::FALSE_EXTENSION);
+    }
+
+    /**
+     * Throws an error of the given type
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        if (null !== $file) {
+            $this->_value = $file['name'];
+        }
+
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/File/ImageSize.php
===================================================================
--- trunk/library/Zend/Validate/File/ImageSize.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/ImageSize.php	(revision 5146)
@@ -0,0 +1,364 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: ImageSize.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the image size of a image file
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_ImageSize extends Zend_Validate_Abstract
+{
+    /**
+     * @const string Error constants
+     */
+    const WIDTH_TOO_BIG    = 'fileImageSizeWidthTooBig';
+    const WIDTH_TOO_SMALL  = 'fileImageSizeWidthTooSmall';
+    const HEIGHT_TOO_BIG   = 'fileImageSizeHeightTooBig';
+    const HEIGHT_TOO_SMALL = 'fileImageSizeHeightTooSmall';
+    const NOT_DETECTED     = 'fileImageSizeNotDetected';
+    const NOT_READABLE     = 'fileImageSizeNotReadable';
+
+    /**
+     * @var array Error message template
+     */
+    protected $_messageTemplates = array(
+        self::WIDTH_TOO_BIG    => "Maximum allowed width for image '%value%' should be '%maxwidth%' but '%width%' detected",
+        self::WIDTH_TOO_SMALL  => "Minimum expected width for image '%value%' should be '%minwidth%' but '%width%' detected",
+        self::HEIGHT_TOO_BIG   => "Maximum allowed height for image '%value%' should be '%maxheight%' but '%height%' detected",
+        self::HEIGHT_TOO_SMALL => "Minimum expected height for image '%value%' should be '%minheight%' but '%height%' detected",
+        self::NOT_DETECTED     => "The size of image '%value%' could not be detected",
+        self::NOT_READABLE     => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * @var array Error message template variables
+     */
+    protected $_messageVariables = array(
+        'minwidth'  => '_minwidth',
+        'maxwidth'  => '_maxwidth',
+        'minheight' => '_minheight',
+        'maxheight' => '_maxheight',
+        'width'     => '_width',
+        'height'    => '_height'
+    );
+
+    /**
+     * Minimum image width
+     *
+     * @var integer
+     */
+    protected $_minwidth;
+
+    /**
+     * Maximum image width
+     *
+     * @var integer
+     */
+    protected $_maxwidth;
+
+    /**
+     * Minimum image height
+     *
+     * @var integer
+     */
+    protected $_minheight;
+
+    /**
+     * Maximum image height
+     *
+     * @var integer
+     */
+    protected $_maxheight;
+
+    /**
+     * Detected width
+     *
+     * @var integer
+     */
+    protected $_width;
+
+    /**
+     * Detected height
+     *
+     * @var integer
+     */
+    protected $_height;
+
+    /**
+     * Sets validator options
+     *
+     * Accepts the following option keys:
+     * - minheight
+     * - minwidth
+     * - maxheight
+     * - maxwidth
+     *
+     * @param  Zend_Config|array $options
+     * @return void
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (1 < func_num_args()) {
+            if (!is_array($options)) {
+                $options = array('minwidth' => $options);
+            }
+            $argv = func_get_args();
+            array_shift($argv);
+            $options['minheight'] = array_shift($argv);
+            if (!empty($argv)) {
+                $options['maxwidth'] = array_shift($argv);
+                if (!empty($argv)) {
+                    $options['maxheight'] = array_shift($argv);
+                }
+            }
+        } else if (!is_array($options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception ('Invalid options to validator provided');
+        }
+
+        if (isset($options['minheight']) || isset($options['minwidth'])) {
+            $this->setImageMin($options);
+        }
+
+        if (isset($options['maxheight']) || isset($options['maxwidth'])) {
+            $this->setImageMax($options);
+        }
+    }
+
+    /**
+     * Returns the set minimum image sizes
+     *
+     * @return array
+     */
+    public function getImageMin()
+    {
+        return array('minwidth' => $this->_minwidth, 'minheight' => $this->_minheight);
+    }
+
+    /**
+     * Returns the set maximum image sizes
+     *
+     * @return array
+     */
+    public function getImageMax()
+    {
+        return array('maxwidth' => $this->_maxwidth, 'maxheight' => $this->_maxheight);
+    }
+
+    /**
+     * Returns the set image width sizes
+     *
+     * @return array
+     */
+    public function getImageWidth()
+    {
+        return array('minwidth' => $this->_minwidth, 'maxwidth' => $this->_maxwidth);
+    }
+
+    /**
+     * Returns the set image height sizes
+     *
+     * @return array
+     */
+    public function getImageHeight()
+    {
+        return array('minheight' => $this->_minheight, 'maxheight' => $this->_maxheight);
+    }
+
+    /**
+     * Sets the minimum image size
+     *
+     * @param  array $options               The minimum image dimensions
+     * @throws Zend_Validate_Exception      When minwidth is greater than maxwidth
+     * @throws Zend_Validate_Exception      When minheight is greater than maxheight
+     * @return Zend_Validate_File_ImageSize Provides a fluent interface
+     */
+    public function setImageMin($options)
+    {
+        if (isset($options['minwidth'])) {
+            if (($this->_maxwidth !== null) and ($options['minwidth'] > $this->_maxwidth)) {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception("The minimum image width must be less than or equal to the "
+                    . " maximum image width, but {$options['minwidth']} > {$this->_maxwidth}");
+            }
+        }
+
+        if (isset($options['maxheight'])) {
+            if (($this->_maxheight !== null) and ($options['minheight'] > $this->_maxheight)) {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception("The minimum image height must be less than or equal to the "
+                    . " maximum image height, but {$options['minheight']} > {$this->_maxheight}");
+            }
+        }
+
+        if (isset($options['minwidth'])) {
+            $this->_minwidth  = (int) $options['minwidth'];
+        }
+
+        if (isset($options['minheight'])) {
+            $this->_minheight = (int) $options['minheight'];
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets the maximum image size
+     *
+     * @param  array $options          The maximum image dimensions
+     * @throws Zend_Validate_Exception When maxwidth is smaller than minwidth
+     * @throws Zend_Validate_Exception When maxheight is smaller than minheight
+     * @return Zend_Validate_StringLength Provides a fluent interface
+     */
+    public function setImageMax($options)
+    {
+        if (isset($options['maxwidth'])) {
+            if (($this->_minwidth !== null) and ($options['maxwidth'] < $this->_minwidth)) {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception("The maximum image width must be greater than or equal to the "
+                    . "minimum image width, but {$options['maxwidth']} < {$this->_minwidth}");
+            }
+        }
+
+        if (isset($options['maxheight'])) {
+            if (($this->_minheight !== null) and ($options['maxheight'] < $this->_minheight)) {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception("The maximum image height must be greater than or equal to the "
+                    . "minimum image height, but {$options['maxheight']} < {$this->_minwidth}");
+            }
+        }
+
+        if (isset($options['maxwidth'])) {
+            $this->_maxwidth  = (int) $options['maxwidth'];
+        }
+
+        if (isset($options['maxheight'])) {
+            $this->_maxheight = (int) $options['maxheight'];
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets the mimimum and maximum image width
+     *
+     * @param  array $options               The image width dimensions
+     * @return Zend_Validate_File_ImageSize Provides a fluent interface
+     */
+    public function setImageWidth($options)
+    {
+        $this->setImageMin($options);
+        $this->setImageMax($options);
+
+        return $this;
+    }
+
+    /**
+     * Sets the mimimum and maximum image height
+     *
+     * @param  array $options               The image height dimensions
+     * @return Zend_Validate_File_ImageSize Provides a fluent interface
+     */
+    public function setImageHeight($options)
+    {
+        $this->setImageMin($options);
+        $this->setImageMax($options);
+
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the imagesize of $value is at least min and
+     * not bigger than max
+     *
+     * @param  string $value Real file to check for image size
+     * @param  array  $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_READABLE);
+        }
+
+        $size = @getimagesize($value);
+        $this->_setValue($file);
+
+        if (empty($size) or ($size[0] === 0) or ($size[1] === 0)) {
+            return $this->_throw($file, self::NOT_DETECTED);
+        }
+
+        $this->_width  = $size[0];
+        $this->_height = $size[1];
+        if ($this->_width < $this->_minwidth) {
+            $this->_throw($file, self::WIDTH_TOO_SMALL);
+        }
+
+        if (($this->_maxwidth !== null) and ($this->_maxwidth < $this->_width)) {
+            $this->_throw($file, self::WIDTH_TOO_BIG);
+        }
+
+        if ($this->_height < $this->_minheight) {
+            $this->_throw($file, self::HEIGHT_TOO_SMALL);
+        }
+
+        if (($this->_maxheight !== null) and ($this->_maxheight < $this->_height)) {
+            $this->_throw($file, self::HEIGHT_TOO_BIG);
+        }
+
+        if (count($this->_messages) > 0) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Throws an error of the given type
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        if ($file !== null) {
+            $this->_value = $file['name'];
+        }
+
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/File/IsImage.php
===================================================================
--- trunk/library/Zend/Validate/File/IsImage.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/IsImage.php	(revision 5146)
@@ -0,0 +1,173 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: IsImage.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_File_MimeType
+ */
+require_once 'Zend/Validate/File/MimeType.php';
+
+/**
+ * Validator which checks if the file already exists in the directory
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_IsImage extends Zend_Validate_File_MimeType
+{
+    /**
+     * @const string Error constants
+     */
+    const FALSE_TYPE   = 'fileIsImageFalseType';
+    const NOT_DETECTED = 'fileIsImageNotDetected';
+    const NOT_READABLE = 'fileIsImageNotReadable';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::FALSE_TYPE   => "File '%value%' is no image, '%type%' detected",
+        self::NOT_DETECTED => "The mimetype of file '%value%' could not be detected",
+        self::NOT_READABLE => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * Sets validator options
+     *
+     * @param  string|array|Zend_Config $mimetype
+     * @return void
+     */
+    public function __construct($mimetype = array())
+    {
+        if ($mimetype instanceof Zend_Config) {
+            $mimetype = $mimetype->toArray();
+        }
+
+        $temp    = array();
+        // http://de.wikipedia.org/wiki/Liste_von_Dateiendungen
+        // http://www.iana.org/assignments/media-types/image/
+        $default = array(
+            'application/cdf',
+            'application/dicom',
+            'application/fractals',
+            'application/postscript',
+            'application/vnd.hp-hpgl',
+            'application/vnd.oasis.opendocument.graphics',
+            'application/x-cdf',
+            'application/x-cmu-raster',
+            'application/x-ima',
+            'application/x-inventor',
+            'application/x-koan',
+            'application/x-portable-anymap',
+            'application/x-world-x-3dmf',
+            'image/bmp',
+            'image/c',
+            'image/cgm',
+            'image/fif',
+            'image/gif',
+            'image/jpeg',
+            'image/jpm',
+            'image/jpx',
+            'image/jp2',
+            'image/naplps',
+            'image/pjpeg',
+            'image/png',
+            'image/svg',
+            'image/svg+xml',
+            'image/tiff',
+            'image/vnd.adobe.photoshop',
+            'image/vnd.djvu',
+            'image/vnd.fpx',
+            'image/vnd.net-fpx',
+            'image/x-cmu-raster',
+            'image/x-cmx',
+            'image/x-coreldraw',
+            'image/x-cpi',
+            'image/x-emf',
+            'image/x-ico',
+            'image/x-icon',
+            'image/x-jg',
+            'image/x-ms-bmp',
+            'image/x-niff',
+            'image/x-pict',
+            'image/x-pcx',
+            'image/x-portable-anymap',
+            'image/x-portable-bitmap',
+            'image/x-portable-greymap',
+            'image/x-portable-pixmap',
+            'image/x-quicktime',
+            'image/x-rgb',
+            'image/x-tiff',
+            'image/x-unknown',
+            'image/x-windows-bmp',
+            'image/x-xpmi',
+        );
+
+        if (is_array($mimetype)) {
+            $temp = $mimetype;
+            if (array_key_exists('magicfile', $temp)) {
+                unset($temp['magicfile']);
+            }
+
+            if (array_key_exists('headerCheck', $temp)) {
+                unset($temp['headerCheck']);
+            }
+
+            if (empty($temp)) {
+                $mimetype += $default;
+            }
+        }
+
+        if (empty($mimetype)) {
+            $mimetype = $default;
+        }
+
+        parent::__construct($mimetype);
+    }
+
+    /**
+     * Throws an error of the given type
+     * Duplicates parent method due to OOP Problem with late static binding in PHP 5.2
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        $this->_value = $file['name'];
+        switch($errorType) {
+            case Zend_Validate_File_MimeType::FALSE_TYPE :
+                $errorType = self::FALSE_TYPE;
+                break;
+            case Zend_Validate_File_MimeType::NOT_DETECTED :
+                $errorType = self::NOT_DETECTED;
+                break;
+            case Zend_Validate_File_MimeType::NOT_READABLE :
+                $errorType = self::NOT_READABLE;
+                break;
+        }
+
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/File/Exists.php
===================================================================
--- trunk/library/Zend/Validate/File/Exists.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/Exists.php	(revision 5146)
@@ -0,0 +1,203 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: Exists.php 20352 2010-01-17 17:55:38Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator which checks if the file already exists in the directory
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_Exists extends Zend_Validate_Abstract
+{
+    /**
+     * @const string Error constants
+     */
+    const DOES_NOT_EXIST = 'fileExistsDoesNotExist';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::DOES_NOT_EXIST => "File '%value%' does not exist",
+    );
+
+    /**
+     * Internal list of directories
+     * @var string
+     */
+    protected $_directory = '';
+
+    /**
+     * @var array Error message template variables
+     */
+    protected $_messageVariables = array(
+        'directory' => '_directory'
+    );
+
+    /**
+     * Sets validator options
+     *
+     * @param  string|array|Zend_Config $directory
+     * @return void
+     */
+    public function __construct($directory = array())
+    {
+        if ($directory instanceof Zend_Config) {
+            $directory = $directory->toArray();
+        } else if (is_string($directory)) {
+            $directory = explode(',', $directory);
+        } else if (!is_array($directory)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception ('Invalid options to validator provided');
+        }
+
+        $this->setDirectory($directory);
+    }
+
+    /**
+     * Returns the set file directories which are checked
+     *
+     * @param  boolean $asArray Returns the values as array, when false an concated string is returned
+     * @return string
+     */
+    public function getDirectory($asArray = false)
+    {
+        $asArray   = (bool) $asArray;
+        $directory = (string) $this->_directory;
+        if ($asArray) {
+            $directory = explode(',', $directory);
+        }
+
+        return $directory;
+    }
+
+    /**
+     * Sets the file directory which will be checked
+     *
+     * @param  string|array $directory The directories to validate
+     * @return Zend_Validate_File_Extension Provides a fluent interface
+     */
+    public function setDirectory($directory)
+    {
+        $this->_directory = null;
+        $this->addDirectory($directory);
+        return $this;
+    }
+
+    /**
+     * Adds the file directory which will be checked
+     *
+     * @param  string|array $directory The directory to add for validation
+     * @return Zend_Validate_File_Extension Provides a fluent interface
+     */
+    public function addDirectory($directory)
+    {
+        $directories = $this->getDirectory(true);
+
+        if (is_string($directory)) {
+            $directory = explode(',', $directory);
+        } else if (!is_array($directory)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception ('Invalid options to validator provided');
+        }
+
+        foreach ($directory as $content) {
+            if (empty($content) || !is_string($content)) {
+                continue;
+            }
+
+            $directories[] = trim($content);
+        }
+        $directories = array_unique($directories);
+
+        // Sanity check to ensure no empty values
+        foreach ($directories as $key => $dir) {
+            if (empty($dir)) {
+                unset($directories[$key]);
+            }
+        }
+
+        $this->_directory = implode(',', $directories);
+
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the file already exists in the set directories
+     *
+     * @param  string  $value Real file to check for existance
+     * @param  array   $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        $directories = $this->getDirectory(true);
+        if (($file !== null) and (!empty($file['destination']))) {
+            $directories[] = $file['destination'];
+        } else if (!isset($file['name'])) {
+            $file['name'] = $value;
+        }
+
+        $check = false;
+        foreach ($directories as $directory) {
+            if (empty($directory)) {
+                continue;
+            }
+
+            $check = true;
+            if (!file_exists($directory . DIRECTORY_SEPARATOR . $file['name'])) {
+                return $this->_throw($file, self::DOES_NOT_EXIST);
+            }
+        }
+
+        if (!$check) {
+            return $this->_throw($file, self::DOES_NOT_EXIST);
+        }
+
+        return true;
+    }
+
+    /**
+     * Throws an error of the given type
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        if ($file !== null) {
+            $this->_value = $file['name'];
+        }
+
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/File/Hash.php
===================================================================
--- trunk/library/Zend/Validate/File/Hash.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/Hash.php	(revision 5146)
@@ -0,0 +1,194 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: Hash.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the hash of given files
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_Hash extends Zend_Validate_Abstract
+{
+    /**
+     * @const string Error constants
+     */
+    const DOES_NOT_MATCH = 'fileHashDoesNotMatch';
+    const NOT_DETECTED   = 'fileHashHashNotDetected';
+    const NOT_FOUND      = 'fileHashNotFound';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::DOES_NOT_MATCH => "File '%value%' does not match the given hashes",
+        self::NOT_DETECTED   => "A hash could not be evaluated for the given file",
+        self::NOT_FOUND      => "File '%value%' is not readable or does not exist"
+    );
+
+    /**
+     * Hash of the file
+     *
+     * @var string
+     */
+    protected $_hash;
+
+    /**
+     * Sets validator options
+     *
+     * @param  string|array $options
+     * @return void
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (is_scalar($options)) {
+            $options = array('hash1' => $options);
+        } elseif (!is_array($options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid options to validator provided');
+        }
+
+        if (1 < func_num_args()) {
+            $options['algorithm'] = func_get_arg(1);
+        }
+
+        $this->setHash($options);
+    }
+
+    /**
+     * Returns the set hash values as array, the hash as key and the algorithm the value
+     *
+     * @return array
+     */
+    public function getHash()
+    {
+        return $this->_hash;
+    }
+
+    /**
+     * Sets the hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function setHash($options)
+    {
+        $this->_hash  = null;
+        $this->addHash($options);
+
+        return $this;
+    }
+
+    /**
+     * Adds the hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function addHash($options)
+    {
+        if (is_string($options)) {
+            $options = array($options);
+        } else if (!is_array($options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("False parameter given");
+        }
+
+        $known = hash_algos();
+        if (!isset($options['algorithm'])) {
+            $algorithm = 'crc32';
+        } else {
+            $algorithm = $options['algorithm'];
+            unset($options['algorithm']);
+        }
+
+        if (!in_array($algorithm, $known)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("Unknown algorithm '{$algorithm}'");
+        }
+
+        foreach ($options as $value) {
+            $this->_hash[$value] = $algorithm;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the given file confirms the set hash
+     *
+     * @param  string $value Filename to check for hash
+     * @param  array  $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_FOUND);
+        }
+
+        $algos  = array_unique(array_values($this->_hash));
+        $hashes = array_unique(array_keys($this->_hash));
+        foreach ($algos as $algorithm) {
+            $filehash = hash_file($algorithm, $value);
+            if ($filehash === false) {
+                return $this->_throw($file, self::NOT_DETECTED);
+            }
+
+            foreach($hashes as $hash) {
+                if ($filehash === $hash) {
+                    return true;
+                }
+            }
+        }
+
+        return $this->_throw($file, self::DOES_NOT_MATCH);
+    }
+
+    /**
+     * Throws an error of the given type
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        if ($file !== null) {
+            $this->_value = $file['name'];
+        }
+
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/File/WordCount.php
===================================================================
--- trunk/library/Zend/Validate/File/WordCount.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/WordCount.php	(revision 5146)
@@ -0,0 +1,101 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: WordCount.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_File_Count
+ */
+require_once 'Zend/Validate/File/Count.php';
+
+/**
+ * Validator for counting all words in a file
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_WordCount extends Zend_Validate_File_Count
+{
+    /**#@+
+     * @const string Error constants
+     */
+    const TOO_MUCH  = 'fileWordCountTooMuch';
+    const TOO_LESS  = 'fileWordCountTooLess';
+    const NOT_FOUND = 'fileWordCountNotFound';
+    /**#@-*/
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::TOO_MUCH => "Too much words, maximum '%max%' are allowed but '%count%' were counted",
+        self::TOO_LESS => "Too less words, minimum '%min%' are expected but '%count%' were counted",
+        self::NOT_FOUND => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the counted words are at least min and
+     * not bigger than max (when max is not null).
+     *
+     * @param  string $value Filename to check for word count
+     * @param  array  $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_FOUND);
+        }
+
+        $content = file_get_contents($value);
+        $this->_count = str_word_count($content);
+        if (($this->_max !== null) && ($this->_count > $this->_max)) {
+            return $this->_throw($file, self::TOO_MUCH);
+        }
+
+        if (($this->_min !== null) && ($this->_count < $this->_min)) {
+            return $this->_throw($file, self::TOO_LESS);
+        }
+
+        return true;
+    }
+
+    /**
+     * Throws an error of the given type
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        if ($file !== null) {
+            $this->_value = $file['name'];
+        }
+
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/File/Upload.php
===================================================================
--- trunk/library/Zend/Validate/File/Upload.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/Upload.php	(revision 5146)
@@ -0,0 +1,251 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: Upload.php 23570 2010-12-20 08:38:30Z mjh_ca $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for the maximum size of a file up to a max of 2GB
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_Upload extends Zend_Validate_Abstract
+{
+    /**@#+
+     * @const string Error constants
+     */
+    const INI_SIZE       = 'fileUploadErrorIniSize';
+    const FORM_SIZE      = 'fileUploadErrorFormSize';
+    const PARTIAL        = 'fileUploadErrorPartial';
+    const NO_FILE        = 'fileUploadErrorNoFile';
+    const NO_TMP_DIR     = 'fileUploadErrorNoTmpDir';
+    const CANT_WRITE     = 'fileUploadErrorCantWrite';
+    const EXTENSION      = 'fileUploadErrorExtension';
+    const ATTACK         = 'fileUploadErrorAttack';
+    const FILE_NOT_FOUND = 'fileUploadErrorFileNotFound';
+    const UNKNOWN        = 'fileUploadErrorUnknown';
+    /**@#-*/
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::INI_SIZE       => "File '%value%' exceeds the defined ini size",
+        self::FORM_SIZE      => "File '%value%' exceeds the defined form size",
+        self::PARTIAL        => "File '%value%' was only partially uploaded",
+        self::NO_FILE        => "File '%value%' was not uploaded",
+        self::NO_TMP_DIR     => "No temporary directory was found for file '%value%'",
+        self::CANT_WRITE     => "File '%value%' can't be written",
+        self::EXTENSION      => "A PHP extension returned an error while uploading the file '%value%'",
+        self::ATTACK         => "File '%value%' was illegally uploaded. This could be a possible attack",
+        self::FILE_NOT_FOUND => "File '%value%' was not found",
+        self::UNKNOWN        => "Unknown error while uploading file '%value%'"
+    );
+
+    /**
+     * Internal array of files
+     * @var array
+     */
+    protected $_files = array();
+
+    /**
+     * Sets validator options
+     *
+     * The array $files must be given in syntax of Zend_File_Transfer to be checked
+     * If no files are given the $_FILES array will be used automatically.
+     * NOTE: This validator will only work with HTTP POST uploads!
+     *
+     * @param  array|Zend_Config $files Array of files in syntax of Zend_File_Transfer
+     * @return void
+     */
+    public function __construct($files = array())
+    {
+        if ($files instanceof Zend_Config) {
+            $files = $files->toArray();
+        }
+
+        $this->setFiles($files);
+    }
+
+    /**
+     * Returns the array of set files
+     *
+     * @param  string $files (Optional) The file to return in detail
+     * @return array
+     * @throws Zend_Validate_Exception If file is not found
+     */
+    public function getFiles($file = null)
+    {
+        if ($file !== null) {
+            $return = array();
+            foreach ($this->_files as $name => $content) {
+                if ($name === $file) {
+                    $return[$file] = $this->_files[$name];
+                }
+
+                if ($content['name'] === $file) {
+                    $return[$name] = $this->_files[$name];
+                }
+            }
+
+            if (count($return) === 0) {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception("The file '$file' was not found");
+            }
+
+            return $return;
+        }
+
+        return $this->_files;
+    }
+
+    /**
+     * Sets the files to be checked
+     *
+     * @param  array $files The files to check in syntax of Zend_File_Transfer
+     * @return Zend_Validate_File_Upload Provides a fluent interface
+     */
+    public function setFiles($files = array())
+    {
+        if (count($files) === 0) {
+            $this->_files = $_FILES;
+        } else {
+            $this->_files = $files;
+        }
+
+        // see ZF-10738
+        if (is_null($this->_files)) {
+            $this->_files = array();
+        }
+
+        foreach($this->_files as $file => $content) {
+            if (!isset($content['error'])) {
+                unset($this->_files[$file]);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the file was uploaded without errors
+     *
+     * @param  string $value Single file to check for upload errors, when giving null the $_FILES array
+     *                       from initialization will be used
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        $this->_messages = null;
+        if (array_key_exists($value, $this->_files)) {
+            $files[$value] = $this->_files[$value];
+        } else {
+            foreach ($this->_files as $file => $content) {
+                if (isset($content['name']) && ($content['name'] === $value)) {
+                    $files[$file] = $this->_files[$file];
+                }
+
+                if (isset($content['tmp_name']) && ($content['tmp_name'] === $value)) {
+                    $files[$file] = $this->_files[$file];
+                }
+            }
+        }
+
+        if (empty($files)) {
+            return $this->_throw($file, self::FILE_NOT_FOUND);
+        }
+
+        foreach ($files as $file => $content) {
+            $this->_value = $file;
+            switch($content['error']) {
+                case 0:
+                    if (!is_uploaded_file($content['tmp_name'])) {
+                        $this->_throw($file, self::ATTACK);
+                    }
+                    break;
+
+                case 1:
+                    $this->_throw($file, self::INI_SIZE);
+                    break;
+
+                case 2:
+                    $this->_throw($file, self::FORM_SIZE);
+                    break;
+
+                case 3:
+                    $this->_throw($file, self::PARTIAL);
+                    break;
+
+                case 4:
+                    $this->_throw($file, self::NO_FILE);
+                    break;
+
+                case 6:
+                    $this->_throw($file, self::NO_TMP_DIR);
+                    break;
+
+                case 7:
+                    $this->_throw($file, self::CANT_WRITE);
+                    break;
+
+                case 8:
+                    $this->_throw($file, self::EXTENSION);
+                    break;
+
+                default:
+                    $this->_throw($file, self::UNKNOWN);
+                    break;
+            }
+        }
+
+        if (count($this->_messages) > 0) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Throws an error of the given type
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        if ($file !== null) {
+            if (is_array($file) and !empty($file['name'])) {
+                $this->_value = $file['name'];
+            }
+        }
+
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/File/Md5.php
===================================================================
--- trunk/library/Zend/Validate/File/Md5.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/Md5.php	(revision 5146)
@@ -0,0 +1,183 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: Md5.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_File_Hash
+ */
+require_once 'Zend/Validate/File/Hash.php';
+
+/**
+ * Validator for the md5 hash of given files
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_Md5 extends Zend_Validate_File_Hash
+{
+    /**
+     * @const string Error constants
+     */
+    const DOES_NOT_MATCH = 'fileMd5DoesNotMatch';
+    const NOT_DETECTED   = 'fileMd5NotDetected';
+    const NOT_FOUND      = 'fileMd5NotFound';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::DOES_NOT_MATCH => "File '%value%' does not match the given md5 hashes",
+        self::NOT_DETECTED   => "A md5 hash could not be evaluated for the given file",
+        self::NOT_FOUND      => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * Hash of the file
+     *
+     * @var string
+     */
+    protected $_hash;
+
+    /**
+     * Sets validator options
+     *
+     * $hash is the hash we accept for the file $file
+     *
+     * @param  string|array $options
+     * @return void
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (is_scalar($options)) {
+            $options = array('hash1' => $options);
+        } elseif (!is_array($options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid options to validator provided');
+        }
+
+        $this->setMd5($options);
+    }
+
+    /**
+     * Returns all set md5 hashes
+     *
+     * @return array
+     */
+    public function getMd5()
+    {
+        return $this->getHash();
+    }
+
+    /**
+     * Sets the md5 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @param  string       $algorithm (Deprecated) Algorithm to use, fixed to md5
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function setHash($options)
+    {
+        if (!is_array($options)) {
+            $options = (array) $options;
+        }
+
+        $options['algorithm'] = 'md5';
+        parent::setHash($options);
+        return $this;
+    }
+
+    /**
+     * Sets the md5 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function setMd5($options)
+    {
+        $this->setHash($options);
+        return $this;
+    }
+
+    /**
+     * Adds the md5 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @param  string       $algorithm (Deprecated) Algorithm to use, fixed to md5
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function addHash($options)
+    {
+        if (!is_array($options)) {
+            $options = (array) $options;
+        }
+
+        $options['algorithm'] = 'md5';
+        parent::addHash($options);
+        return $this;
+    }
+
+    /**
+     * Adds the md5 hash for one or multiple files
+     *
+     * @param  string|array $options
+     * @return Zend_Validate_File_Hash Provides a fluent interface
+     */
+    public function addMd5($options)
+    {
+        $this->addHash($options);
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the given file confirms the set hash
+     *
+     * @param  string $value Filename to check for hash
+     * @param  array  $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_FOUND);
+        }
+
+        $hashes = array_unique(array_keys($this->_hash));
+        $filehash = hash_file('md5', $value);
+        if ($filehash === false) {
+            return $this->_throw($file, self::NOT_DETECTED);
+        }
+
+        foreach($hashes as $hash) {
+            if ($filehash === $hash) {
+                return true;
+            }
+        }
+
+        return $this->_throw($file, self::DOES_NOT_MATCH);
+    }
+}
Index: trunk/library/Zend/Validate/File/ExcludeExtension.php
===================================================================
--- trunk/library/Zend/Validate/File/ExcludeExtension.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/ExcludeExtension.php	(revision 5146)
@@ -0,0 +1,94 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: ExcludeExtension.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/File/Extension.php';
+
+/**
+ * Validator for the excluding file extensions
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_ExcludeExtension extends Zend_Validate_File_Extension
+{
+    /**
+     * @const string Error constants
+     */
+    const FALSE_EXTENSION = 'fileExcludeExtensionFalse';
+    const NOT_FOUND       = 'fileExcludeExtensionNotFound';
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::FALSE_EXTENSION => "File '%value%' has a false extension",
+        self::NOT_FOUND       => "File '%value%' is not readable or does not exist",
+    );
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the fileextension of $value is not included in the
+     * set extension list
+     *
+     * @param  string  $value Real file to check for extension
+     * @param  array   $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        // Is file readable ?
+        require_once 'Zend/Loader.php';
+        if (!Zend_Loader::isReadable($value)) {
+            return $this->_throw($file, self::NOT_FOUND);
+        }
+
+        if ($file !== null) {
+            $info['extension'] = substr($file['name'], strrpos($file['name'], '.') + 1);
+        } else {
+            $info = pathinfo($value);
+        }
+
+        $extensions = $this->getExtension();
+
+        if ($this->_case and (!in_array($info['extension'], $extensions))) {
+            return true;
+        } else if (!$this->_case) {
+            $found = false;
+            foreach ($extensions as $extension) {
+                if (strtolower($extension) == strtolower($info['extension'])) {
+                    $found = true;
+                }
+            }
+
+            if (!$found) {
+                return true;
+            }
+        }
+
+        return $this->_throw($file, self::FALSE_EXTENSION);
+    }
+}
Index: trunk/library/Zend/Validate/File/Count.php
===================================================================
--- trunk/library/Zend/Validate/File/Count.php	(revision 5146)
+++ trunk/library/Zend/Validate/File/Count.php	(revision 5146)
@@ -0,0 +1,284 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ * @version   $Id: Count.php 21325 2010-03-04 20:26:36Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validator for counting all given files
+ *
+ * @category  Zend
+ * @package   Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license   http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_File_Count extends Zend_Validate_Abstract
+{
+    /**#@+
+     * @const string Error constants
+     */
+    const TOO_MANY = 'fileCountTooMany';
+    const TOO_FEW  = 'fileCountTooFew';
+    /**#@-*/
+
+    /**
+     * @var array Error message templates
+     */
+    protected $_messageTemplates = array(
+        self::TOO_MANY => "Too many files, maximum '%max%' are allowed but '%count%' are given",
+        self::TOO_FEW  => "Too few files, minimum '%min%' are expected but '%count%' are given",
+    );
+
+    /**
+     * @var array Error message template variables
+     */
+    protected $_messageVariables = array(
+        'min'   => '_min',
+        'max'   => '_max',
+        'count' => '_count'
+    );
+
+    /**
+     * Minimum file count
+     *
+     * If null, there is no minimum file count
+     *
+     * @var integer
+     */
+    protected $_min;
+
+    /**
+     * Maximum file count
+     *
+     * If null, there is no maximum file count
+     *
+     * @var integer|null
+     */
+    protected $_max;
+
+    /**
+     * Actual filecount
+     *
+     * @var integer
+     */
+    protected $_count;
+
+    /**
+     * Internal file array
+     * @var array
+     */
+    protected $_files;
+
+    /**
+     * Sets validator options
+     *
+     * Min limits the file count, when used with max=null it is the maximum file count
+     * It also accepts an array with the keys 'min' and 'max'
+     *
+     * If $options is a integer, it will be used as maximum file count
+     * As Array is accepts the following keys:
+     * 'min': Minimum filecount
+     * 'max': Maximum filecount
+     *
+     * @param  integer|array|Zend_Config $options Options for the adapter
+     * @return void
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (is_string($options) || is_numeric($options)) {
+            $options = array('max' => $options);
+        } elseif (!is_array($options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception ('Invalid options to validator provided');
+        }
+
+        if (1 < func_num_args()) {
+            $options['min'] = func_get_arg(0);
+            $options['max'] = func_get_arg(1);
+        }
+
+        if (isset($options['min'])) {
+            $this->setMin($options);
+        }
+
+        if (isset($options['max'])) {
+            $this->setMax($options);
+        }
+    }
+
+    /**
+     * Returns the minimum file count
+     *
+     * @return integer
+     */
+    public function getMin()
+    {
+        return $this->_min;
+    }
+
+    /**
+     * Sets the minimum file count
+     *
+     * @param  integer|array $min The minimum file count
+     * @return Zend_Validate_File_Count Provides a fluent interface
+     * @throws Zend_Validate_Exception When min is greater than max
+     */
+    public function setMin($min)
+    {
+        if (is_array($min) and isset($min['min'])) {
+            $min = $min['min'];
+        }
+
+        if (!is_string($min) and !is_numeric($min)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception ('Invalid options to validator provided');
+        }
+
+        $min = (integer) $min;
+        if (($this->_max !== null) && ($min > $this->_max)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("The minimum must be less than or equal to the maximum file count, but $min >"
+                                            . " {$this->_max}");
+        }
+
+        $this->_min = $min;
+        return $this;
+    }
+
+    /**
+     * Returns the maximum file count
+     *
+     * @return integer
+     */
+    public function getMax()
+    {
+        return $this->_max;
+    }
+
+    /**
+     * Sets the maximum file count
+     *
+     * @param  integer|array $max The maximum file count
+     * @return Zend_Validate_StringLength Provides a fluent interface
+     * @throws Zend_Validate_Exception When max is smaller than min
+     */
+    public function setMax($max)
+    {
+        if (is_array($max) and isset($max['max'])) {
+            $max = $max['max'];
+        }
+
+        if (!is_string($max) and !is_numeric($max)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception ('Invalid options to validator provided');
+        }
+
+        $max = (integer) $max;
+        if (($this->_min !== null) && ($max < $this->_min)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("The maximum must be greater than or equal to the minimum file count, but "
+                                            . "$max < {$this->_min}");
+        }
+
+        $this->_max = $max;
+        return $this;
+    }
+
+    /**
+     * Adds a file for validation
+     *
+     * @param string|array $file
+     */
+    public function addFile($file)
+    {
+        if (is_string($file)) {
+            $file = array($file);
+        }
+
+        if (is_array($file)) {
+            foreach ($file as $name) {
+                if (!isset($this->_files[$name]) && !empty($name)) {
+                    $this->_files[$name] = $name;
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the file count of all checked files is at least min and
+     * not bigger than max (when max is not null). Attention: When checking with set min you
+     * must give all files with the first call, otherwise you will get an false.
+     *
+     * @param  string|array $value Filenames to check for count
+     * @param  array        $file  File data from Zend_File_Transfer
+     * @return boolean
+     */
+    public function isValid($value, $file = null)
+    {
+        if (($file !== null) && !array_key_exists('destination', $file)) {
+            $file['destination'] = dirname($value);
+        }
+
+        if (($file !== null) && array_key_exists('tmp_name', $file)) {
+            $value = $file['destination'] . DIRECTORY_SEPARATOR . $file['name'];
+        }
+
+        if (($file === null) || !empty($file['tmp_name'])) {
+            $this->addFile($value);
+        }
+
+        $this->_count = count($this->_files);
+        if (($this->_max !== null) && ($this->_count > $this->_max)) {
+            return $this->_throw($file, self::TOO_MANY);
+        }
+
+        if (($this->_min !== null) && ($this->_count < $this->_min)) {
+            return $this->_throw($file, self::TOO_FEW);
+        }
+
+        return true;
+    }
+
+    /**
+     * Throws an error of the given type
+     *
+     * @param  string $file
+     * @param  string $errorType
+     * @return false
+     */
+    protected function _throw($file, $errorType)
+    {
+        if ($file !== null) {
+            $this->_value = $file['name'];
+        }
+
+        $this->_error($errorType);
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/Identical.php
===================================================================
--- trunk/library/Zend/Validate/Identical.php	(revision 5146)
+++ trunk/library/Zend/Validate/Identical.php	(revision 5146)
@@ -0,0 +1,164 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Identical.php 22075 2010-05-02 13:42:08Z thomas $
+ */
+
+/** @see Zend_Validate_Abstract */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Identical extends Zend_Validate_Abstract
+{
+    /**
+     * Error codes
+     * @const string
+     */
+    const NOT_SAME      = 'notSame';
+    const MISSING_TOKEN = 'missingToken';
+
+    /**
+     * Error messages
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOT_SAME      => "The two given tokens do not match",
+        self::MISSING_TOKEN => 'No token was provided to match against',
+    );
+
+    /**
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'token' => '_tokenString'
+    );
+
+    /**
+     * Original token against which to validate
+     * @var string
+     */
+    protected $_tokenString;
+    protected $_token;
+    protected $_strict = true;
+
+    /**
+     * Sets validator options
+     *
+     * @param  mixed $token
+     * @return void
+     */
+    public function __construct($token = null)
+    {
+        if ($token instanceof Zend_Config) {
+            $token = $token->toArray();
+        }
+
+        if (is_array($token) && array_key_exists('token', $token)) {
+            if (array_key_exists('strict', $token)) {
+                $this->setStrict($token['strict']);
+            }
+
+            $this->setToken($token['token']);
+        } else if (null !== $token) {
+            $this->setToken($token);
+        }
+    }
+
+    /**
+     * Retrieve token
+     *
+     * @return string
+     */
+    public function getToken()
+    {
+        return $this->_token;
+    }
+
+    /**
+     * Set token against which to compare
+     *
+     * @param  mixed $token
+     * @return Zend_Validate_Identical
+     */
+    public function setToken($token)
+    {
+        $this->_tokenString = (string) $token;
+        $this->_token       = $token;
+        return $this;
+    }
+
+    /**
+     * Returns the strict parameter
+     *
+     * @return boolean
+     */
+    public function getStrict()
+    {
+        return $this->_strict;
+    }
+
+    /**
+     * Sets the strict parameter
+     *
+     * @param Zend_Validate_Identical
+     */
+    public function setStrict($strict)
+    {
+        $this->_strict = (boolean) $strict;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if a token has been set and the provided value
+     * matches that token.
+     *
+     * @param  mixed $value
+     * @param  array $context
+     * @return boolean
+     */
+    public function isValid($value, $context = null)
+    {
+        $this->_setValue((string) $value);
+
+        if (($context !== null) && isset($context) && array_key_exists($this->getToken(), $context)) {
+            $token = $context[$this->getToken()];
+        } else {
+            $token = $this->getToken();
+        }
+
+        if ($token === null) {
+            $this->_error(self::MISSING_TOKEN);
+            return false;
+        }
+
+        $strict = $this->getStrict();
+        if (($strict && ($value !== $token)) || (!$strict && ($value != $token))) {
+            $this->_error(self::NOT_SAME);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/EmailAddress.php
===================================================================
--- trunk/library/Zend/Validate/EmailAddress.php	(revision 5146)
+++ trunk/library/Zend/Validate/EmailAddress.php	(revision 5146)
@@ -0,0 +1,560 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: EmailAddress.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @see Zend_Validate_Hostname
+ */
+require_once 'Zend/Validate/Hostname.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_EmailAddress extends Zend_Validate_Abstract
+{
+    const INVALID            = 'emailAddressInvalid';
+    const INVALID_FORMAT     = 'emailAddressInvalidFormat';
+    const INVALID_HOSTNAME   = 'emailAddressInvalidHostname';
+    const INVALID_MX_RECORD  = 'emailAddressInvalidMxRecord';
+    const INVALID_SEGMENT    = 'emailAddressInvalidSegment';
+    const DOT_ATOM           = 'emailAddressDotAtom';
+    const QUOTED_STRING      = 'emailAddressQuotedString';
+    const INVALID_LOCAL_PART = 'emailAddressInvalidLocalPart';
+    const LENGTH_EXCEEDED    = 'emailAddressLengthExceeded';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID            => "Invalid type given. String expected",
+        self::INVALID_FORMAT     => "'%value%' is no valid email address in the basic format local-part@hostname",
+        self::INVALID_HOSTNAME   => "'%hostname%' is no valid hostname for email address '%value%'",
+        self::INVALID_MX_RECORD  => "'%hostname%' does not appear to have a valid MX record for the email address '%value%'",
+        self::INVALID_SEGMENT    => "'%hostname%' is not in a routable network segment. The email address '%value%' should not be resolved from public network",
+        self::DOT_ATOM           => "'%localPart%' can not be matched against dot-atom format",
+        self::QUOTED_STRING      => "'%localPart%' can not be matched against quoted-string format",
+        self::INVALID_LOCAL_PART => "'%localPart%' is no valid local part for email address '%value%'",
+        self::LENGTH_EXCEEDED    => "'%value%' exceeds the allowed length",
+    );
+
+    /**
+     * @see http://en.wikipedia.org/wiki/IPv4
+     * @var array
+     */
+    protected $_invalidIp = array(
+        '0'   => '0.0.0.0/8',
+        '10'  => '10.0.0.0/8',
+        '127' => '127.0.0.0/8',
+        '128' => '128.0.0.0/16',
+        '169' => '169.254.0.0/16',
+        '172' => '172.16.0.0/12',
+        '191' => '191.255.0.0/16',
+        '192' => array(
+            '192.0.0.0/24',
+            '192.0.2.0/24',
+            '192.88.99.0/24',
+            '192.168.0.0/16'
+        ),
+        '198' => '198.18.0.0/15',
+        '223' => '223.255.255.0/24',
+        '224' => '224.0.0.0/4',
+        '240' => '240.0.0.0/4'
+    );
+
+    /**
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'hostname'  => '_hostname',
+        'localPart' => '_localPart'
+    );
+
+    /**
+     * @var string
+     */
+    protected $_hostname;
+
+    /**
+     * @var string
+     */
+    protected $_localPart;
+
+    /**
+     * Internal options array
+     */
+    protected $_options = array(
+        'mx'       => false,
+        'deep'     => false,
+        'domain'   => true,
+        'allow'    => Zend_Validate_Hostname::ALLOW_DNS,
+        'hostname' => null
+    );
+
+    /**
+     * Instantiates hostname validator for local use
+     *
+     * The following option keys are supported:
+     * 'hostname' => A hostname validator, see Zend_Validate_Hostname
+     * 'allow'    => Options for the hostname validator, see Zend_Validate_Hostname::ALLOW_*
+     * 'mx'       => If MX check should be enabled, boolean
+     * 'deep'     => If a deep MX check should be done, boolean
+     *
+     * @param array|Zend_Config $options OPTIONAL
+     * @return void
+     */
+    public function __construct($options = array())
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } else if (!is_array($options)) {
+            $options = func_get_args();
+            $temp['allow'] = array_shift($options);
+            if (!empty($options)) {
+                $temp['mx'] = array_shift($options);
+            }
+
+            if (!empty($options)) {
+                $temp['hostname'] = array_shift($options);
+            }
+
+            $options = $temp;
+        }
+
+        $options += $this->_options;
+        $this->setOptions($options);
+    }
+
+    /**
+     * Returns all set Options
+     *
+     * @return array
+     */
+    public function getOptions()
+    {
+        return $this->_options;
+    }
+
+    /**
+     * Set options for the email validator
+     *
+     * @param array $options
+     * @return Zend_Validate_EmailAddress fluid interface
+     */
+    public function setOptions(array $options = array())
+    {
+        if (array_key_exists('messages', $options)) {
+            $this->setMessages($options['messages']);
+        }
+
+        if (array_key_exists('hostname', $options)) {
+            if (array_key_exists('allow', $options)) {
+                $this->setHostnameValidator($options['hostname'], $options['allow']);
+            } else {
+                $this->setHostnameValidator($options['hostname']);
+            }
+        }
+
+        if (array_key_exists('mx', $options)) {
+            $this->setValidateMx($options['mx']);
+        }
+
+        if (array_key_exists('deep', $options)) {
+            $this->setDeepMxCheck($options['deep']);
+        }
+
+        if (array_key_exists('domain', $options)) {
+            $this->setDomainCheck($options['domain']);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets the validation failure message template for a particular key
+     * Adds the ability to set messages to the attached hostname validator
+     *
+     * @param  string $messageString
+     * @param  string $messageKey     OPTIONAL
+     * @return Zend_Validate_Abstract Provides a fluent interface
+     * @throws Zend_Validate_Exception
+     */
+    public function setMessage($messageString, $messageKey = null)
+    {
+        $messageKeys = $messageKey;
+        if ($messageKey === null) {
+            $keys = array_keys($this->_messageTemplates);
+            $messageKeys = current($keys);
+        }
+
+        if (!isset($this->_messageTemplates[$messageKeys])) {
+            $this->_options['hostname']->setMessage($messageString, $messageKey);
+        }
+
+        $this->_messageTemplates[$messageKeys] = $messageString;
+        return $this;
+    }
+
+    /**
+     * Returns the set hostname validator
+     *
+     * @return Zend_Validate_Hostname
+     */
+    public function getHostnameValidator()
+    {
+        return $this->_options['hostname'];
+    }
+
+    /**
+     * @param Zend_Validate_Hostname $hostnameValidator OPTIONAL
+     * @param int                    $allow             OPTIONAL
+     * @return void
+     */
+    public function setHostnameValidator(Zend_Validate_Hostname $hostnameValidator = null, $allow = Zend_Validate_Hostname::ALLOW_DNS)
+    {
+        if (!$hostnameValidator) {
+            $hostnameValidator = new Zend_Validate_Hostname($allow);
+        }
+
+        $this->_options['hostname'] = $hostnameValidator;
+        $this->_options['allow']    = $allow;
+        return $this;
+    }
+
+    /**
+     * Whether MX checking via getmxrr is supported or not
+     *
+     * This currently only works on UNIX systems
+     *
+     * @return boolean
+     */
+    public function validateMxSupported()
+    {
+        return function_exists('getmxrr');
+    }
+
+    /**
+     * Returns the set validateMx option
+     *
+     * @return boolean
+     */
+    public function getValidateMx()
+    {
+        return $this->_options['mx'];
+    }
+
+    /**
+     * Set whether we check for a valid MX record via DNS
+     *
+     * This only applies when DNS hostnames are validated
+     *
+     * @param boolean $mx Set allowed to true to validate for MX records, and false to not validate them
+     * @return Zend_Validate_EmailAddress Fluid Interface
+     */
+    public function setValidateMx($mx)
+    {
+        if ((bool) $mx && !$this->validateMxSupported()) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('MX checking not available on this system');
+        }
+
+        $this->_options['mx'] = (bool) $mx;
+        return $this;
+    }
+
+    /**
+     * Returns the set deepMxCheck option
+     *
+     * @return boolean
+     */
+    public function getDeepMxCheck()
+    {
+        return $this->_options['deep'];
+    }
+
+    /**
+     * Set whether we check MX record should be a deep validation
+     *
+     * @param boolean $deep Set deep to true to perform a deep validation process for MX records
+     * @return Zend_Validate_EmailAddress Fluid Interface
+     */
+    public function setDeepMxCheck($deep)
+    {
+        $this->_options['deep'] = (bool) $deep;
+        return $this;
+    }
+
+    /**
+     * Returns the set domainCheck option
+     *
+     * @return unknown
+     */
+    public function getDomainCheck()
+    {
+        return $this->_options['domain'];
+    }
+
+    /**
+     * Sets if the domain should also be checked
+     * or only the local part of the email address
+     *
+     * @param boolean $domain
+     * @return Zend_Validate_EmailAddress Fluid Interface
+     */
+    public function setDomainCheck($domain = true)
+    {
+        $this->_options['domain'] = (boolean) $domain;
+        return $this;
+    }
+
+    /**
+     * Returns if the given host is reserved
+     *
+     * @param string $host
+     * @return boolean
+     */
+    private function _isReserved($host){
+        if (!preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $host)) {
+            $host = gethostbyname($host);
+        }
+
+        $octet = explode('.',$host);
+        if ((int)$octet[0] >= 224) {
+            return true;
+        } else if (array_key_exists($octet[0], $this->_invalidIp)) {
+            foreach ((array)$this->_invalidIp[$octet[0]] as $subnetData) {
+                // we skip the first loop as we already know that octet matches
+                for ($i = 1; $i < 4; $i++) {
+                    if (strpos($subnetData, $octet[$i]) !== $i * 4) {
+                        break;
+                    }
+                }
+
+                $host       = explode("/", $subnetData);
+                $binaryHost = "";
+                $tmp        = explode(".", $host[0]);
+                for ($i = 0; $i < 4 ; $i++) {
+                    $binaryHost .= str_pad(decbin($tmp[$i]), 8, "0", STR_PAD_LEFT);
+                }
+
+                $segmentData = array(
+                    'network'   => (int)$this->_toIp(str_pad(substr($binaryHost, 0, $host[1]), 32, 0)),
+                    'broadcast' => (int)$this->_toIp(str_pad(substr($binaryHost, 0, $host[1]), 32, 1))
+                );
+
+                for ($j = $i; $j < 4; $j++) {
+                    if ((int)$octet[$j] < $segmentData['network'][$j] ||
+                        (int)$octet[$j] > $segmentData['broadcast'][$j]) {
+                        return false;
+                    }
+                }
+            }
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Converts a binary string to an IP address
+     *
+     * @param string $binary
+     * @return mixed
+     */
+    private function _toIp($binary)
+    {
+        $ip  = array();
+        $tmp = explode(".", chunk_split($binary, 8, "."));
+        for ($i = 0; $i < 4 ; $i++) {
+            $ip[$i] = bindec($tmp[$i]);
+        }
+
+        return $ip;
+    }
+
+    /**
+     * Internal method to validate the local part of the email address
+     *
+     * @return boolean
+     */
+    private function _validateLocalPart()
+    {
+        // First try to match the local part on the common dot-atom format
+        $result = false;
+
+        // Dot-atom characters are: 1*atext *("." 1*atext)
+        // atext: ALPHA / DIGIT / and "!", "#", "$", "%", "&", "'", "*",
+        //        "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~"
+        $atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d\x7e';
+        if (preg_match('/^[' . $atext . ']+(\x2e+[' . $atext . ']+)*$/', $this->_localPart)) {
+            $result = true;
+        } else {
+            // Try quoted string format
+
+            // Quoted-string characters are: DQUOTE *([FWS] qtext/quoted-pair) [FWS] DQUOTE
+            // qtext: Non white space controls, and the rest of the US-ASCII characters not
+            //   including "\" or the quote character
+            $noWsCtl = '\x01-\x08\x0b\x0c\x0e-\x1f\x7f';
+            $qtext   = $noWsCtl . '\x21\x23-\x5b\x5d-\x7e';
+            $ws      = '\x20\x09';
+            if (preg_match('/^\x22([' . $ws . $qtext . '])*[$ws]?\x22$/', $this->_localPart)) {
+                $result = true;
+            } else {
+                $this->_error(self::DOT_ATOM);
+                $this->_error(self::QUOTED_STRING);
+                $this->_error(self::INVALID_LOCAL_PART);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Internal method to validate the servers MX records
+     *
+     * @return boolean
+     */
+    private function _validateMXRecords()
+    {
+        $mxHosts = array();
+        $result = getmxrr($this->_hostname, $mxHosts);
+        if (!$result) {
+            $this->_error(self::INVALID_MX_RECORD);
+        } else if ($this->_options['deep'] && function_exists('checkdnsrr')) {
+            $validAddress = false;
+            $reserved     = true;
+            foreach ($mxHosts as $hostname) {
+                $res = $this->_isReserved($hostname);
+                if (!$res) {
+                    $reserved = false;
+                }
+
+                if (!$res
+                    && (checkdnsrr($hostname, "A")
+                    || checkdnsrr($hostname, "AAAA")
+                    || checkdnsrr($hostname, "A6"))) {
+                    $validAddress = true;
+                    break;
+                }
+            }
+
+            if (!$validAddress) {
+                $result = false;
+                if ($reserved) {
+                    $this->_error(self::INVALID_SEGMENT);
+                } else {
+                    $this->_error(self::INVALID_MX_RECORD);
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Internal method to validate the hostname part of the email address
+     *
+     * @return boolean
+     */
+    private function _validateHostnamePart()
+    {
+        $hostname = $this->_options['hostname']->setTranslator($this->getTranslator())
+                         ->isValid($this->_hostname);
+        if (!$hostname) {
+            $this->_error(self::INVALID_HOSTNAME);
+
+            // Get messages and errors from hostnameValidator
+            foreach ($this->_options['hostname']->getMessages() as $code => $message) {
+                $this->_messages[$code] = $message;
+            }
+
+            foreach ($this->_options['hostname']->getErrors() as $error) {
+                $this->_errors[] = $error;
+            }
+        } else if ($this->_options['mx']) {
+            // MX check on hostname
+            $hostname = $this->_validateMXRecords();
+        }
+
+        return $hostname;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value is a valid email address
+     * according to RFC2822
+     *
+     * @link   http://www.ietf.org/rfc/rfc2822.txt RFC2822
+     * @link   http://www.columbia.edu/kermit/ascii.html US-ASCII characters
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $matches = array();
+        $length  = true;
+        $this->_setValue($value);
+
+        // Split email address up and disallow '..'
+        if ((strpos($value, '..') !== false) or
+            (!preg_match('/^(.+)@([^@]+)$/', $value, $matches))) {
+            $this->_error(self::INVALID_FORMAT);
+            return false;
+        }
+
+        $this->_localPart = $matches[1];
+        $this->_hostname  = $matches[2];
+
+        if ((strlen($this->_localPart) > 64) || (strlen($this->_hostname) > 255)) {
+            $length = false;
+            $this->_error(self::LENGTH_EXCEEDED);
+        }
+
+        // Match hostname part
+        if ($this->_options['domain']) {
+            $hostname = $this->_validateHostnamePart();
+        }
+
+        $local = $this->_validateLocalPart();
+
+        // If both parts valid, return true
+        if ($local && $length) {
+            if (($this->_options['domain'] && $hostname) || !$this->_options['domain']) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/Abstract.php
===================================================================
--- trunk/library/Zend/Validate/Abstract.php	(revision 5146)
+++ trunk/library/Zend/Validate/Abstract.php	(revision 5146)
@@ -0,0 +1,456 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Abstract.php 22472 2010-06-20 07:36:16Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Interface
+ */
+require_once 'Zend/Validate/Interface.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Validate_Abstract implements Zend_Validate_Interface
+{
+    /**
+     * The value to be validated
+     *
+     * @var mixed
+     */
+    protected $_value;
+
+    /**
+     * Additional variables available for validation failure messages
+     *
+     * @var array
+     */
+    protected $_messageVariables = array();
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array();
+
+    /**
+     * Array of validation failure messages
+     *
+     * @var array
+     */
+    protected $_messages = array();
+
+    /**
+     * Flag indidcating whether or not value should be obfuscated in error
+     * messages
+     * @var bool
+     */
+    protected $_obscureValue = false;
+
+    /**
+     * Array of validation failure message codes
+     *
+     * @var array
+     * @deprecated Since 1.5.0
+     */
+    protected $_errors = array();
+
+    /**
+     * Translation object
+     * @var Zend_Translate
+     */
+    protected $_translator;
+
+    /**
+     * Default translation object for all validate objects
+     * @var Zend_Translate
+     */
+    protected static $_defaultTranslator;
+
+    /**
+     * Is translation disabled?
+     * @var Boolean
+     */
+    protected $_translatorDisabled = false;
+
+    /**
+     * Limits the maximum returned length of a error message
+     *
+     * @var Integer
+     */
+    protected static $_messageLength = -1;
+
+    /**
+     * Returns array of validation failure messages
+     *
+     * @return array
+     */
+    public function getMessages()
+    {
+        return $this->_messages;
+    }
+
+    /**
+     * Returns an array of the names of variables that are used in constructing validation failure messages
+     *
+     * @return array
+     */
+    public function getMessageVariables()
+    {
+        return array_keys($this->_messageVariables);
+    }
+
+    /**
+     * Returns the message templates from the validator
+     *
+     * @return array
+     */
+    public function getMessageTemplates()
+    {
+        return $this->_messageTemplates;
+    }
+
+    /**
+     * Sets the validation failure message template for a particular key
+     *
+     * @param  string $messageString
+     * @param  string $messageKey     OPTIONAL
+     * @return Zend_Validate_Abstract Provides a fluent interface
+     * @throws Zend_Validate_Exception
+     */
+    public function setMessage($messageString, $messageKey = null)
+    {
+        if ($messageKey === null) {
+            $keys = array_keys($this->_messageTemplates);
+            foreach($keys as $key) {
+                $this->setMessage($messageString, $key);
+            }
+            return $this;
+        }
+
+        if (!isset($this->_messageTemplates[$messageKey])) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("No message template exists for key '$messageKey'");
+        }
+
+        $this->_messageTemplates[$messageKey] = $messageString;
+        return $this;
+    }
+
+    /**
+     * Sets validation failure message templates given as an array, where the array keys are the message keys,
+     * and the array values are the message template strings.
+     *
+     * @param  array $messages
+     * @return Zend_Validate_Abstract
+     */
+    public function setMessages(array $messages)
+    {
+        foreach ($messages as $key => $message) {
+            $this->setMessage($message, $key);
+        }
+        return $this;
+    }
+
+    /**
+     * Magic function returns the value of the requested property, if and only if it is the value or a
+     * message variable.
+     *
+     * @param  string $property
+     * @return mixed
+     * @throws Zend_Validate_Exception
+     */
+    public function __get($property)
+    {
+        if ($property == 'value') {
+            return $this->_value;
+        }
+        if (array_key_exists($property, $this->_messageVariables)) {
+            return $this->{$this->_messageVariables[$property]};
+        }
+        /**
+         * @see Zend_Validate_Exception
+         */
+        require_once 'Zend/Validate/Exception.php';
+        throw new Zend_Validate_Exception("No property exists by the name '$property'");
+    }
+
+    /**
+     * Constructs and returns a validation failure message with the given message key and value.
+     *
+     * Returns null if and only if $messageKey does not correspond to an existing template.
+     *
+     * If a translator is available and a translation exists for $messageKey,
+     * the translation will be used.
+     *
+     * @param  string $messageKey
+     * @param  string $value
+     * @return string
+     */
+    protected function _createMessage($messageKey, $value)
+    {
+        if (!isset($this->_messageTemplates[$messageKey])) {
+            return null;
+        }
+
+        $message = $this->_messageTemplates[$messageKey];
+
+        if (null !== ($translator = $this->getTranslator())) {
+            if ($translator->isTranslated($messageKey)) {
+                $message = $translator->translate($messageKey);
+            } else {
+                $message = $translator->translate($message);
+            }
+        }
+
+        if (is_object($value)) {
+            if (!in_array('__toString', get_class_methods($value))) {
+                $value = get_class($value) . ' object';
+            } else {
+                $value = $value->__toString();
+            }
+        } else {
+            $value = (string)$value;
+        }
+
+        if ($this->getObscureValue()) {
+            $value = str_repeat('*', strlen($value));
+        }
+
+        $message = str_replace('%value%', (string) $value, $message);
+        foreach ($this->_messageVariables as $ident => $property) {
+            $message = str_replace("%$ident%", (string) $this->$property, $message);
+        }
+
+        $length = self::getMessageLength();
+        if (($length > -1) && (strlen($message) > $length)) {
+            $message = substr($message, 0, (self::getMessageLength() - 3)) . '...';
+        }
+
+        return $message;
+    }
+
+    /**
+     * @param  string $messageKey
+     * @param  string $value      OPTIONAL
+     * @return void
+     */
+    protected function _error($messageKey, $value = null)
+    {
+        if ($messageKey === null) {
+            $keys = array_keys($this->_messageTemplates);
+            $messageKey = current($keys);
+        }
+        if ($value === null) {
+            $value = $this->_value;
+        }
+        $this->_errors[]              = $messageKey;
+        $this->_messages[$messageKey] = $this->_createMessage($messageKey, $value);
+    }
+
+    /**
+     * Sets the value to be validated and clears the messages and errors arrays
+     *
+     * @param  mixed $value
+     * @return void
+     */
+    protected function _setValue($value)
+    {
+        $this->_value    = $value;
+        $this->_messages = array();
+        $this->_errors   = array();
+    }
+
+    /**
+     * Returns array of validation failure message codes
+     *
+     * @return array
+     * @deprecated Since 1.5.0
+     */
+    public function getErrors()
+    {
+        return $this->_errors;
+    }
+
+    /**
+     * Set flag indicating whether or not value should be obfuscated in messages
+     *
+     * @param  bool $flag
+     * @return Zend_Validate_Abstract
+     */
+    public function setObscureValue($flag)
+    {
+        $this->_obscureValue = (bool) $flag;
+        return $this;
+    }
+
+    /**
+     * Retrieve flag indicating whether or not value should be obfuscated in
+     * messages
+     *
+     * @return bool
+     */
+    public function getObscureValue()
+    {
+        return $this->_obscureValue;
+    }
+
+    /**
+     * Set translation object
+     *
+     * @param  Zend_Translate|Zend_Translate_Adapter|null $translator
+     * @return Zend_Validate_Abstract
+     */
+    public function setTranslator($translator = null)
+    {
+        if ((null === $translator) || ($translator instanceof Zend_Translate_Adapter)) {
+            $this->_translator = $translator;
+        } elseif ($translator instanceof Zend_Translate) {
+            $this->_translator = $translator->getAdapter();
+        } else {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid translator specified');
+        }
+        return $this;
+    }
+
+    /**
+     * Return translation object
+     *
+     * @return Zend_Translate_Adapter|null
+     */
+    public function getTranslator()
+    {
+        if ($this->translatorIsDisabled()) {
+            return null;
+        }
+
+        if (null === $this->_translator) {
+            return self::getDefaultTranslator();
+        }
+
+        return $this->_translator;
+    }
+
+    /**
+     * Does this validator have its own specific translator?
+     *
+     * @return bool
+     */
+    public function hasTranslator()
+    {
+        return (bool)$this->_translator;
+    }
+
+    /**
+     * Set default translation object for all validate objects
+     *
+     * @param  Zend_Translate|Zend_Translate_Adapter|null $translator
+     * @return void
+     */
+    public static function setDefaultTranslator($translator = null)
+    {
+        if ((null === $translator) || ($translator instanceof Zend_Translate_Adapter)) {
+            self::$_defaultTranslator = $translator;
+        } elseif ($translator instanceof Zend_Translate) {
+            self::$_defaultTranslator = $translator->getAdapter();
+        } else {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid translator specified');
+        }
+    }
+
+    /**
+     * Get default translation object for all validate objects
+     *
+     * @return Zend_Translate_Adapter|null
+     */
+    public static function getDefaultTranslator()
+    {
+        if (null === self::$_defaultTranslator) {
+            require_once 'Zend/Registry.php';
+            if (Zend_Registry::isRegistered('Zend_Translate')) {
+                $translator = Zend_Registry::get('Zend_Translate');
+                if ($translator instanceof Zend_Translate_Adapter) {
+                    return $translator;
+                } elseif ($translator instanceof Zend_Translate) {
+                    return $translator->getAdapter();
+                }
+            }
+        }
+
+        return self::$_defaultTranslator;
+    }
+
+    /**
+     * Is there a default translation object set?
+     *
+     * @return boolean
+     */
+    public static function hasDefaultTranslator()
+    {
+        return (bool)self::$_defaultTranslator;
+    }
+
+    /**
+     * Indicate whether or not translation should be disabled
+     *
+     * @param  bool $flag
+     * @return Zend_Validate_Abstract
+     */
+    public function setDisableTranslator($flag)
+    {
+        $this->_translatorDisabled = (bool) $flag;
+        return $this;
+    }
+
+    /**
+     * Is translation disabled?
+     *
+     * @return bool
+     */
+    public function translatorIsDisabled()
+    {
+        return $this->_translatorDisabled;
+    }
+
+    /**
+     * Returns the maximum allowed message length
+     *
+     * @return integer
+     */
+    public static function getMessageLength()
+    {
+        return self::$_messageLength;
+    }
+
+    /**
+     * Sets the maximum allowed message length
+     *
+     * @param integer $length
+     */
+    public static function setMessageLength($length = -1)
+    {
+        self::$_messageLength = $length;
+    }
+}
Index: trunk/library/Zend/Validate/Float.php
===================================================================
--- trunk/library/Zend/Validate/Float.php	(revision 5146)
+++ trunk/library/Zend/Validate/Float.php	(revision 5146)
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Float.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @see Zend_Locale_Format
+ */
+require_once 'Zend/Locale/Format.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Float extends Zend_Validate_Abstract
+{
+    const INVALID   = 'floatInvalid';
+    const NOT_FLOAT = 'notFloat';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID   => "Invalid type given. String, integer or float expected",
+        self::NOT_FLOAT => "'%value%' does not appear to be a float",
+    );
+
+    protected $_locale;
+
+    /**
+     * Constructor for the float validator
+     *
+     * @param string|Zend_Config|Zend_Locale $locale
+     */
+    public function __construct($locale = null)
+    {
+        if ($locale instanceof Zend_Config) {
+            $locale = $locale->toArray();
+        }
+
+        if (is_array($locale)) {
+            if (array_key_exists('locale', $locale)) {
+                $locale = $locale['locale'];
+            } else {
+                $locale = null;
+            }
+        }
+
+        if (empty($locale)) {
+            require_once 'Zend/Registry.php';
+            if (Zend_Registry::isRegistered('Zend_Locale')) {
+                $locale = Zend_Registry::get('Zend_Locale');
+            }
+        }
+
+        $this->setLocale($locale);
+    }
+
+    /**
+     * Returns the set locale
+     */
+    public function getLocale()
+    {
+        return $this->_locale;
+    }
+
+    /**
+     * Sets the locale to use
+     *
+     * @param string|Zend_Locale $locale
+     */
+    public function setLocale($locale = null)
+    {
+        require_once 'Zend/Locale.php';
+        $this->_locale = Zend_Locale::findLocale($locale);
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value is a floating-point value
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value) && !is_int($value) && !is_float($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        if (is_float($value)) {
+            return true;
+        }
+
+        $this->_setValue($value);
+        try {
+            if (!Zend_Locale_Format::isFloat($value, array('locale' => $this->_locale))) {
+                $this->_error(self::NOT_FLOAT);
+                return false;
+            }
+        } catch (Zend_Locale_Exception $e) {
+            $this->_error(self::NOT_FLOAT);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/Ip.php
===================================================================
--- trunk/library/Zend/Validate/Ip.php	(revision 5146)
+++ trunk/library/Zend/Validate/Ip.php	(revision 5146)
@@ -0,0 +1,191 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Ip.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Ip extends Zend_Validate_Abstract
+{
+    const INVALID        = 'ipInvalid';
+    const NOT_IP_ADDRESS = 'notIpAddress';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID        => "Invalid type given. String expected",
+        self::NOT_IP_ADDRESS => "'%value%' does not appear to be a valid IP address",
+    );
+
+    /**
+     * internal options
+     *
+     * @var array
+     */
+    protected $_options = array(
+        'allowipv6' => true,
+        'allowipv4' => true
+    );
+
+    /**
+     * Sets validator options
+     *
+     * @param array $options OPTIONAL Options to set, see the manual for all available options
+     * @return void
+     */
+    public function __construct($options = array())
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } else if (!is_array($options)) {
+            $options = func_get_args();
+            $temp['allowipv6'] = array_shift($options);
+            if (!empty($options)) {
+                $temp['allowipv4'] = array_shift($options);
+            }
+
+            $options = $temp;
+        }
+
+        $options += $this->_options;
+        $this->setOptions($options);
+    }
+
+    /**
+     * Returns all set options
+     *
+     * @return array
+     */
+    public function getOptions()
+    {
+        return $this->_options;
+    }
+
+    /**
+     * Sets the options for this validator
+     *
+     * @param array $options
+     * @return Zend_Validate_Ip
+     */
+    public function setOptions($options)
+    {
+        if (array_key_exists('allowipv6', $options)) {
+            $this->_options['allowipv6'] = (boolean) $options['allowipv6'];
+        }
+
+        if (array_key_exists('allowipv4', $options)) {
+            $this->_options['allowipv4'] = (boolean) $options['allowipv4'];
+        }
+
+        if (!$this->_options['allowipv4'] && !$this->_options['allowipv6']) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Nothing to validate. Check your options');
+        }
+
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value is a valid IP address
+     *
+     * @param  mixed $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+        if (($this->_options['allowipv4'] && !$this->_options['allowipv6'] && !$this->_validateIPv4($value)) ||
+            (!$this->_options['allowipv4'] && $this->_options['allowipv6'] && !$this->_validateIPv6($value)) ||
+            ($this->_options['allowipv4'] && $this->_options['allowipv6'] && !$this->_validateIPv4($value) && !$this->_validateIPv6($value))) {
+            $this->_error(self::NOT_IP_ADDRESS);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Validates an IPv4 address
+     *
+     * @param string $value
+     */
+    protected function _validateIPv4($value) {
+        $ip2long = ip2long($value);
+        if($ip2long === false) {
+            return false;
+        }
+
+        return $value == long2ip($ip2long);
+    }
+
+    /**
+     * Validates an IPv6 address
+     *
+     * @param  string $value Value to check against
+     * @return boolean True when $value is a valid ipv6 address
+     *                 False otherwise
+     */
+    protected function _validateIPv6($value) {
+        if (strlen($value) < 3) {
+            return $value == '::';
+        }
+
+        if (strpos($value, '.')) {
+            $lastcolon = strrpos($value, ':');
+            if (!($lastcolon && $this->_validateIPv4(substr($value, $lastcolon + 1)))) {
+                return false;
+            }
+
+            $value = substr($value, 0, $lastcolon) . ':0:0';
+        }
+
+        if (strpos($value, '::') === false) {
+            return preg_match('/\A(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}\z/i', $value);
+        }
+
+        $colonCount = substr_count($value, ':');
+        if ($colonCount < 8) {
+            return preg_match('/\A(?::|(?:[a-f0-9]{1,4}:)+):(?:(?:[a-f0-9]{1,4}:)*[a-f0-9]{1,4})?\z/i', $value);
+        }
+
+        // special case with ending or starting double colon
+        if ($colonCount == 8) {
+            return preg_match('/\A(?:::)?(?:[a-f0-9]{1,4}:){6}[a-f0-9]{1,4}(?:::)?\z/i', $value);
+        }
+
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/Alnum.php
===================================================================
--- trunk/library/Zend/Validate/Alnum.php	(revision 5146)
+++ trunk/library/Zend/Validate/Alnum.php	(revision 5146)
@@ -0,0 +1,150 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Alnum.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Alnum extends Zend_Validate_Abstract
+{
+    const INVALID      = 'alnumInvalid';
+    const NOT_ALNUM    = 'notAlnum';
+    const STRING_EMPTY = 'alnumStringEmpty';
+
+    /**
+     * Whether to allow white space characters; off by default
+     *
+     * @var boolean
+     * @deprecated
+     */
+    public $allowWhiteSpace;
+
+    /**
+     * Alphanumeric filter used for validation
+     *
+     * @var Zend_Filter_Alnum
+     */
+    protected static $_filter = null;
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID      => "Invalid type given. String, integer or float expected",
+        self::NOT_ALNUM    => "'%value%' contains characters which are non alphabetic and no digits",
+        self::STRING_EMPTY => "'%value%' is an empty string",
+    );
+
+    /**
+     * Sets default option values for this instance
+     *
+     * @param  boolean|Zend_Config $allowWhiteSpace
+     * @return void
+     */
+    public function __construct($allowWhiteSpace = false)
+    {
+        if ($allowWhiteSpace instanceof Zend_Config) {
+            $allowWhiteSpace = $allowWhiteSpace->toArray();
+        }
+
+        if (is_array($allowWhiteSpace)) {
+            if (array_key_exists('allowWhiteSpace', $allowWhiteSpace)) {
+                $allowWhiteSpace = $allowWhiteSpace['allowWhiteSpace'];
+            } else {
+                $allowWhiteSpace = false;
+            }
+        }
+
+        $this->allowWhiteSpace = (boolean) $allowWhiteSpace;
+    }
+
+    /**
+     * Returns the allowWhiteSpace option
+     *
+     * @return boolean
+     */
+    public function getAllowWhiteSpace()
+    {
+        return $this->allowWhiteSpace;
+    }
+
+    /**
+     * Sets the allowWhiteSpace option
+     *
+     * @param boolean $allowWhiteSpace
+     * @return Zend_Filter_Alnum Provides a fluent interface
+     */
+    public function setAllowWhiteSpace($allowWhiteSpace)
+    {
+        $this->allowWhiteSpace = (boolean) $allowWhiteSpace;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value contains only alphabetic and digit characters
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value) && !is_int($value) && !is_float($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+
+        if ('' === $value) {
+            $this->_error(self::STRING_EMPTY);
+            return false;
+        }
+
+        if (null === self::$_filter) {
+            /**
+             * @see Zend_Filter_Alnum
+             */
+            require_once 'Zend/Filter/Alnum.php';
+            self::$_filter = new Zend_Filter_Alnum();
+        }
+
+        self::$_filter->allowWhiteSpace = $this->allowWhiteSpace;
+
+        if ($value != self::$_filter->filter($value)) {
+            $this->_error(self::NOT_ALNUM);
+            return false;
+        }
+
+        return true;
+    }
+
+}
Index: trunk/library/Zend/Validate/NotEmpty.php
===================================================================
--- trunk/library/Zend/Validate/NotEmpty.php	(revision 5146)
+++ trunk/library/Zend/Validate/NotEmpty.php	(revision 5146)
@@ -0,0 +1,279 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: NotEmpty.php 22691 2010-07-26 19:29:14Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_NotEmpty extends Zend_Validate_Abstract
+{
+    const BOOLEAN       = 1;
+    const INTEGER       = 2;
+    const FLOAT         = 4;
+    const STRING        = 8;
+    const ZERO          = 16;
+    const EMPTY_ARRAY   = 32;
+    const NULL          = 64;
+    const PHP           = 127;
+    const SPACE         = 128;
+    const OBJECT        = 256;
+    const OBJECT_STRING = 512;
+    const OBJECT_COUNT  = 1024;
+    const ALL           = 2047;
+
+    const INVALID  = 'notEmptyInvalid';
+    const IS_EMPTY = 'isEmpty';
+
+    protected $_constants = array(
+        self::BOOLEAN       => 'boolean',
+        self::INTEGER       => 'integer',
+        self::FLOAT         => 'float',
+        self::STRING        => 'string',
+        self::ZERO          => 'zero',
+        self::EMPTY_ARRAY   => 'array',
+        self::NULL          => 'null',
+        self::PHP           => 'php',
+        self::SPACE         => 'space',
+        self::OBJECT        => 'object',
+        self::OBJECT_STRING => 'objectstring',
+        self::OBJECT_COUNT  => 'objectcount',
+        self::ALL           => 'all',
+    );
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::IS_EMPTY => "Value is required and can't be empty",
+        self::INVALID  => "Invalid type given. String, integer, float, boolean or array expected",
+    );
+
+    /**
+     * Internal type to detect
+     *
+     * @var integer
+     */
+    protected $_type = 493;
+
+    /**
+     * Constructor
+     *
+     * @param string|array|Zend_Config $options OPTIONAL
+     */
+    public function __construct($options = null)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } else if (!is_array($options)) {
+            $options = func_get_args();
+            $temp    = array();
+            if (!empty($options)) {
+                $temp['type'] = array_shift($options);
+            }
+
+            $options = $temp;
+        }
+
+        if (is_array($options) && array_key_exists('type', $options)) {
+            $this->setType($options['type']);
+        }
+    }
+
+    /**
+     * Returns the set types
+     *
+     * @return array
+     */
+    public function getType()
+    {
+        return $this->_type;
+    }
+
+    /**
+     * Set the types
+     *
+     * @param  integer|array $type
+     * @throws Zend_Validate_Exception
+     * @return Zend_Validate_NotEmpty
+     */
+    public function setType($type = null)
+    {
+        if (is_array($type)) {
+            $detected = 0;
+            foreach($type as $value) {
+                if (is_int($value)) {
+                    $detected += $value;
+                } else if (in_array($value, $this->_constants)) {
+                    $detected += array_search($value, $this->_constants);
+                }
+            }
+
+            $type = $detected;
+        } else if (is_string($type) && in_array($type, $this->_constants)) {
+            $type = array_search($type, $this->_constants);
+        }
+
+        if (!is_int($type) || ($type < 0) || ($type > self::ALL)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Unknown type');
+        }
+
+        $this->_type = $type;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value is not an empty value.
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if ($value !== null && !is_string($value) && !is_int($value) && !is_float($value) &&
+            !is_bool($value) && !is_array($value) && !is_object($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $type    = $this->getType();
+        $this->_setValue($value);
+        $object  = false;
+
+        // OBJECT_COUNT (countable object)
+        if ($type >= self::OBJECT_COUNT) {
+            $type -= self::OBJECT_COUNT;
+            $object = true;
+
+            if (is_object($value) && ($value instanceof Countable) && (count($value) == 0)) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        // OBJECT_STRING (object's toString)
+        if ($type >= self::OBJECT_STRING) {
+            $type -= self::OBJECT_STRING;
+            $object = true;
+
+            if ((is_object($value) && (!method_exists($value, '__toString'))) ||
+                (is_object($value) && (method_exists($value, '__toString')) && (((string) $value) == ""))) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        // OBJECT (object)
+        if ($type >= self::OBJECT) {
+            $type -= self::OBJECT;
+            // fall trough, objects are always not empty
+        } else if ($object === false) {
+            // object not allowed but object given -> return false
+            if (is_object($value)) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        // SPACE ('   ')
+        if ($type >= self::SPACE) {
+            $type -= self::SPACE;
+            if (is_string($value) && (preg_match('/^\s+$/s', $value))) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        // NULL (null)
+        if ($type >= self::NULL) {
+            $type -= self::NULL;
+            if ($value === null) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        // EMPTY_ARRAY (array())
+        if ($type >= self::EMPTY_ARRAY) {
+            $type -= self::EMPTY_ARRAY;
+            if (is_array($value) && ($value == array())) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        // ZERO ('0')
+        if ($type >= self::ZERO) {
+            $type -= self::ZERO;
+            if (is_string($value) && ($value == '0')) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        // STRING ('')
+        if ($type >= self::STRING) {
+            $type -= self::STRING;
+            if (is_string($value) && ($value == '')) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        // FLOAT (0.0)
+        if ($type >= self::FLOAT) {
+            $type -= self::FLOAT;
+            if (is_float($value) && ($value == 0.0)) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        // INTEGER (0)
+        if ($type >= self::INTEGER) {
+            $type -= self::INTEGER;
+            if (is_int($value) && ($value == 0)) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        // BOOLEAN (false)
+        if ($type >= self::BOOLEAN) {
+            $type -= self::BOOLEAN;
+            if (is_bool($value) && ($value == false)) {
+                $this->_error(self::IS_EMPTY);
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/Between.php
===================================================================
--- trunk/library/Zend/Validate/Between.php	(revision 5146)
+++ trunk/library/Zend/Validate/Between.php	(revision 5146)
@@ -0,0 +1,224 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Between.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Between extends Zend_Validate_Abstract
+{
+    /**
+     * Validation failure message key for when the value is not between the min and max, inclusively
+     */
+    const NOT_BETWEEN        = 'notBetween';
+
+    /**
+     * Validation failure message key for when the value is not strictly between the min and max
+     */
+    const NOT_BETWEEN_STRICT = 'notBetweenStrict';
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOT_BETWEEN        => "'%value%' is not between '%min%' and '%max%', inclusively",
+        self::NOT_BETWEEN_STRICT => "'%value%' is not strictly between '%min%' and '%max%'"
+    );
+
+    /**
+     * Additional variables available for validation failure messages
+     *
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'min' => '_min',
+        'max' => '_max'
+    );
+
+    /**
+     * Minimum value
+     *
+     * @var mixed
+     */
+    protected $_min;
+
+    /**
+     * Maximum value
+     *
+     * @var mixed
+     */
+    protected $_max;
+
+    /**
+     * Whether to do inclusive comparisons, allowing equivalence to min and/or max
+     *
+     * If false, then strict comparisons are done, and the value may equal neither
+     * the min nor max options
+     *
+     * @var boolean
+     */
+    protected $_inclusive;
+
+    /**
+     * Sets validator options
+     * Accepts the following option keys:
+     *   'min' => scalar, minimum border
+     *   'max' => scalar, maximum border
+     *   'inclusive' => boolean, inclusive border values
+     *
+     * @param  array|Zend_Config $options
+     * @return void
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } else if (!is_array($options)) {
+            $options = func_get_args();
+            $temp['min'] = array_shift($options);
+            if (!empty($options)) {
+                $temp['max'] = array_shift($options);
+            }
+
+            if (!empty($options)) {
+                $temp['inclusive'] = array_shift($options);
+            }
+
+            $options = $temp;
+        }
+
+        if (!array_key_exists('min', $options) || !array_key_exists('max', $options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("Missing option. 'min' and 'max' has to be given");
+        }
+
+        if (!array_key_exists('inclusive', $options)) {
+            $options['inclusive'] = true;
+        }
+
+        $this->setMin($options['min'])
+             ->setMax($options['max'])
+             ->setInclusive($options['inclusive']);
+    }
+
+    /**
+     * Returns the min option
+     *
+     * @return mixed
+     */
+    public function getMin()
+    {
+        return $this->_min;
+    }
+
+    /**
+     * Sets the min option
+     *
+     * @param  mixed $min
+     * @return Zend_Validate_Between Provides a fluent interface
+     */
+    public function setMin($min)
+    {
+        $this->_min = $min;
+        return $this;
+    }
+
+    /**
+     * Returns the max option
+     *
+     * @return mixed
+     */
+    public function getMax()
+    {
+        return $this->_max;
+    }
+
+    /**
+     * Sets the max option
+     *
+     * @param  mixed $max
+     * @return Zend_Validate_Between Provides a fluent interface
+     */
+    public function setMax($max)
+    {
+        $this->_max = $max;
+        return $this;
+    }
+
+    /**
+     * Returns the inclusive option
+     *
+     * @return boolean
+     */
+    public function getInclusive()
+    {
+        return $this->_inclusive;
+    }
+
+    /**
+     * Sets the inclusive option
+     *
+     * @param  boolean $inclusive
+     * @return Zend_Validate_Between Provides a fluent interface
+     */
+    public function setInclusive($inclusive)
+    {
+        $this->_inclusive = $inclusive;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value is between min and max options, inclusively
+     * if inclusive option is true.
+     *
+     * @param  mixed $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        $this->_setValue($value);
+
+        if ($this->_inclusive) {
+            if ($this->_min > $value || $value > $this->_max) {
+                $this->_error(self::NOT_BETWEEN);
+                return false;
+            }
+        } else {
+            if ($this->_min >= $value || $value >= $this->_max) {
+                $this->_error(self::NOT_BETWEEN_STRICT);
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
Index: trunk/library/Zend/Validate/Int.php
===================================================================
--- trunk/library/Zend/Validate/Int.php	(revision 5146)
+++ trunk/library/Zend/Validate/Int.php	(revision 5146)
@@ -0,0 +1,148 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Int.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @see Zend_Locale_Format
+ */
+require_once 'Zend/Locale/Format.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Int extends Zend_Validate_Abstract
+{
+    const INVALID = 'intInvalid';
+    const NOT_INT = 'notInt';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID => "Invalid type given. String or integer expected",
+        self::NOT_INT => "'%value%' does not appear to be an integer",
+    );
+
+    protected $_locale;
+
+    /**
+     * Constructor for the integer validator
+     *
+     * @param string|Zend_Config|Zend_Locale $locale
+     */
+    public function __construct($locale = null)
+    {
+        if ($locale instanceof Zend_Config) {
+            $locale = $locale->toArray();
+        }
+
+        if (is_array($locale)) {
+            if (array_key_exists('locale', $locale)) {
+                $locale = $locale['locale'];
+            } else {
+                $locale = null;
+            }
+        }
+
+        if (empty($locale)) {
+            require_once 'Zend/Registry.php';
+            if (Zend_Registry::isRegistered('Zend_Locale')) {
+                $locale = Zend_Registry::get('Zend_Locale');
+            }
+        }
+
+        if ($locale !== null) {
+            $this->setLocale($locale);
+        }
+    }
+
+    /**
+     * Returns the set locale
+     */
+    public function getLocale()
+    {
+        return $this->_locale;
+    }
+
+    /**
+     * Sets the locale to use
+     *
+     * @param string|Zend_Locale $locale
+     */
+    public function setLocale($locale = null)
+    {
+        require_once 'Zend/Locale.php';
+        $this->_locale = Zend_Locale::findLocale($locale);
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value is a valid integer
+     *
+     * @param  string|integer $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value) && !is_int($value) && !is_float($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        if (is_int($value)) {
+            return true;
+        }
+
+        $this->_setValue($value);
+        if ($this->_locale === null) {
+            $locale        = localeconv();
+            $valueFiltered = str_replace($locale['decimal_point'], '.', $value);
+            $valueFiltered = str_replace($locale['thousands_sep'], '', $valueFiltered);
+
+            if (strval(intval($valueFiltered)) != $valueFiltered) {
+                $this->_error(self::NOT_INT);
+                return false;
+            }
+
+        } else {
+            try {
+                if (!Zend_Locale_Format::isInteger($value, array('locale' => $this->_locale))) {
+                    $this->_error(self::NOT_INT);
+                    return false;
+                }
+            } catch (Zend_Locale_Exception $e) {
+                $this->_error(self::NOT_INT);
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/Exception.php
===================================================================
--- trunk/library/Zend/Validate/Exception.php	(revision 5146)
+++ trunk/library/Zend/Validate/Exception.php	(revision 5146)
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Exception extends Zend_Exception
+{}
Index: trunk/library/Zend/Validate/Db/NoRecordExists.php
===================================================================
--- trunk/library/Zend/Validate/Db/NoRecordExists.php	(revision 5146)
+++ trunk/library/Zend/Validate/Db/NoRecordExists.php	(revision 5146)
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: NoRecordExists.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Validate_Db_Abstract
+ */
+require_once 'Zend/Validate/Db/Abstract.php';
+
+/**
+ * Confirms a record does not exist in a table.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @uses       Zend_Validate_Db_Abstract
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Db_NoRecordExists extends Zend_Validate_Db_Abstract
+{
+    public function isValid($value)
+    {
+        $valid = true;
+        $this->_setValue($value);
+
+        $result = $this->_query($value);
+        if ($result) {
+            $valid = false;
+            $this->_error(self::ERROR_RECORD_FOUND);
+        }
+
+        return $valid;
+    }
+}
Index: trunk/library/Zend/Validate/Db/RecordExists.php
===================================================================
--- trunk/library/Zend/Validate/Db/RecordExists.php	(revision 5146)
+++ trunk/library/Zend/Validate/Db/RecordExists.php	(revision 5146)
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: RecordExists.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Validate_Db_Abstract
+ */
+require_once 'Zend/Validate/Db/Abstract.php';
+
+/**
+ * Confirms a record exists in a table.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @uses       Zend_Validate_Db_Abstract
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Db_RecordExists extends Zend_Validate_Db_Abstract
+{
+    public function isValid($value)
+    {
+        $valid = true;
+        $this->_setValue($value);
+
+        $result = $this->_query($value);
+        if (!$result) {
+            $valid = false;
+            $this->_error(self::ERROR_NO_RECORD_FOUND);
+        }
+
+        return $valid;
+    }
+}
Index: trunk/library/Zend/Validate/Db/Abstract.php
===================================================================
--- trunk/library/Zend/Validate/Db/Abstract.php	(revision 5146)
+++ trunk/library/Zend/Validate/Db/Abstract.php	(revision 5146)
@@ -0,0 +1,351 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Abstract.php 23484 2010-12-10 03:57:59Z mjh_ca $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Class for Database record validation
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @uses       Zend_Validate_Abstract
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Validate_Db_Abstract extends Zend_Validate_Abstract
+{
+    /**
+     * Error constants
+     */
+    const ERROR_NO_RECORD_FOUND = 'noRecordFound';
+    const ERROR_RECORD_FOUND    = 'recordFound';
+
+    /**
+     * @var array Message templates
+     */
+    protected $_messageTemplates = array(
+        self::ERROR_NO_RECORD_FOUND => "No record matching '%value%' was found",
+        self::ERROR_RECORD_FOUND    => "A record matching '%value%' was found",
+    );
+
+    /**
+     * @var string
+     */
+    protected $_schema = null;
+
+    /**
+     * @var string
+     */
+    protected $_table = '';
+
+    /**
+     * @var string
+     */
+    protected $_field = '';
+
+    /**
+     * @var mixed
+     */
+    protected $_exclude = null;
+
+    /**
+     * Database adapter to use. If null isValid() will use Zend_Db::getInstance instead
+     *
+     * @var unknown_type
+     */
+    protected $_adapter = null;
+
+    /**
+     * Select object to use. can be set, or will be auto-generated
+     * @var Zend_Db_Select
+     */
+    protected $_select;
+
+    /**
+     * Provides basic configuration for use with Zend_Validate_Db Validators
+     * Setting $exclude allows a single record to be excluded from matching.
+     * Exclude can either be a String containing a where clause, or an array with `field` and `value` keys
+     * to define the where clause added to the sql.
+     * A database adapter may optionally be supplied to avoid using the registered default adapter.
+     *
+     * The following option keys are supported:
+     * 'table'   => The database table to validate against
+     * 'schema'  => The schema keys
+     * 'field'   => The field to check for a match
+     * 'exclude' => An optional where clause or field/value pair to exclude from the query
+     * 'adapter' => An optional database adapter to use
+     *
+     * @param array|Zend_Config $options Options to use for this validator
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Db_Select) {
+            $this->setSelect($options);
+            return;
+        }
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } else if (func_num_args() > 1) {
+            $options       = func_get_args();
+            $temp['table'] = array_shift($options);
+            $temp['field'] = array_shift($options);
+            if (!empty($options)) {
+                $temp['exclude'] = array_shift($options);
+            }
+
+            if (!empty($options)) {
+                $temp['adapter'] = array_shift($options);
+            }
+
+            $options = $temp;
+        }
+
+        if (!array_key_exists('table', $options) && !array_key_exists('schema', $options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Table or Schema option missing!');
+        }
+
+        if (!array_key_exists('field', $options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Field option missing!');
+        }
+
+        if (array_key_exists('adapter', $options)) {
+            $this->setAdapter($options['adapter']);
+        }
+
+        if (array_key_exists('exclude', $options)) {
+            $this->setExclude($options['exclude']);
+        }
+
+        $this->setField($options['field']);
+        if (array_key_exists('table', $options)) {
+            $this->setTable($options['table']);
+        }
+
+        if (array_key_exists('schema', $options)) {
+            $this->setSchema($options['schema']);
+        }
+    }
+
+    /**
+     * Returns the set adapter
+     *
+     * @return Zend_Db_Adapter
+     */
+    public function getAdapter()
+    {
+        /**
+         * Check for an adapter being defined. if not, fetch the default adapter.
+         */
+        if ($this->_adapter === null) {
+            $this->_adapter = Zend_Db_Table_Abstract::getDefaultAdapter();
+            if (null === $this->_adapter) {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception('No database adapter present');
+            }
+        }
+        return $this->_adapter;
+    }
+
+    /**
+     * Sets a new database adapter
+     *
+     * @param  Zend_Db_Adapter_Abstract $adapter
+     * @return Zend_Validate_Db_Abstract
+     */
+    public function setAdapter($adapter)
+    {
+        if (!($adapter instanceof Zend_Db_Adapter_Abstract)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Adapter option must be a database adapter!');
+        }
+
+        $this->_adapter = $adapter;
+        return $this;
+    }
+
+    /**
+     * Returns the set exclude clause
+     *
+     * @return string|array
+     */
+    public function getExclude()
+    {
+        return $this->_exclude;
+    }
+
+    /**
+     * Sets a new exclude clause
+     *
+     * @param string|array $exclude
+     * @return Zend_Validate_Db_Abstract
+     */
+    public function setExclude($exclude)
+    {
+        $this->_exclude = $exclude;
+        return $this;
+    }
+
+    /**
+     * Returns the set field
+     *
+     * @return string|array
+     */
+    public function getField()
+    {
+        return $this->_field;
+    }
+
+    /**
+     * Sets a new field
+     *
+     * @param string $field
+     * @return Zend_Validate_Db_Abstract
+     */
+    public function setField($field)
+    {
+        $this->_field = (string) $field;
+        return $this;
+    }
+
+    /**
+     * Returns the set table
+     *
+     * @return string
+     */
+    public function getTable()
+    {
+        return $this->_table;
+    }
+
+    /**
+     * Sets a new table
+     *
+     * @param string $table
+     * @return Zend_Validate_Db_Abstract
+     */
+    public function setTable($table)
+    {
+        $this->_table = (string) $table;
+        return $this;
+    }
+
+    /**
+     * Returns the set schema
+     *
+     * @return string
+     */
+    public function getSchema()
+    {
+        return $this->_schema;
+    }
+
+    /**
+     * Sets a new schema
+     *
+     * @param string $schema
+     * @return Zend_Validate_Db_Abstract
+     */
+    public function setSchema($schema)
+    {
+        $this->_schema = $schema;
+        return $this;
+    }
+
+    /**
+     * Sets the select object to be used by the validator
+     *
+     * @param Zend_Db_Select $select
+     * @return Zend_Validate_Db_Abstract
+     */
+    public function setSelect($select)
+    {
+        if (!$select instanceof Zend_Db_Select) {
+            throw new Zend_Validate_Exception('Select option must be a valid ' .
+                                              'Zend_Db_Select object');
+        }
+        $this->_select = $select;
+        return $this;
+    }
+
+    /**
+     * Gets the select object to be used by the validator.
+     * If no select object was supplied to the constructor,
+     * then it will auto-generate one from the given table,
+     * schema, field, and adapter options.
+     *
+     * @return Zend_Db_Select The Select object which will be used
+     */
+    public function getSelect()
+    {
+        if (null === $this->_select) {
+            $db = $this->getAdapter();
+            /**
+             * Build select object
+             */
+            $select = new Zend_Db_Select($db);
+            $select->from($this->_table, array($this->_field), $this->_schema);
+            if ($db->supportsParameters('named')) {
+                $select->where($db->quoteIdentifier($this->_field, true).' = :value'); // named
+            } else {
+                $select->where($db->quoteIdentifier($this->_field, true).' = ?'); // positional
+            }
+            if ($this->_exclude !== null) {
+                if (is_array($this->_exclude)) {
+                    $select->where(
+                          $db->quoteIdentifier($this->_exclude['field'], true) .
+                            ' != ?', $this->_exclude['value']
+                    );
+                } else {
+                    $select->where($this->_exclude);
+                }
+            }
+            $select->limit(1);
+            $this->_select = $select;
+        }
+        return $this->_select;
+    }
+
+    /**
+     * Run query and returns matches, or null if no matches are found.
+     *
+     * @param  String $value
+     * @return Array when matches are found.
+     */
+    protected function _query($value)
+    {
+        $select = $this->getSelect();
+        /**
+         * Run query
+         */
+        $result = $select->getAdapter()->fetchRow(
+            $select,
+            array('value' => $value), // this should work whether db supports positional or named params
+            Zend_Db::FETCH_ASSOC
+            );
+
+        return $result;
+    }
+}
Index: trunk/library/Zend/Validate/Barcode.php
===================================================================
--- trunk/library/Zend/Validate/Barcode.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode.php	(revision 5146)
@@ -0,0 +1,228 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Barcode.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode extends Zend_Validate_Abstract
+{
+    const INVALID        = 'barcodeInvalid';
+    const FAILED         = 'barcodeFailed';
+    const INVALID_CHARS  = 'barcodeInvalidChars';
+    const INVALID_LENGTH = 'barcodeInvalidLength';
+
+    protected $_messageTemplates = array(
+        self::FAILED         => "'%value%' failed checksum validation",
+        self::INVALID_CHARS  => "'%value%' contains invalid characters",
+        self::INVALID_LENGTH => "'%value%' should have a length of %length% characters",
+        self::INVALID        => "Invalid type given. String expected",
+    );
+
+    /**
+     * Additional variables available for validation failure messages
+     *
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'length' => '_length'
+    );
+
+    /**
+     * Length for the set subtype
+     *
+     * @var integer
+     */
+    protected $_length;
+
+    /**
+     * Barcode adapter
+     *
+     * @var Zend_Validate_Barcode_BarcodeAdapter
+     */
+    protected $_adapter;
+
+    /**
+     * Generates the standard validator object
+     *
+     * @param  string|Zend_Config|
+     *         Zend_Validate_Barcode_BarcodeAdapter $adapter Barcode adapter to use
+     * @return void
+     * @throws Zend_Validate_Exception
+     */
+    public function __construct($adapter)
+    {
+        if ($adapter instanceof Zend_Config) {
+            $adapter = $adapter->toArray();
+        }
+
+        $options  = null;
+        $checksum = null;
+        if (is_array($adapter)) {
+            if (array_key_exists('options', $adapter)) {
+                $options = $adapter['options'];
+            }
+
+            if (array_key_exists('checksum', $adapter)) {
+                $checksum = $adapter['checksum'];
+            }
+
+            if (array_key_exists('adapter', $adapter)) {
+                $adapter = $adapter['adapter'];
+            } else {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception("Missing option 'adapter'");
+            }
+        }
+
+        $this->setAdapter($adapter, $options);
+        if ($checksum !== null) {
+            $this->setChecksum($checksum);
+        }
+    }
+
+    /**
+     * Returns the set adapter
+     *
+     * @return Zend_Validate_Barcode_BarcodeAdapter
+     */
+    public function getAdapter()
+    {
+        return $this->_adapter;
+    }
+
+    /**
+     * Sets a new barcode adapter
+     *
+     * @param  string|Zend_Validate_Barcode $adapter Barcode adapter to use
+     * @param  array  $options Options for this adapter
+     * @return void
+     * @throws Zend_Validate_Exception
+     */
+    public function setAdapter($adapter, $options = null)
+    {
+        $adapter = ucfirst(strtolower($adapter));
+        require_once 'Zend/Loader.php';
+        if (Zend_Loader::isReadable('Zend/Validate/Barcode/' . $adapter. '.php')) {
+            $adapter = 'Zend_Validate_Barcode_' . $adapter;
+        }
+
+        if (!class_exists($adapter)) {
+            Zend_Loader::loadClass($adapter);
+        }
+
+        $this->_adapter = new $adapter($options);
+        if (!$this->_adapter instanceof Zend_Validate_Barcode_AdapterInterface) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception(
+                "Adapter " . $adapter . " does not implement Zend_Validate_Barcode_AdapterInterface"
+            );
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the checksum option
+     *
+     * @return boolean
+     */
+    public function getChecksum()
+    {
+        return $this->getAdapter()->getCheck();
+    }
+
+    /**
+     * Sets the checksum option
+     *
+     * @param  boolean $checksum
+     * @return Zend_Validate_Barcode
+     */
+    public function setChecksum($checksum)
+    {
+        $this->getAdapter()->setCheck($checksum);
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value contains a valid barcode
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+        $adapter       = $this->getAdapter();
+        $this->_length = $adapter->getLength();
+        $result        = $adapter->checkLength($value);
+        if (!$result) {
+            if (is_array($this->_length)) {
+                $temp = $this->_length;
+                $this->_length = "";
+                foreach($temp as $length) {
+                    $this->_length .= "/";
+                    $this->_length .= $length;
+                }
+
+                $this->_length = substr($this->_length, 1);
+            }
+
+            $this->_error(self::INVALID_LENGTH);
+            return false;
+        }
+
+        $result = $adapter->checkChars($value);
+        if (!$result) {
+            $this->_error(self::INVALID_CHARS);
+            return false;
+        }
+
+        if ($this->getChecksum()) {
+            $result = $adapter->checksum($value);
+            if (!$result) {
+                $this->_error(self::FAILED);
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/GreaterThan.php
===================================================================
--- trunk/library/Zend/Validate/GreaterThan.php	(revision 5146)
+++ trunk/library/Zend/Validate/GreaterThan.php	(revision 5146)
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: GreaterThan.php 20352 2010-01-17 17:55:38Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_GreaterThan extends Zend_Validate_Abstract
+{
+
+    const NOT_GREATER = 'notGreaterThan';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOT_GREATER => "'%value%' is not greater than '%min%'",
+    );
+
+    /**
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'min' => '_min'
+    );
+
+    /**
+     * Minimum value
+     *
+     * @var mixed
+     */
+    protected $_min;
+
+    /**
+     * Sets validator options
+     *
+     * @param  mixed|Zend_Config $min
+     * @return void
+     */
+    public function __construct($min)
+    {
+        if ($min instanceof Zend_Config) {
+            $min = $min->toArray();
+        }
+
+        if (is_array($min)) {
+            if (array_key_exists('min', $min)) {
+                $min = $min['min'];
+            } else {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception("Missing option 'min'");
+            }
+        }
+
+        $this->setMin($min);
+    }
+
+    /**
+     * Returns the min option
+     *
+     * @return mixed
+     */
+    public function getMin()
+    {
+        return $this->_min;
+    }
+
+    /**
+     * Sets the min option
+     *
+     * @param  mixed $min
+     * @return Zend_Validate_GreaterThan Provides a fluent interface
+     */
+    public function setMin($min)
+    {
+        $this->_min = $min;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value is greater than min option
+     *
+     * @param  mixed $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        $this->_setValue($value);
+
+        if ($this->_min >= $value) {
+            $this->_error(self::NOT_GREATER);
+            return false;
+        }
+        return true;
+    }
+
+}
Index: trunk/library/Zend/Validate/Ccnum.php
===================================================================
--- trunk/library/Zend/Validate/Ccnum.php	(revision 5146)
+++ trunk/library/Zend/Validate/Ccnum.php	(revision 5146)
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Ccnum.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Ccnum extends Zend_Validate_Abstract
+{
+    /**
+     * Validation failure message key for when the value is not of valid length
+     */
+    const LENGTH   = 'ccnumLength';
+
+    /**
+     * Validation failure message key for when the value fails the mod-10 checksum
+     */
+    const CHECKSUM = 'ccnumChecksum';
+
+    /**
+     * Digits filter for input
+     *
+     * @var Zend_Filter_Digits
+     */
+    protected static $_filter = null;
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::LENGTH   => "'%value%' must contain between 13 and 19 digits",
+        self::CHECKSUM => "Luhn algorithm (mod-10 checksum) failed on '%value%'"
+    );
+
+    public function __construct()
+    {
+        trigger_error('Using the Ccnum validator is deprecated in favor of the CreditCard validator');
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value follows the Luhn algorithm (mod-10 checksum)
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        $this->_setValue($value);
+
+        if (null === self::$_filter) {
+            /**
+             * @see Zend_Filter_Digits
+             */
+            require_once 'Zend/Filter/Digits.php';
+            self::$_filter = new Zend_Filter_Digits();
+        }
+
+        $valueFiltered = self::$_filter->filter($value);
+
+        $length = strlen($valueFiltered);
+
+        if ($length < 13 || $length > 19) {
+            $this->_error(self::LENGTH);
+            return false;
+        }
+
+        $sum    = 0;
+        $weight = 2;
+
+        for ($i = $length - 2; $i >= 0; $i--) {
+            $digit = $weight * $valueFiltered[$i];
+            $sum += floor($digit / 10) + $digit % 10;
+            $weight = $weight % 2 + 1;
+        }
+
+        if ((10 - $sum % 10) % 10 != $valueFiltered[$length - 1]) {
+            $this->_error(self::CHECKSUM, $valueFiltered);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Royalmail.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Royalmail.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Royalmail.php	(revision 5146)
@@ -0,0 +1,121 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Royalmail.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Royalmail extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = -1;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+    protected $_rows = array(
+        '0' => 1, '1' => 1, '2' => 1, '3' => 1, '4' => 1, '5' => 1,
+        '6' => 2, '7' => 2, '8' => 2, '9' => 2, 'A' => 2, 'B' => 2,
+        'C' => 3, 'D' => 3, 'E' => 3, 'F' => 3, 'G' => 3, 'H' => 3,
+        'I' => 4, 'J' => 4, 'K' => 4, 'L' => 4, 'M' => 4, 'N' => 4,
+        'O' => 5, 'P' => 5, 'Q' => 5, 'R' => 5, 'S' => 5, 'T' => 5,
+        'U' => 0, 'V' => 0, 'W' => 0, 'X' => 0, 'Y' => 0, 'Z' => 0,
+     );
+
+    protected $_columns = array(
+        '0' => 1, '1' => 2, '2' => 3, '3' => 4, '4' => 5, '5' => 0,
+        '6' => 1, '7' => 2, '8' => 3, '9' => 4, 'A' => 5, 'B' => 0,
+        'C' => 1, 'D' => 2, 'E' => 3, 'F' => 4, 'G' => 5, 'H' => 0,
+        'I' => 1, 'J' => 2, 'K' => 3, 'L' => 4, 'M' => 5, 'N' => 0,
+        'O' => 1, 'P' => 2, 'Q' => 3, 'R' => 4, 'S' => 5, 'T' => 0,
+        'U' => 1, 'V' => 2, 'W' => 3, 'X' => 4, 'Y' => 5, 'Z' => 0,
+    );
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_royalmail';
+
+    /**
+     * Validates the checksum ()
+     *
+     * @param  string $value The barcode to validate
+     * @return boolean
+     */
+    protected function _royalmail($value)
+    {
+        $checksum = substr($value, -1, 1);
+        $values   = str_split(substr($value, 0, -1));
+        $rowvalue = 0;
+        $colvalue = 0;
+        foreach($values as $row) {
+            $rowvalue += $this->_rows[$row];
+            $colvalue += $this->_columns[$row];
+        }
+
+        $rowvalue %= 6;
+        $colvalue %= 6;
+
+        $rowchkvalue = array_keys($this->_rows, $rowvalue);
+        $colchkvalue = array_keys($this->_columns, $colvalue);
+        $chkvalue    = current(array_intersect($rowchkvalue, $colchkvalue));
+        if ($chkvalue == $checksum) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Allows start and stop tag within checked chars
+     *
+     * @param  string $value The barcode to check for allowed characters
+     * @return boolean
+     */
+    public function checkChars($value)
+    {
+        if ($value[0] == '(') {
+            $value = substr($value, 1);
+
+            if ($value[strlen($value) - 1] == ')') {
+                $value = substr($value, 0, -1);
+            } else {
+                return false;
+            }
+        }
+
+        return parent::checkChars($value);
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Sscc.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Sscc.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Sscc.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Sscc.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Sscc extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 18;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+}
Index: trunk/library/Zend/Validate/Barcode/Ean8.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Ean8.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Ean8.php	(revision 5146)
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Ean8.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Ean8 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = array(7, 8);
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+
+    /**
+     * Overrides parent checkLength
+     *
+     * @param string $value Value
+     * @return boolean
+     */
+    public function checkLength($value)
+    {
+        if (strlen($value) == 7) {
+            $this->setCheck(false);
+        } else {
+            $this->setCheck(true);
+        }
+
+        return parent::checkLength($value);
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Upce.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Upce.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Upce.php	(revision 5146)
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Upce.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Upce extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = array(6, 7, 8);
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+
+    /**
+     * Overrides parent checkLength
+     *
+     * @param string $value Value
+     * @return boolean
+     */
+    public function checkLength($value)
+    {
+        if (strlen($value) != 8) {
+            $this->setCheck(false);
+        } else {
+            $this->setCheck(true);
+        }
+
+        return parent::checkLength($value);
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Postnet.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Postnet.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Postnet.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Postnet.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Postnet extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = array(6, 7, 10, 12);
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_postnet';
+}
Index: trunk/library/Zend/Validate/Barcode/Identcode.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Identcode.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Identcode.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Identcode.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Identcode extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 12;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_identcode';
+}
Index: trunk/library/Zend/Validate/Barcode/AdapterInterface.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/AdapterInterface.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/AdapterInterface.php	(revision 5146)
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: AdapterInterface.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Validate_Barcode_AdapterInterface
+{
+    /**
+     * Checks the length of a barcode
+     *
+     * @param  string $value  The barcode to check for proper length
+     * @return boolean
+     */
+    public function checkLength($value);
+
+    /**
+     * Checks for allowed characters within the barcode
+     *
+     * @param  string $value The barcode to check for allowed characters
+     * @return boolean
+     */
+    public function checkChars($value);
+
+    /**
+     * Validates the checksum
+     *
+     * @param string $value The barcode to check the checksum for
+     * @return boolean
+     */
+    public function checksum($value);
+
+    /**
+     * Returns if barcode uses a checksum
+     *
+     * @return boolean
+     */
+    public function getCheck();
+
+    /**
+     * Sets the checksum validation
+     *
+     * @param  boolean $check
+     * @return Zend_Validate_Barcode_Adapter Provides fluid interface
+     */
+    public function setCheck($check);
+}
Index: trunk/library/Zend/Validate/Barcode/AdapterAbstract.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/AdapterAbstract.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/AdapterAbstract.php	(revision 5146)
@@ -0,0 +1,315 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: AdapterAbstract.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterInterface
+ */
+require_once 'Zend/Validate/Barcode/AdapterInterface.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Validate_Barcode_AdapterAbstract
+    implements Zend_Validate_Barcode_AdapterInterface
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer|array|string
+     */
+    protected $_length;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters;
+
+    /**
+     * Callback to checksum function
+     * @var string|array
+     */
+    protected $_checksum;
+
+    /**
+     * Is a checksum value included?
+     * @var boolean
+     */
+    protected $_hasChecksum = true;
+
+    /**
+     * Checks the length of a barcode
+     *
+     * @param  string $value The barcode to check for proper length
+     * @return boolean
+     */
+    public function checkLength($value)
+    {
+        if (!is_string($value)) {
+            return false;
+        }
+
+        $fixum  = strlen($value);
+        $found  = false;
+        $length = $this->getLength();
+        if (is_array($length)) {
+            foreach ($length as $value) {
+                if ($fixum == $value) {
+                    $found = true;
+                }
+
+                if ($value == -1) {
+                    $found = true;
+                }
+            }
+        } elseif ($fixum == $length) {
+            $found = true;
+        } elseif ($length == -1) {
+            $found = true;
+        } elseif ($length == 'even') {
+            $count = $fixum % 2;
+            $found = ($count == 0) ? true : false;
+        } elseif ($length == 'odd') {
+            $count = $fixum % 2;
+            $found = ($count == 1) ? true : false;
+        }
+
+        return $found;
+    }
+
+    /**
+     * Checks for allowed characters within the barcode
+     *
+     * @param  string $value The barcode to check for allowed characters
+     * @return boolean
+     */
+    public function checkChars($value)
+    {
+        if (!is_string($value)) {
+            return false;
+        }
+
+        $characters = $this->getCharacters();
+        if ($characters == 128) {
+            for ($x = 0; $x < 128; ++$x) {
+                $value = str_replace(chr($x), '', $value);
+            }
+        } else {
+            $chars = str_split($characters);
+            foreach ($chars as $char) {
+                $value = str_replace($char, '', $value);
+            }
+        }
+
+        if (strlen($value) > 0) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Validates the checksum
+     *
+     * @param  string $value The barcode to check the checksum for
+     * @return boolean
+     */
+    public function checksum($value)
+    {
+        $checksum = $this->getChecksum();
+        if (!empty($checksum)) {
+            if (method_exists($this, $checksum)) {
+                return call_user_func(array($this, $checksum), $value);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the allowed barcode length
+     *
+     * @return string
+     */
+    public function getLength()
+    {
+        return $this->_length;
+    }
+
+    /**
+     * Returns the allowed characters
+     *
+     * @return integer|string
+     */
+    public function getCharacters()
+    {
+        return $this->_characters;
+    }
+
+    /**
+     * Returns the checksum function name
+     *
+     */
+    public function getChecksum()
+    {
+        return $this->_checksum;
+    }
+
+    /**
+     * Returns if barcode uses checksum
+     *
+     * @return boolean
+     */
+    public function getCheck()
+    {
+        return $this->_hasChecksum;
+    }
+
+    /**
+     * Sets the checksum validation
+     *
+     * @param  boolean $check
+     * @return Zend_Validate_Barcode_AdapterAbstract
+     */
+    public function setCheck($check)
+    {
+        $this->_hasChecksum = (boolean) $check;
+        return $this;
+    }
+
+    /**
+     * Validates the checksum (Modulo 10)
+     * GTIN implementation factor 3
+     *
+     * @param  string $value The barcode to validate
+     * @return boolean
+     */
+    protected function _gtin($value)
+    {
+        $barcode = substr($value, 0, -1);
+        $sum     = 0;
+        $length  = strlen($barcode) - 1;
+
+        for ($i = 0; $i <= $length; $i++) {
+            if (($i % 2) === 0) {
+                $sum += $barcode[$length - $i] * 3;
+            } else {
+                $sum += $barcode[$length - $i];
+            }
+        }
+
+        $calc     = $sum % 10;
+        $checksum = ($calc === 0) ? 0 : (10 - $calc);
+        if ($value[$length + 1] != $checksum) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Validates the checksum (Modulo 10)
+     * IDENTCODE implementation factors 9 and 4
+     *
+     * @param  string $value The barcode to validate
+     * @return boolean
+     */
+    protected function _identcode($value)
+    {
+        $barcode = substr($value, 0, -1);
+        $sum     = 0;
+        $length  = strlen($value) - 2;
+
+        for ($i = 0; $i <= $length; $i++) {
+            if (($i % 2) === 0) {
+                $sum += $barcode[$length - $i] * 4;
+            } else {
+                $sum += $barcode[$length - $i] * 9;
+            }
+        }
+
+        $calc     = $sum % 10;
+        $checksum = ($calc === 0) ? 0 : (10 - $calc);
+        if ($value[$length + 1] != $checksum) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Validates the checksum (Modulo 10)
+     * CODE25 implementation factor 3
+     *
+     * @param  string $value The barcode to validate
+     * @return boolean
+     */
+    protected function _code25($value)
+    {
+        $barcode = substr($value, 0, -1);
+        $sum     = 0;
+        $length  = strlen($barcode) - 1;
+
+        for ($i = 0; $i <= $length; $i++) {
+            if (($i % 2) === 0) {
+                $sum += $barcode[$i] * 3;
+            } else {
+                $sum += $barcode[$i];
+            }
+        }
+
+        $calc     = $sum % 10;
+        $checksum = ($calc === 0) ? 0 : (10 - $calc);
+        if ($value[$length + 1] != $checksum) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Validates the checksum ()
+     * POSTNET implementation
+     *
+     * @param  string $value The barcode to validate
+     * @return boolean
+     */
+    protected function _postnet($value)
+    {
+        $checksum = substr($value, -1, 1);
+        $values   = str_split(substr($value, 0, -1));
+
+        $check = 0;
+        foreach($values as $row) {
+            $check += $row;
+        }
+
+        $check %= 10;
+        $check = 10 - $check;
+        if ($check == $checksum) {
+            return true;
+        }
+
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Code25interleaved.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Code25interleaved.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Code25interleaved.php	(revision 5146)
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Code25interleaved.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Code25interleaved extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 'even';
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_code25';
+
+    /**
+     * Constructor
+     *
+     * Sets check flag to false.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->setCheck(false);
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Gtin12.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Gtin12.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Gtin12.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Gtin12.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Gtin12 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 12;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+}
Index: trunk/library/Zend/Validate/Barcode/Gtin13.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Gtin13.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Gtin13.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Gtin13.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Gtin13 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 13;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+}
Index: trunk/library/Zend/Validate/Barcode/Gtin14.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Gtin14.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Gtin14.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Gtin14.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Gtin14 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 14;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+}
Index: trunk/library/Zend/Validate/Barcode/Ean12.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Ean12.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Ean12.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Ean12.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Ean12 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 12;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+}
Index: trunk/library/Zend/Validate/Barcode/Code39ext.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Code39ext.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Code39ext.php	(revision 5146)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Code39ext.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Code39ext extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = -1;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = 128;
+
+    /**
+     * Constructor
+     *
+     * Sets check flag to false.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->setCheck(false);
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Code93ext.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Code93ext.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Code93ext.php	(revision 5146)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Code93ext.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Code93ext extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = -1;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = 128;
+
+    /**
+     * Constructor
+     *
+     * Sets check flag to false.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->setCheck(false);
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Ean13.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Ean13.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Ean13.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Ean13.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Ean13 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 13;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+}
Index: trunk/library/Zend/Validate/Barcode/Ean14.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Ean14.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Ean14.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Ean14.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Ean14 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 14;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+}
Index: trunk/library/Zend/Validate/Barcode/Ean18.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Ean18.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Ean18.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Ean18.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Ean18 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 18;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+}
Index: trunk/library/Zend/Validate/Barcode/Issn.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Issn.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Issn.php	(revision 5146)
@@ -0,0 +1,119 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Issn.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Issn extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = array(8, 13);
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789X';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+
+    /**
+     * Allows X on length of 8 chars
+     *
+     * @param  string $value The barcode to check for allowed characters
+     * @return boolean
+     */
+    public function checkChars($value)
+    {
+        if (strlen($value) != 8) {
+            if (strpos($value, 'X') !== false) {
+                return false;
+            }
+        }
+
+        return parent::checkChars($value);
+    }
+
+    /**
+     * Validates the checksum
+     *
+     * @param  string $value The barcode to check the checksum for
+     * @return boolean
+     */
+    public function checksum($value)
+    {
+        if (strlen($value) == 8) {
+            $this->_checksum = '_issn';
+        } else {
+            $this->_checksum = '_gtin';
+        }
+
+        return parent::checksum($value);
+    }
+
+    /**
+     * Validates the checksum ()
+     * ISSN implementation (reversed mod11)
+     *
+     * @param  string $value The barcode to validate
+     * @return boolean
+     */
+    protected function _issn($value)
+    {
+        $checksum = substr($value, -1, 1);
+        $values   = str_split(substr($value, 0, -1));
+        $check    = 0;
+        $multi    = 8;
+        foreach($values as $token) {
+            if ($token == 'X') {
+                $token = 10;
+            }
+
+            $check += ($token * $multi);
+            --$multi;
+        }
+
+        $check %= 11;
+        $check  = 11 - $check;
+        if ($check == $checksum) {
+            return true;
+        } else if (($check == 10) && ($checksum == 'X')) {
+            return true;
+        }
+
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Code25.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Code25.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Code25.php	(revision 5146)
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Code25.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Code25 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = -1;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_code25';
+
+    /**
+     * Constructor
+     *
+     * Sets check flag to false.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->setCheck(false);
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Intelligentmail.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Intelligentmail.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Intelligentmail.php	(revision 5146)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Intelligentmail.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_IntelligentMail extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = array(20, 25, 29, 31);
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Constructor
+     *
+     * Sets check flag to false.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->setCheck(false);
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Planet.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Planet.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Planet.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Planet.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Planet extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = array(12, 14);
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_postnet';
+}
Index: trunk/library/Zend/Validate/Barcode/Ean2.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Ean2.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Ean2.php	(revision 5146)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Ean2.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Ean2 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 2;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Constructor
+     *
+     * Sets check flag to false.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->setCheck(false);
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Code93.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Code93.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Code93.php	(revision 5146)
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Code93.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Code93 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = -1;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ -.$/+%';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_code93';
+
+    /**
+     * Note that the characters !"Â§& are only synonyms
+     * @var array
+     */
+    protected $_check = array(
+        '0' =>  0, '1' =>  1, '2' =>  2, '3' =>  3, '4' =>  4, '5' =>  5, '6' =>  6,
+        '7' =>  7, '8' =>  8, '9' =>  9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13,
+        'E' => 14, 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20,
+        'L' => 21, 'M' => 22, 'N' => 23, 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27,
+        'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, 'Y' => 34,
+        'Z' => 35, '-' => 36, '.' => 37, ' ' => 38, '$' => 39, '/' => 40, '+' => 41,
+        '%' => 42, '!' => 43, '"' => 44, 'Â§' => 45, '&' => 46,
+    );
+
+    /**
+     * Constructor
+     *
+     * Sets check flag to false.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->setCheck(false);
+    }
+
+    /**
+     * Validates the checksum (Modulo CK)
+     *
+     * @param  string $value The barcode to validate
+     * @return boolean
+     */
+    protected function _code93($value)
+    {
+        $checksum = substr($value, -2, 2);
+        $value    = str_split(substr($value, 0, -2));
+        $count    = 0;
+        $length   = count($value) % 20;
+        foreach($value as $char) {
+            if ($length == 0) {
+                $length = 20;
+            }
+
+            $count += $this->_check[$char] * $length;
+            --$length;
+        }
+
+        $check   = array_search(($count % 47), $this->_check);
+        $value[] = $check;
+        $count   = 0;
+        $length  = count($value) % 15;
+        foreach($value as $char) {
+            if ($length == 0) {
+                $length = 15;
+            }
+
+            $count += $this->_check[$char] * $length;
+            --$length;
+        }
+        $check .= array_search(($count % 47), $this->_check);
+
+        if ($check == $checksum) {
+            return true;
+        }
+
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Code39.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Code39.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Code39.php	(revision 5146)
@@ -0,0 +1,100 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Code39.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Code39 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = -1;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ -.$/+%';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_code39';
+
+    /**
+     * @var array
+     */
+    protected $_check = array(
+        '0' =>  0, '1' =>  1, '2' =>  2, '3' =>  3, '4' =>  4, '5' =>  5, '6' =>  6,
+        '7' =>  7, '8' =>  8, '9' =>  9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13,
+        'E' => 14, 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20,
+        'L' => 21, 'M' => 22, 'N' => 23, 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27,
+        'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, 'Y' => 34,
+        'Z' => 35, '-' => 36, '.' => 37, ' ' => 38, '$' => 39, '/' => 40, '+' => 41,
+        '%' => 42,
+    );
+
+    /**
+     * Constructor
+     *
+     * Sets check flag to false.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->setCheck(false);
+    }
+
+    /**
+     * Validates the checksum (Modulo 43)
+     *
+     * @param  string $value The barcode to validate
+     * @return boolean
+     */
+    protected function _code39($value)
+    {
+        $checksum = substr($value, -1, 1);
+        $value    = str_split(substr($value, 0, -1));
+        $count    = 0;
+        foreach($value as $char) {
+            $count += $this->_check[$char];
+        }
+
+        $mod = $count % 43;
+        if ($mod == $this->_check[$checksum]) {
+            return true;
+        }
+
+        return false;
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Itf14.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Itf14.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Itf14.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Itf14.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Itf14 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 14;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+}
Index: trunk/library/Zend/Validate/Barcode/Ean5.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Ean5.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Ean5.php	(revision 5146)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Ean5.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Ean5 extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 5;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Constructor
+     *
+     * Sets check flag to false.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->setCheck(false);
+    }
+}
Index: trunk/library/Zend/Validate/Barcode/Leitcode.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Leitcode.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Leitcode.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Leitcode.php 20785 2010-01-31 09:43:03Z mikaelkael $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Leitcode extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 14;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_identcode';
+}
Index: trunk/library/Zend/Validate/Barcode/Upca.php
===================================================================
--- trunk/library/Zend/Validate/Barcode/Upca.php	(revision 5146)
+++ trunk/library/Zend/Validate/Barcode/Upca.php	(revision 5146)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Upca.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Validate_Barcode_AdapterAbstract
+ */
+require_once 'Zend/Validate/Barcode/AdapterAbstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Barcode_Upca extends Zend_Validate_Barcode_AdapterAbstract
+{
+    /**
+     * Allowed barcode lengths
+     * @var integer
+     */
+    protected $_length = 12;
+
+    /**
+     * Allowed barcode characters
+     * @var string
+     */
+    protected $_characters = '0123456789';
+
+    /**
+     * Checksum function
+     * @var string
+     */
+    protected $_checksum = '_gtin';
+}
Index: trunk/library/Zend/Validate/Iban.php
===================================================================
--- trunk/library/Zend/Validate/Iban.php	(revision 5146)
+++ trunk/library/Zend/Validate/Iban.php	(revision 5146)
@@ -0,0 +1,223 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Iban.php 22400 2010-06-09 19:25:02Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validates IBAN Numbers (International Bank Account Numbers)
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Iban extends Zend_Validate_Abstract
+{
+    const NOTSUPPORTED = 'ibanNotSupported';
+    const FALSEFORMAT  = 'ibanFalseFormat';
+    const CHECKFAILED  = 'ibanCheckFailed';
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOTSUPPORTED => "Unknown country within the IBAN '%value%'",
+        self::FALSEFORMAT  => "'%value%' has a false IBAN format",
+        self::CHECKFAILED  => "'%value%' has failed the IBAN check",
+    );
+
+    /**
+     * Optional locale
+     *
+     * @var string|Zend_Locale|null
+     */
+    protected $_locale;
+
+    /**
+     * IBAN regexes by region
+     *
+     * @var array
+     */
+    protected $_ibanregex = array(
+        'AD' => '/^AD[0-9]{2}[0-9]{8}[A-Z0-9]{12}$/',
+        'AT' => '/^AT[0-9]{2}[0-9]{5}[0-9]{11}$/',
+        'BA' => '/^BA[0-9]{2}[0-9]{6}[0-9]{10}$/',
+        'BE' => '/^BE[0-9]{2}[0-9]{3}[0-9]{9}$/',
+        'BG' => '/^BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Z0-9]{8}$/',
+        'CH' => '/^CH[0-9]{2}[0-9]{5}[A-Z0-9]{12}$/',
+        'CS' => '/^CS[0-9]{2}[0-9]{3}[0-9]{15}$/',
+        'CY' => '/^CY[0-9]{2}[0-9]{8}[A-Z0-9]{16}$/',
+        'CZ' => '/^CZ[0-9]{2}[0-9]{4}[0-9]{16}$/',
+        'DE' => '/^DE[0-9]{2}[0-9]{8}[0-9]{10}$/',
+        'DK' => '/^DK[0-9]{2}[0-9]{4}[0-9]{10}$/',
+        'EE' => '/^EE[0-9]{2}[0-9]{4}[0-9]{12}$/',
+        'ES' => '/^ES[0-9]{2}[0-9]{8}[0-9]{12}$/',
+        'FR' => '/^FR[0-9]{2}[0-9]{10}[A-Z0-9]{13}$/',
+        'FI' => '/^FI[0-9]{2}[0-9]{6}[0-9]{8}$/',
+        'GB' => '/^GB[0-9]{2}[A-Z]{4}[0-9]{14}$/',
+        'GI' => '/^GI[0-9]{2}[A-Z]{4}[A-Z0-9]{15}$/',
+        'GR' => '/^GR[0-9]{2}[0-9]{7}[A-Z0-9]{16}$/',
+        'HR' => '/^HR[0-9]{2}[0-9]{7}[0-9]{10}$/',
+        'HU' => '/^HU[0-9]{2}[0-9]{7}[0-9]{1}[0-9]{15}[0-9]{1}$/',
+        'IE' => '/^IE[0-9]{2}[A-Z0-9]{4}[0-9]{6}[0-9]{8}$/',
+        'IS' => '/^IS[0-9]{2}[0-9]{4}[0-9]{18}$/',
+        'IT' => '/^IT[0-9]{2}[A-Z]{1}[0-9]{10}[A-Z0-9]{12}$/',
+        'LI' => '/^LI[0-9]{2}[0-9]{5}[A-Z0-9]{12}$/',
+        'LU' => '/^LU[0-9]{2}[0-9]{3}[A-Z0-9]{13}$/',
+        'LT' => '/^LT[0-9]{2}[0-9]{5}[0-9]{11}$/',
+        'LV' => '/^LV[0-9]{2}[A-Z]{4}[A-Z0-9]{13}$/',
+        'MK' => '/^MK[0-9]{2}[A-Z]{3}[A-Z0-9]{10}[0-9]{2}$/',
+        'MT' => '/^MT[0-9]{2}[A-Z]{4}[0-9]{5}[A-Z0-9]{18}$/',
+        'NL' => '/^NL[0-9]{2}[A-Z]{4}[0-9]{10}$/',
+        'NO' => '/^NO[0-9]{2}[0-9]{4}[0-9]{7}$/',
+        'PL' => '/^PL[0-9]{2}[0-9]{8}[0-9]{16}$/',
+        'PT' => '/^PT[0-9]{2}[0-9]{8}[0-9]{13}$/',
+        'RO' => '/^RO[0-9]{2}[A-Z]{4}[A-Z0-9]{16}$/',
+        'SE' => '/^SE[0-9]{2}[0-9]{3}[0-9]{17}$/',
+        'SI' => '/^SI[0-9]{2}[0-9]{5}[0-9]{8}[0-9]{2}$/',
+        'SK' => '/^SK[0-9]{2}[0-9]{4}[0-9]{16}$/',
+        'TN' => '/^TN[0-9]{2}[0-9]{5}[0-9]{15}$/',
+        'TR' => '/^TR[0-9]{2}[0-9]{5}[A-Z0-9]{17}$/'
+    );
+
+    /**
+     * Sets validator options
+     *
+     * @param  string|Zend_Config|Zend_Locale $locale OPTIONAL
+     * @return void
+     */
+    public function __construct($locale = null)
+    {
+        if ($locale instanceof Zend_Config) {
+            $locale = $locale->toArray();
+        }
+
+        if (is_array($locale)) {
+            if (array_key_exists('locale', $locale)) {
+                $locale = $locale['locale'];
+            } else {
+                $locale = null;
+            }
+        }
+
+        if (empty($locale)) {
+            require_once 'Zend/Registry.php';
+            if (Zend_Registry::isRegistered('Zend_Locale')) {
+                $locale = Zend_Registry::get('Zend_Locale');
+            }
+        }
+
+        if ($locale !== null) {
+            $this->setLocale($locale);
+        }
+    }
+
+    /**
+     * Returns the locale option
+     *
+     * @return string|Zend_Locale|null
+     */
+    public function getLocale()
+    {
+        return $this->_locale;
+    }
+
+    /**
+     * Sets the locale option
+     *
+     * @param  string|Zend_Locale $locale
+     * @return Zend_Validate_Date provides a fluent interface
+     */
+    public function setLocale($locale = null)
+    {
+        if ($locale !== false) {
+            require_once 'Zend/Locale.php';
+            $locale = Zend_Locale::findLocale($locale);
+            if (strlen($locale) < 4) {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception('Region must be given for IBAN validation');
+            }
+        }
+
+        $this->_locale = $locale;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if $value is a valid IBAN
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        $value = strtoupper($value);
+        $this->_setValue($value);
+
+        if (empty($this->_locale)) {
+            $region = substr($value, 0, 2);
+        } else {
+            $region = new Zend_Locale($this->_locale);
+            $region = $region->getRegion();
+        }
+
+        if (!array_key_exists($region, $this->_ibanregex)) {
+            $this->_setValue($region);
+            $this->_error(self::NOTSUPPORTED);
+            return false;
+        }
+
+        if (!preg_match($this->_ibanregex[$region], $value)) {
+            $this->_error(self::FALSEFORMAT);
+            return false;
+        }
+
+        $format = substr($value, 4) . substr($value, 0, 4);
+        $format = str_replace(
+            array('A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',
+                  'N',  'O',  'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',  'Y',  'Z'),
+            array('10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22',
+                  '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35'),
+            $format);
+
+        $temp = intval(substr($format, 0, 1));
+        $len  = strlen($format);
+        for ($x = 1; $x < $len; ++$x) {
+            $temp *= 10;
+            $temp += intval(substr($format, $x, 1));
+            $temp %= 97;
+        }
+
+        if ($temp != 1) {
+            $this->_error(self::CHECKFAILED);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/Sitemap/Loc.php
===================================================================
--- trunk/library/Zend/Validate/Sitemap/Loc.php	(revision 5146)
+++ trunk/library/Zend/Validate/Sitemap/Loc.php	(revision 5146)
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @subpackage Sitemap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Loc.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @see Zend_Uri
+ */
+require_once 'Zend/Uri.php';
+
+/**
+ * Validates whether a given value is valid as a sitemap <loc> value
+ *
+ * @link       http://www.sitemaps.org/protocol.php Sitemaps XML format
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @subpackage Sitemap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Sitemap_Loc extends Zend_Validate_Abstract
+{
+    /**
+     * Validation key for not valid
+     *
+     */
+    const NOT_VALID = 'sitemapLocNotValid';
+    const INVALID   = 'sitemapLocInvalid';
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOT_VALID => "'%value%' is no valid sitemap location",
+        self::INVALID   => "Invalid type given. String expected",
+    );
+
+    /**
+     * Validates if a string is valid as a sitemap location
+     *
+     * @link http://www.sitemaps.org/protocol.php#locdef <loc>
+     *
+     * @param  string  $value  value to validate
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+        $result = Zend_Uri::check($value);
+        if ($result !== true) {
+            $this->_error(self::NOT_VALID);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/Sitemap/Priority.php
===================================================================
--- trunk/library/Zend/Validate/Sitemap/Priority.php	(revision 5146)
+++ trunk/library/Zend/Validate/Sitemap/Priority.php	(revision 5146)
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @subpackage Sitemap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Priority.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validates whether a given value is valid as a sitemap <priority> value
+ *
+ * @link       http://www.sitemaps.org/protocol.php Sitemaps XML format
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @subpackage Sitemap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Sitemap_Priority extends Zend_Validate_Abstract
+{
+    /**
+     * Validation key for not valid
+     *
+     */
+    const NOT_VALID = 'sitemapPriorityNotValid';
+    const INVALID   = 'sitemapPriorityInvalid';
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOT_VALID => "'%value%' is no valid sitemap priority",
+        self::INVALID   => "Invalid type given. Numeric string, integer or float expected",
+    );
+
+    /**
+     * Validates if a string is valid as a sitemap priority
+     *
+     * @link http://www.sitemaps.org/protocol.php#prioritydef <priority>
+     *
+     * @param  string  $value  value to validate
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_numeric($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+        $value = (float) $value;
+        if ($value < 0 || $value > 1) {
+            $this->_error(self::NOT_VALID);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/Sitemap/Changefreq.php
===================================================================
--- trunk/library/Zend/Validate/Sitemap/Changefreq.php	(revision 5146)
+++ trunk/library/Zend/Validate/Sitemap/Changefreq.php	(revision 5146)
@@ -0,0 +1,95 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @subpackage Sitemap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Changefreq.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validates whether a given value is valid as a sitemap <changefreq> value
+ *
+ * @link       http://www.sitemaps.org/protocol.php Sitemaps XML format
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @subpackage Sitemap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Sitemap_Changefreq extends Zend_Validate_Abstract
+{
+    /**
+     * Validation key for not valid
+     *
+     */
+    const NOT_VALID = 'sitemapChangefreqNotValid';
+    const INVALID   = 'sitemapChangefreqInvalid';
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOT_VALID => "'%value%' is no valid sitemap changefreq",
+        self::INVALID   => "Invalid type given. String expected",
+    );
+
+    /**
+     * Valid change frequencies
+     *
+     * @var array
+     */
+    protected $_changeFreqs = array(
+        'always',  'hourly', 'daily', 'weekly',
+        'monthly', 'yearly', 'never'
+    );
+
+    /**
+     * Validates if a string is valid as a sitemap changefreq
+     *
+     * @link http://www.sitemaps.org/protocol.php#changefreqdef <changefreq>
+     *
+     * @param  string  $value  value to validate
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+        if (!is_string($value)) {
+            return false;
+        }
+
+        if (!in_array($value, $this->_changeFreqs, true)) {
+            $this->_error(self::NOT_VALID);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/Sitemap/Lastmod.php
===================================================================
--- trunk/library/Zend/Validate/Sitemap/Lastmod.php	(revision 5146)
+++ trunk/library/Zend/Validate/Sitemap/Lastmod.php	(revision 5146)
@@ -0,0 +1,88 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @subpackage Sitemap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Lastmod.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * Validates whether a given value is valid as a sitemap <lastmod> value
+ *
+ * @link       http://www.sitemaps.org/protocol.php Sitemaps XML format
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @subpackage Sitemap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Sitemap_Lastmod extends Zend_Validate_Abstract
+{
+    /**
+     * Regular expression to use when validating
+     *
+     */
+    const LASTMOD_REGEX = '/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])(T([0-1][0-9]|2[0-3])(:[0-5][0-9])(:[0-5][0-9])?(\\+|-)([0-1][0-9]|2[0-3]):[0-5][0-9])?$/';
+
+    /**
+     * Validation key for not valid
+     *
+     */
+    const NOT_VALID = 'sitemapLastmodNotValid';
+    const INVALID   = 'sitemapLastmodInvalid';
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOT_VALID => "'%value%' is no valid sitemap lastmod",
+        self::INVALID   => "Invalid type given. String expected",
+    );
+
+    /**
+     * Validates if a string is valid as a sitemap lastmod
+     *
+     * @link http://www.sitemaps.org/protocol.php#lastmoddef <lastmod>
+     *
+     * @param  string  $value  value to validate
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+        $result = @preg_match(self::LASTMOD_REGEX, $value);
+        if ($result != 1) {
+            $this->_error(self::NOT_VALID);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/Hostname.php
===================================================================
--- trunk/library/Zend/Validate/Hostname.php	(revision 5146)
+++ trunk/library/Zend/Validate/Hostname.php	(revision 5146)
@@ -0,0 +1,794 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Hostname.php 23488 2010-12-10 07:22:55Z mjh_ca $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @see Zend_Validate_Ip
+ */
+require_once 'Zend/Validate/Ip.php';
+
+/**
+ * Please note there are two standalone test scripts for testing IDN characters due to problems
+ * with file encoding.
+ *
+ * The first is tests/Zend/Validate/HostnameTestStandalone.php which is designed to be run on
+ * the command line.
+ *
+ * The second is tests/Zend/Validate/HostnameTestForm.php which is designed to be run via HTML
+ * to allow users to test entering UTF-8 characters in a form.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Hostname extends Zend_Validate_Abstract
+{
+    const CANNOT_DECODE_PUNYCODE  = 'hostnameCannotDecodePunycode';
+    const INVALID                 = 'hostnameInvalid';
+    const INVALID_DASH            = 'hostnameDashCharacter';
+    const INVALID_HOSTNAME        = 'hostnameInvalidHostname';
+    const INVALID_HOSTNAME_SCHEMA = 'hostnameInvalidHostnameSchema';
+    const INVALID_LOCAL_NAME      = 'hostnameInvalidLocalName';
+    const INVALID_URI             = 'hostnameInvalidUri';
+    const IP_ADDRESS_NOT_ALLOWED  = 'hostnameIpAddressNotAllowed';
+    const LOCAL_NAME_NOT_ALLOWED  = 'hostnameLocalNameNotAllowed';
+    const UNDECIPHERABLE_TLD      = 'hostnameUndecipherableTld';
+    const UNKNOWN_TLD             = 'hostnameUnknownTld';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::CANNOT_DECODE_PUNYCODE  => "'%value%' appears to be a DNS hostname but the given punycode notation cannot be decoded",
+        self::INVALID                 => "Invalid type given. String expected",
+        self::INVALID_DASH            => "'%value%' appears to be a DNS hostname but contains a dash in an invalid position",
+        self::INVALID_HOSTNAME        => "'%value%' does not match the expected structure for a DNS hostname",
+        self::INVALID_HOSTNAME_SCHEMA => "'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'",
+        self::INVALID_LOCAL_NAME      => "'%value%' does not appear to be a valid local network name",
+        self::INVALID_URI             => "'%value%' does not appear to be a valid URI hostname",
+        self::IP_ADDRESS_NOT_ALLOWED  => "'%value%' appears to be an IP address, but IP addresses are not allowed",
+        self::LOCAL_NAME_NOT_ALLOWED  => "'%value%' appears to be a local network name but local network names are not allowed",
+        self::UNDECIPHERABLE_TLD      => "'%value%' appears to be a DNS hostname but cannot extract TLD part",
+        self::UNKNOWN_TLD             => "'%value%' appears to be a DNS hostname but cannot match TLD against known list",
+    );
+
+    /**
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'tld' => '_tld'
+    );
+
+    /**
+     * Allows Internet domain names (e.g., example.com)
+     */
+    const ALLOW_DNS   = 1;
+
+    /**
+     * Allows IP addresses
+     */
+    const ALLOW_IP    = 2;
+
+    /**
+     * Allows local network names (e.g., localhost, www.localdomain)
+     */
+    const ALLOW_LOCAL = 4;
+
+    /**
+     * Allows all types of hostnames
+     */
+    const ALLOW_ALL   = 7;
+
+    /**
+     * Allows all types of hostnames
+     */
+    const ALLOW_URI = 8;
+
+    /**
+     * Array of valid top-level-domains
+     *
+     * @see ftp://data.iana.org/TLD/tlds-alpha-by-domain.txt  List of all TLDs by domain
+     * @see http://www.iana.org/domains/root/db/ Official list of supported TLDs
+     * @var array
+     */
+    protected $_validTlds = array(
+        'ac', 'ad', 'ae', 'aero', 'af', 'ag', 'ai', 'al', 'am', 'an', 'ao', 'aq', 'ar', 'arpa',
+        'as', 'asia', 'at', 'au', 'aw', 'ax', 'az', 'ba', 'bb', 'bd', 'be', 'bf', 'bg', 'bh', 'bi',
+        'biz', 'bj', 'bm', 'bn', 'bo', 'br', 'bs', 'bt', 'bv', 'bw', 'by', 'bz', 'ca', 'cat', 'cc',
+        'cd', 'cf', 'cg', 'ch', 'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'com', 'coop', 'cr', 'cu',
+        'cv', 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm', 'do', 'dz', 'ec', 'edu', 'ee', 'eg', 'er',
+        'es', 'et', 'eu', 'fi', 'fj', 'fk', 'fm', 'fo', 'fr', 'ga', 'gb', 'gd', 'ge', 'gf', 'gg',
+        'gh', 'gi', 'gl', 'gm', 'gn', 'gov', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk',
+        'hm', 'hn', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'im', 'in', 'info', 'int', 'io', 'iq',
+        'ir', 'is', 'it', 'je', 'jm', 'jo', 'jobs', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp',
+        'kr', 'kw', 'ky', 'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu', 'lv', 'ly',
+        'ma', 'mc', 'md', 'me', 'mg', 'mh', 'mil', 'mk', 'ml', 'mm', 'mn', 'mo', 'mobi', 'mp',
+        'mq', 'mr', 'ms', 'mt', 'mu', 'museum', 'mv', 'mw', 'mx', 'my', 'mz', 'na', 'name', 'nc',
+        'ne', 'net', 'nf', 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om', 'org', 'pa', 'pe',
+        'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'pro', 'ps', 'pt', 'pw', 'py', 'qa', 're',
+        'ro', 'rs', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd', 'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl',
+        'sm', 'sn', 'so', 'sr', 'st', 'su', 'sv', 'sy', 'sz', 'tc', 'td', 'tel', 'tf', 'tg', 'th',
+        'tj', 'tk', 'tl', 'tm', 'tn', 'to', 'tp', 'tr', 'travel', 'tt', 'tv', 'tw', 'tz', 'ua',
+        'ug', 'uk', 'um', 'us', 'uy', 'uz', 'va', 'vc', 've', 'vg', 'vi', 'vn', 'vu', 'wf', 'ws',
+        'ye', 'yt', 'yu', 'za', 'zm', 'zw'
+    );
+
+    /**
+     * @var string
+     */
+    protected $_tld;
+
+    /**
+     * Array for valid Idns
+     * @see http://www.iana.org/domains/idn-tables/ Official list of supported IDN Chars
+     * (.AC) Ascension Island http://www.nic.ac/pdf/AC-IDN-Policy.pdf
+     * (.AR) Argentinia http://www.nic.ar/faqidn.html
+     * (.AS) American Samoa http://www.nic.as/idn/chars.cfm
+     * (.AT) Austria http://www.nic.at/en/service/technical_information/idn/charset_converter/
+     * (.BIZ) International http://www.iana.org/domains/idn-tables/
+     * (.BR) Brazil http://registro.br/faq/faq6.html
+     * (.BV) Bouvett Island http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
+     * (.CAT) Catalan http://www.iana.org/domains/idn-tables/tables/cat_ca_1.0.html
+     * (.CH) Switzerland https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1
+     * (.CL) Chile http://www.iana.org/domains/idn-tables/tables/cl_latn_1.0.html
+     * (.COM) International http://www.verisign.com/information-services/naming-services/internationalized-domain-names/index.html
+     * (.DE) Germany http://www.denic.de/en/domains/idns/liste.html
+     * (.DK) Danmark http://www.dk-hostmaster.dk/index.php?id=151
+     * (.ES) Spain https://www.nic.es/media/2008-05/1210147705287.pdf
+     * (.FI) Finland http://www.ficora.fi/en/index/palvelut/fiverkkotunnukset/aakkostenkaytto.html
+     * (.GR) Greece https://grweb.ics.forth.gr/CharacterTable1_en.jsp
+     * (.HU) Hungary http://www.domain.hu/domain/English/szabalyzat/szabalyzat.html
+     * (.INFO) International http://www.nic.info/info/idn
+     * (.IO) British Indian Ocean Territory http://www.nic.io/IO-IDN-Policy.pdf
+     * (.IR) Iran http://www.nic.ir/Allowable_Characters_dot-iran
+     * (.IS) Iceland http://www.isnic.is/domain/rules.php
+     * (.KR) Korea http://www.iana.org/domains/idn-tables/tables/kr_ko-kr_1.0.html
+     * (.LI) Liechtenstein https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1
+     * (.LT) Lithuania http://www.domreg.lt/static/doc/public/idn_symbols-en.pdf
+     * (.MD) Moldova http://www.register.md/
+     * (.MUSEUM) International http://www.iana.org/domains/idn-tables/tables/museum_latn_1.0.html
+     * (.NET) International http://www.verisign.com/information-services/naming-services/internationalized-domain-names/index.html
+     * (.NO) Norway http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
+     * (.NU) Niue http://www.worldnames.net/
+     * (.ORG) International http://www.pir.org/index.php?db=content/FAQs&tbl=FAQs_Registrant&id=2
+     * (.PE) Peru https://www.nic.pe/nuevas_politicas_faq_2.php
+     * (.PL) Poland http://www.dns.pl/IDN/allowed_character_sets.pdf
+     * (.PR) Puerto Rico http://www.nic.pr/idn_rules.asp
+     * (.PT) Portugal https://online.dns.pt/dns_2008/do?com=DS;8216320233;111;+PAGE(4000058)+K-CAT-CODIGO(C.125)+RCNT(100);
+     * (.RU) Russia http://www.iana.org/domains/idn-tables/tables/ru_ru-ru_1.0.html
+     * (.SA) Saudi Arabia http://www.iana.org/domains/idn-tables/tables/sa_ar_1.0.html
+     * (.SE) Sweden http://www.iis.se/english/IDN_campaignsite.shtml?lang=en
+     * (.SH) Saint Helena http://www.nic.sh/SH-IDN-Policy.pdf
+     * (.SJ) Svalbard and Jan Mayen http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
+     * (.TH) Thailand http://www.iana.org/domains/idn-tables/tables/th_th-th_1.0.html
+     * (.TM) Turkmenistan http://www.nic.tm/TM-IDN-Policy.pdf
+     * (.TR) Turkey https://www.nic.tr/index.php
+     * (.VE) Venice http://www.iana.org/domains/idn-tables/tables/ve_es_1.0.html
+     * (.VN) Vietnam http://www.vnnic.vn/english/5-6-300-2-2-04-20071115.htm#1.%20Introduction
+     *
+     * @var array
+     */
+    protected $_validIdns = array(
+        'AC'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿ÄÄÄ
+ÄÄÄÄÄÄÄÄÄÄÄÄ¡Ä£Ä¥Ä§Ä«Ä¯ÄµÄ·ÄºÄŒÄŸÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ¡Å£Å¥Å§Å«Å­Å¯Å±Å³ÅµÅ·ÅºÅŒÅŸ]{1,63}$/iu'),
+        'AR'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã£Ã§-ÃªÃ¬Ã­Ã±-ÃµÃŒ]{1,63}$/iu'),
+        'AS'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿ÄÄÄ
+ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¡Ä£Ä¥Ä§Ä©Ä«Ä­Ä¯Ä±ÄµÄ·ÄžÄºÄŒÄŸÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ¡Å£Å¥Å§Å©Å«Å­Å¯Å±Å³ÅµÅ·ÅºÅŒ]{1,63}$/iu'),
+        'AT'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿ÅÅ¡ÅŸ]{1,63}$/iu'),
+        'BIZ' => 'Hostname/Biz.php',
+        'BR'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã£Ã§Ã©Ã­Ã³-ÃµÃºÃŒ]{1,63}$/iu'),
+        'BV'  => array(1 => '/^[\x{002d}0-9a-zÃ Ã¡Ã€-Ã©ÃªÃ±-ÃŽÃ¶ÃžÃŒÄÄÅÅÅ¡Å§ÅŸ]{1,63}$/iu'),
+        'CAT' => array(1 => '/^[\x{002d}0-9a-zÂ·Ã Ã§-Ã©Ã­Ã¯Ã²Ã³ÃºÃŒ]{1,63}$/iu'),
+        'CH'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿Å]{1,63}$/iu'),
+        'CL'  => array(1 => '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã±Ã³ÃºÃŒ]{1,63}$/iu'),
+        'CN'  => 'Hostname/Cn.php',
+        'COM' => 'Zend/Validate/Hostname/Com.php',
+        'DE'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿ÄÄ
+ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¡Ä£Ä¥Ä§Ä­Ä©Ä¯Ä«Ä±ÄµÄ·ÄºÄŸÄŒÅÅÅÅÅÅÅÅÅÄžÅÅÅÅÅÅ¡ÅÅ¥Å£Å§Å­Å¯Å±Å©Å³Å«ÅµÅ·ÅºÅŸÅŒ]{1,63}$/iu'),
+        'DK'  => array(1 => '/^[\x{002d}0-9a-zÃ€Ã©Ã¶ÃŒ]{1,63}$/iu'),
+        'ES'  => array(1 => '/^[\x{002d}0-9a-zÃ Ã¡Ã§ÃšÃ©Ã­Ã¯Ã±Ã²Ã³ÃºÃŒÂ·]{1,63}$/iu'),
+        'EU'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿]{1,63}$/iu',
+            2 => '/^[\x{002d}0-9a-zÄÄÄ
+ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¡Ä£Ä¥Ä§Ä©Ä«Ä­Ä¯Ä±ÄµÄ·ÄºÄŒÄŸÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ¡Å¥Å§Å©Å«Å­Å¯Å±Å³ÅµÅ·ÅºÅŒÅŸ]{1,63}$/iu',
+            3 => '/^[\x{002d}0-9a-zÈÈ]{1,63}$/iu',
+            4 => '/^[\x{002d}0-9a-zÎÎ¬Î­Î®Î¯Î°Î±Î²Î³ÎŽÎµÎ¶Î·ÎžÎ¹ÎºÎ»ÎŒÎœÎŸÎ¿ÏÏÏÏÏÏ
+ÏÏÏÏÏÏÏÏÏ]{1,63}$/iu',
+            5 => '/^[\x{002d}0-9a-zÐ°Ð±Ð²Ð³ÐŽÐµÐ¶Ð·ÐžÐ¹ÐºÐ»ÐŒÐœÐŸÐ¿ÑÑÑÑÑÑ
+ÑÑÑÑÑÑÑÑÑÑ]{1,63}$/iu',
+            6 => '/^[\x{002d}0-9a-záŒ-áŒáŒ-áŒáŒ -áŒ§áŒ°-áŒ·áœ-áœ
+áœ-áœáœ -áœ§áœ°-ÏáŸ-áŸáŸ-áŸáŸ -áŸ§áŸ°-áŸŽáŸ¶áŸ·á¿á¿á¿á¿á¿á¿-Îá¿á¿á¿ -á¿§á¿²á¿³á¿Žá¿¶á¿·]{1,63}$/iu'),
+        'FI'  => array(1 => '/^[\x{002d}0-9a-zÃ€Ã¥Ã¶]{1,63}$/iu'),
+        'GR'  => array(1 => '/^[\x{002d}0-9a-zÎÎÎÎÎÎ-Î¡Î£-ÏáŒ-áŒáŒ-áŒáŒ -áœ
+áœ-áœáœ-áœáœáœáœáœ-áœœáŸ-áŸŽáŸ¶-áŸŒá¿á¿á¿á¿-á¿á¿-á¿á¿-á¿á¿ -á¿¬á¿²á¿³á¿Žá¿¶-á¿Œ]{1,63}$/iu'),
+        'HK'  => 'Zend/Validate/Hostname/Cn.php',
+        'HU'  => array(1 => '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã³Ã¶ÃºÃŒÅÅ±]{1,63}$/iu'),
+        'INFO'=> array(1 => '/^[\x{002d}0-9a-zÃ€Ã¥ÃŠÃ©Ã¶ÃžÃŒ]{1,63}$/iu',
+            2 => '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã³Ã¶ÃºÃŒÅÅ±]{1,63}$/iu',
+            3 => '/^[\x{002d}0-9a-zÃ¡ÃŠÃ©Ã­Ã°Ã³Ã¶ÃºÃœÃŸ]{1,63}$/iu',
+            4 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
+            5 => '/^[\x{002d}0-9a-zÄÄÄÄ£Ä«Ä·ÄŒÅÅÅÅ¡Å«ÅŸ]{1,63}$/iu',
+            6 => '/^[\x{002d}0-9a-zÄ
+ÄÄÄÄ¯Å¡Å«Å³ÅŸ]{1,63}$/iu',
+            7 => '/^[\x{002d}0-9a-zÃ³Ä
+ÄÄÅÅÅÅºÅŒ]{1,63}$/iu',
+            8 => '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã±Ã³ÃºÃŒ]{1,63}$/iu'),
+        'IO'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿ÄÄ
+ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¡Ä£Ä¥Ä§Ä­Ä©Ä¯Ä«Ä±ÄµÄ·ÄºÄŸÄŒÅÅÅÅÅÅÅÅÅÄžÅÅÅÅÅÅ¡ÅÅ¥Å£Å§Å­Å¯Å±Å©Å³Å«ÅµÅ·ÅºÅŸÅŒ]{1,63}$/iu'),
+        'IS'  => array(1 => '/^[\x{002d}0-9a-zÃ¡Ã©ÃœÃºÃ­Ã³ÃŸÃŠÃ¶Ã°]{1,63}$/iu'),
+        'JP'  => 'Zend/Validate/Hostname/Jp.php',
+        'KR'  => array(1 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu'),
+        'LI'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿Å]{1,63}$/iu'),
+        'LT'  => array(1 => '/^[\x{002d}0-9Ä
+ÄÄÄÄ¯Å¡Å³Å«ÅŸ]{1,63}$/iu'),
+        'MD'  => array(1 => '/^[\x{002d}0-9ÄÃ¢Ã®ÅÅ£]{1,63}$/iu'),
+        'MUSEUM' => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿ÄÄÄ
+ÄÄÄÄÄÄÄÄÄÄÄ¡Ä£Ä§Ä«Ä¯Ä±Ä·ÄºÄŒÄŸÅÅÅÅÅÅÅÅÅÅÅÅÅÅ¡Å£Å¥Å§Å«Å¯Å±Å³ÅµÅ·ÅºÅŒÅŸÇÇÇÇ\x{01E5}\x{01E7}\x{01E9}\x{01EF}É\x{0292}áºáºáº
+á»³]{1,63}$/iu'),
+        'NET' => 'Zend/Validate/Hostname/Com.php',
+        'NO'  => array(1 => '/^[\x{002d}0-9a-zÃ Ã¡Ã€-Ã©ÃªÃ±-ÃŽÃ¶ÃžÃŒÄÄÅÅÅ¡Å§ÅŸ]{1,63}$/iu'),
+        'NU'  => 'Zend/Validate/Hostname/Com.php',
+        'ORG' => array(1 => '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã±Ã³ÃºÃŒ]{1,63}$/iu',
+            2 => '/^[\x{002d}0-9a-zÃ³Ä
+ÄÄÅÅÅÅºÅŒ]{1,63}$/iu',
+            3 => '/^[\x{002d}0-9a-zÃ¡Ã€Ã¥ÃŠÃ©Ã«Ã­Ã°Ã³Ã¶ÃžÃºÃŒÃœÃŸ]{1,63}$/iu',
+            4 => '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã³Ã¶ÃºÃŒÅÅ±]{1,63}$/iu',
+            5 => '/^[\x{002d}0-9a-zÄ
+ÄÄÄÄ¯Å¡Å«Å³ÅŸ]{1,63}$/iu',
+            6 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
+            7 => '/^[\x{002d}0-9a-zÄÄÄÄ£Ä«Ä·ÄŒÅÅÅÅ¡Å«ÅŸ]{1,63}$/iu'),
+        'PE'  => array(1 => '/^[\x{002d}0-9a-zÃ±Ã¡Ã©Ã­Ã³ÃºÃŒ]{1,63}$/iu'),
+        'PL'  => array(1 => '/^[\x{002d}0-9a-zÄÄÄÄ£Ä«Ä·ÄŒÅÅÅÅ¡Å«ÅŸ]{1,63}$/iu',
+            2 => '/^[\x{002d}Ð°-ÐžÐº-Ñ\x{0450}ÑÑÑÑÑÑÑ]{1,63}$/iu',
+            3 => '/^[\x{002d}0-9a-zÃ¢Ã®ÄÅÅ£]{1,63}$/iu',
+            4 => '/^[\x{002d}0-9Ð°-ÑÑ\x{04C2}]{1,63}$/iu',
+            5 => '/^[\x{002d}0-9a-zÃ Ã¡Ã¢ÃšÃ©ÃªÃ¬Ã­Ã®Ã²Ã³ÃŽÃ¹ÃºÃ»ÄÄ¡Ä§ÅŒ]{1,63}$/iu',
+            6 => '/^[\x{002d}0-9a-zÃ Ã€Ã¥ÃŠÃ©ÃªÃ²Ã³ÃŽÃ¶ÃžÃŒ]{1,63}$/iu',
+            7 => '/^[\x{002d}0-9a-zÃ³Ä
+ÄÄÅÅÅÅºÅŒ]{1,63}$/iu',
+            8 => '/^[\x{002d}0-9a-zÃ Ã¡Ã¢Ã£Ã§Ã©ÃªÃ­Ã²Ã³ÃŽÃµÃºÃŒ]{1,63}$/iu',
+            9 => '/^[\x{002d}0-9a-zÃ¢Ã®ÄÅÅ£]{1,63}$/iu',
+            10=> '/^[\x{002d}0-9a-zÃ¡Ã€Ã©Ã­Ã³ÃŽÃºÃœÄÄÄºÄŸÅÅÅ¡Å¥ÅŸ]{1,63}$/iu',
+            11=> '/^[\x{002d}0-9a-zÃ§Ã«]{1,63}$/iu',
+            12=> '/^[\x{002d}0-9Ð°-ÐžÐº-ÑÑÑÑÑÑÑ]{1,63}$/iu',
+            13=> '/^[\x{002d}0-9a-zÄÄÄÅ¡ÅŸ]{1,63}$/iu',
+            14=> '/^[\x{002d}0-9a-zÃ¢Ã§Ã¶Ã»ÃŒÄÄ±Å]{1,63}$/iu',
+            15=> '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã±Ã³ÃºÃŒ]{1,63}$/iu',
+            16=> '/^[\x{002d}0-9a-zÃ€ÃµÃ¶ÃŒÅ¡ÅŸ]{1,63}$/iu',
+            17=> '/^[\x{002d}0-9a-zÄÄÄ¥ÄµÅÅ­]{1,63}$/iu',
+            18=> '/^[\x{002d}0-9a-zÃ¢Ã€Ã©Ã«Ã®ÃŽ]{1,63}$/iu',
+            19=> '/^[\x{002d}0-9a-zÃ Ã¡Ã¢Ã€Ã¥ÃŠÃ§ÃšÃ©ÃªÃ«Ã¬Ã­Ã®Ã¯Ã°Ã±Ã²ÃŽÃ¶ÃžÃ¹ÃºÃ»ÃŒÃœÄÄÅÅÅÅÅ¡]{1,63}$/iu',
+            20=> '/^[\x{002d}0-9a-zÃ€Ã¥ÃŠÃµÃ¶ÃžÃŒÅ¡ÅŸ]{1,63}$/iu',
+            21=> '/^[\x{002d}0-9a-zÃ Ã¡Ã§ÃšÃ©Ã¬Ã­Ã²Ã³Ã¹Ãº]{1,63}$/iu',
+            22=> '/^[\x{002d}0-9a-zÃ Ã¡Ã©Ã­Ã³Ã¶ÃºÃŒÅÅ±]{1,63}$/iu',
+            23=> '/^[\x{002d}0-9ÎÎ¬-Ï]{1,63}$/iu',
+            24=> '/^[\x{002d}0-9a-zÃ Ã¡Ã¢Ã¥ÃŠÃ§ÃšÃ©ÃªÃ«Ã°Ã³ÃŽÃ¶ÃžÃŒÃŸÅ]{1,63}$/iu',
+            25=> '/^[\x{002d}0-9a-zÃ¡Ã€Ã©Ã­Ã³Ã¶ÃºÃŒÃœÄÄÄÅÅÅ¡Å¥Å¯ÅŸ]{1,63}$/iu',
+            26=> '/^[\x{002d}0-9a-zÂ·Ã Ã§ÃšÃ©Ã­Ã¯Ã²Ã³ÃºÃŒ]{1,63}$/iu',
+            27=> '/^[\x{002d}0-9Ð°-ÑÑÑÑ\x{0450}\x{045D}]{1,63}$/iu',
+            28=> '/^[\x{002d}0-9Ð°-ÑÑÑÑ]{1,63}$/iu',
+            29=> '/^[\x{002d}0-9a-zÄ
+ÄÄÄÄ¯Å¡Å«Å³ÅŸ]{1,63}$/iu',
+            30=> '/^[\x{002d}0-9a-zÃ¡Ã€Ã¥ÃŠÃ©Ã«Ã­Ã°Ã³Ã¶ÃžÃºÃŒÃœÃŸ]{1,63}$/iu',
+            31=> '/^[\x{002d}0-9a-zÃ Ã¢ÃŠÃ§ÃšÃ©ÃªÃ«Ã®Ã¯Ã±ÃŽÃ¹Ã»ÃŒÃ¿Å]{1,63}$/iu',
+            32=> '/^[\x{002d}0-9Ð°-ÑÑÑÑÑÑÑÑÑÑÑÒ]{1,63}$/iu',
+            33=> '/^[\x{002d}0-9×-×ª]{1,63}$/iu'),
+        'PR'  => array(1 => '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã³ÃºÃ±Ã€Ã«Ã¯ÃŒÃ¶Ã¢ÃªÃ®ÃŽÃ»Ã ÃšÃ¹ÃŠÃ§ÅÃ£Ãµ]{1,63}$/iu'),
+        'PT'  => array(1 => '/^[\x{002d}0-9a-zÃ¡Ã Ã¢Ã£Ã§Ã©ÃªÃ­Ã³ÃŽÃµÃº]{1,63}$/iu'),
+        'RU'  => array(1 => '/^[\x{002d}0-9Ð°-ÑÑ]{1,63}$/iu'),
+        'SA'  => array(1 => '/^[\x{002d}.0-9\x{0621}-\x{063A}\x{0641}-\x{064A}\x{0660}-\x{0669}]{1,63}$/iu'),
+        'SE'  => array(1 => '/^[\x{002d}0-9a-zÃ€Ã¥Ã©Ã¶ÃŒ]{1,63}$/iu'),
+        'SH'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿ÄÄ
+ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¡Ä£Ä¥Ä§Ä­Ä©Ä¯Ä«Ä±ÄµÄ·ÄºÄŸÄŒÅÅÅÅÅÅÅÅÅÄžÅÅÅÅÅÅ¡ÅÅ¥Å£Å§Å­Å¯Å±Å©Å³Å«ÅµÅ·ÅºÅŸÅŒ]{1,63}$/iu'),
+        'SJ'  => array(1 => '/^[\x{002d}0-9a-zÃ Ã¡Ã€-Ã©ÃªÃ±-ÃŽÃ¶ÃžÃŒÄÄÅÅÅ¡Å§ÅŸ]{1,63}$/iu'),
+        'TH'  => array(1 => '/^[\x{002d}0-9a-z\x{0E01}-\x{0E3A}\x{0E40}-\x{0E4D}\x{0E50}-\x{0E59}]{1,63}$/iu'),
+        'TM'  => array(1 => '/^[\x{002d}0-9a-zÃ -Ã¶Ãž-Ã¿ÄÄÄ
+ÄÄÄÄÄÄÄÄÄÄÄÄ¡Ä£Ä¥Ä§Ä«Ä¯ÄµÄ·ÄºÄŒÄŸÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ¡Å£Å¥Å§Å«Å­Å¯Å±Å³ÅµÅ·ÅºÅŒÅŸ]{1,63}$/iu'),
+        'TW'  => 'Zend/Validate/Hostname/Cn.php',
+        'TR'  => array(1 => '/^[\x{002d}0-9a-zÄÄ±ÃŒÅÃ¶Ã§]{1,63}$/iu'),
+        'VE'  => array(1 => '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã³ÃºÃŒÃ±]{1,63}$/iu'),
+        'VN'  => array(1 => '/^[ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ Ã¡Ã¢Ã£ÃšÃ©ÃªÃ¬Ã­Ã²Ã³ÃŽÃµÃ¹ÃºÃœÄÄÄÄÄšÄ©ÅšÅ©Æ Æ¡Æ¯Æ°\x{1EA0}-\x{1EF9}]{1,63}$/iu'),
+        'Ø§ÛØ±Ø§Ù' => array(1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu'),
+        'äž­åœ' => 'Zend/Validate/Hostname/Cn.php',
+        'å
+¬åž' => 'Zend/Validate/Hostname/Cn.php',
+        'çœç»' => 'Zend/Validate/Hostname/Cn.php'
+    );
+
+    protected $_idnLength = array(
+        'BIZ' => array(5 => 17, 11 => 15, 12 => 20),
+        'CN'  => array(1 => 20),
+        'COM' => array(3 => 17, 5 => 20),
+        'HK'  => array(1 => 15),
+        'INFO'=> array(4 => 17),
+        'KR'  => array(1 => 17),
+        'NET' => array(3 => 17, 5 => 20),
+        'ORG' => array(6 => 17),
+        'TW'  => array(1 => 20),
+        'Ø§ÛØ±Ø§Ù' => array(1 => 30),
+        'äž­åœ' => array(1 => 20),
+        'å
+¬åž' => array(1 => 20),
+        'çœç»' => array(1 => 20),
+    );
+
+    protected $_options = array(
+        'allow' => self::ALLOW_DNS,
+        'idn'   => true,
+        'tld'   => true,
+        'ip'    => null
+    );
+
+    /**
+     * Sets validator options
+     *
+     * @param integer          $allow       OPTIONAL Set what types of hostname to allow (default ALLOW_DNS)
+     * @param boolean          $validateIdn OPTIONAL Set whether IDN domains are validated (default true)
+     * @param boolean          $validateTld OPTIONAL Set whether the TLD element of a hostname is validated (default true)
+     * @param Zend_Validate_Ip $ipValidator OPTIONAL
+     * @return void
+     * @see http://www.iana.org/cctld/specifications-policies-cctlds-01apr02.htm  Technical Specifications for ccTLDs
+     */
+    public function __construct($options = array())
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } else if (!is_array($options)) {
+            $options = func_get_args();
+            $temp['allow'] = array_shift($options);
+            if (!empty($options)) {
+                $temp['idn'] = array_shift($options);
+            }
+
+            if (!empty($options)) {
+                $temp['tld'] = array_shift($options);
+            }
+
+            if (!empty($options)) {
+                $temp['ip'] = array_shift($options);
+            }
+
+            $options = $temp;
+        }
+
+        $options += $this->_options;
+        $this->setOptions($options);
+    }
+
+    /**
+     * Returns all set options
+     *
+     * @return array
+     */
+    public function getOptions()
+    {
+        return $this->_options;
+    }
+
+    /**
+     * Sets the options for this validator
+     *
+     * @param array $options
+     * @return Zend_Validate_Hostname
+     */
+    public function setOptions($options)
+    {
+        if (array_key_exists('allow', $options)) {
+            $this->setAllow($options['allow']);
+        }
+
+        if (array_key_exists('idn', $options)) {
+            $this->setValidateIdn($options['idn']);
+        }
+
+        if (array_key_exists('tld', $options)) {
+            $this->setValidateTld($options['tld']);
+        }
+
+        if (array_key_exists('ip', $options)) {
+            $this->setIpValidator($options['ip']);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the set ip validator
+     *
+     * @return Zend_Validate_Ip
+     */
+    public function getIpValidator()
+    {
+        return $this->_options['ip'];
+    }
+
+    /**
+     * @param Zend_Validate_Ip $ipValidator OPTIONAL
+     * @return void;
+     */
+    public function setIpValidator(Zend_Validate_Ip $ipValidator = null)
+    {
+        if ($ipValidator === null) {
+            $ipValidator = new Zend_Validate_Ip();
+        }
+
+        $this->_options['ip'] = $ipValidator;
+        return $this;
+    }
+
+    /**
+     * Returns the allow option
+     *
+     * @return integer
+     */
+    public function getAllow()
+    {
+        return $this->_options['allow'];
+    }
+
+    /**
+     * Sets the allow option
+     *
+     * @param  integer $allow
+     * @return Zend_Validate_Hostname Provides a fluent interface
+     */
+    public function setAllow($allow)
+    {
+        $this->_options['allow'] = $allow;
+        return $this;
+    }
+
+    /**
+     * Returns the set idn option
+     *
+     * @return boolean
+     */
+    public function getValidateIdn()
+    {
+        return $this->_options['idn'];
+    }
+
+    /**
+     * Set whether IDN domains are validated
+     *
+     * This only applies when DNS hostnames are validated
+     *
+     * @param boolean $allowed Set allowed to true to validate IDNs, and false to not validate them
+     */
+    public function setValidateIdn ($allowed)
+    {
+        $this->_options['idn'] = (bool) $allowed;
+        return $this;
+    }
+
+    /**
+     * Returns the set tld option
+     *
+     * @return boolean
+     */
+    public function getValidateTld()
+    {
+        return $this->_options['tld'];
+    }
+
+    /**
+     * Set whether the TLD element of a hostname is validated
+     *
+     * This only applies when DNS hostnames are validated
+     *
+     * @param boolean $allowed Set allowed to true to validate TLDs, and false to not validate them
+     */
+    public function setValidateTld ($allowed)
+    {
+        $this->_options['tld'] = (bool) $allowed;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the $value is a valid hostname with respect to the current allow option
+     *
+     * @param  string $value
+     * @throws Zend_Validate_Exception if a fatal error occurs for validation process
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+        // Check input against IP address schema
+        if (preg_match('/^[0-9.a-e:.]*$/i', $value) &&
+            $this->_options['ip']->setTranslator($this->getTranslator())->isValid($value)) {
+            if (!($this->_options['allow'] & self::ALLOW_IP)) {
+                $this->_error(self::IP_ADDRESS_NOT_ALLOWED);
+                return false;
+            } else {
+                return true;
+            }
+        }
+
+        // RFC3986 3.2.2 states:
+        // 
+        //     The rightmost domain label of a fully qualified domain name
+        //     in DNS may be followed by a single "." and should be if it is 
+        //     necessary to distinguish between the complete domain name and
+        //     some local domain.
+        //     
+        // Strip trailing '.' since it is not necessary to validate a non-IP
+        // hostname.
+        //
+        // (see ZF-6363)
+        if (substr($value, -1) === '.') {
+            $value = substr($value, 0, strlen($value)-1);
+        }
+        
+        // Check input against DNS hostname schema
+        $domainParts = explode('.', $value);
+        if ((count($domainParts) > 1) && (strlen($value) >= 4) && (strlen($value) <= 254)) {
+            $status = false;
+
+            $origenc = iconv_get_encoding('internal_encoding');
+            iconv_set_encoding('internal_encoding', 'UTF-8');
+            do {
+                // First check TLD
+                $matches = array();
+                if (preg_match('/([^.]{2,10})$/i', end($domainParts), $matches) ||
+                    (end($domainParts) == 'Ø§ÛØ±Ø§Ù') || (end($domainParts) == 'äž­åœ') ||
+                    (end($domainParts) == 'å
+¬åž') || (end($domainParts) == 'çœç»')) {
+
+                    reset($domainParts);
+
+                    // Hostname characters are: *(label dot)(label dot label); max 254 chars
+                    // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
+                    // id-prefix: alpha / digit
+                    // ldh: alpha / digit / dash
+
+                    // Match TLD against known list
+                    $this->_tld = strtolower($matches[1]);
+                    if ($this->_options['tld']) {
+                        if (!in_array($this->_tld, $this->_validTlds)) {
+                            $this->_error(self::UNKNOWN_TLD);
+                            $status = false;
+                            break;
+                        }
+                    }
+
+                    /**
+                     * Match against IDN hostnames
+                     * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
+                     * @see Zend_Validate_Hostname_Interface
+                     */
+                    $regexChars = array(0 => '/^[a-z0-9\x2d]{1,63}$/i');
+                    if ($this->_options['idn'] &&  isset($this->_validIdns[strtoupper($this->_tld)])) {
+                        if (is_string($this->_validIdns[strtoupper($this->_tld)])) {
+                            $regexChars += include($this->_validIdns[strtoupper($this->_tld)]);
+                        } else {
+                            $regexChars += $this->_validIdns[strtoupper($this->_tld)];
+                        }
+                    }
+
+                    // Check each hostname part
+                    $check = 0;
+                    foreach ($domainParts as $domainPart) {
+                        // Decode Punycode domainnames to IDN
+                        if (strpos($domainPart, 'xn--') === 0) {
+                            $domainPart = $this->decodePunycode(substr($domainPart, 4));
+                            if ($domainPart === false) {
+                                return false;
+                            }
+                        }
+
+                        // Check dash (-) does not start, end or appear in 3rd and 4th positions
+                        if ((strpos($domainPart, '-') === 0)
+                            || ((strlen($domainPart) > 2) && (strpos($domainPart, '-', 2) == 2) && (strpos($domainPart, '-', 3) == 3))
+                            || (strpos($domainPart, '-') === (strlen($domainPart) - 1))) {
+                                $this->_error(self::INVALID_DASH);
+                            $status = false;
+                            break 2;
+                        }
+
+                        // Check each domain part
+                        $checked = false;
+                        foreach($regexChars as $regexKey => $regexChar) {
+                            $status = @preg_match($regexChar, $domainPart);
+                            if ($status > 0) {
+                                $length = 63;
+                                if (array_key_exists(strtoupper($this->_tld), $this->_idnLength)
+                                    && (array_key_exists($regexKey, $this->_idnLength[strtoupper($this->_tld)]))) {
+                                    $length = $this->_idnLength[strtoupper($this->_tld)];
+                                }
+
+                                if (iconv_strlen($domainPart, 'UTF-8') > $length) {
+                                    $this->_error(self::INVALID_HOSTNAME);
+                                } else {
+                                    $checked = true;
+                                    break;
+                                }
+                            }
+                        }
+
+                        if ($checked) {
+                            ++$check;
+                        }
+                    }
+
+                    // If one of the labels doesn't match, the hostname is invalid
+                    if ($check !== count($domainParts)) {
+                        $this->_error(self::INVALID_HOSTNAME_SCHEMA);
+                        $status = false;
+                    }
+                } else {
+                    // Hostname not long enough
+                    $this->_error(self::UNDECIPHERABLE_TLD);
+                    $status = false;
+                }
+            } while (false);
+
+            iconv_set_encoding('internal_encoding', $origenc);
+            // If the input passes as an Internet domain name, and domain names are allowed, then the hostname
+            // passes validation
+            if ($status && ($this->_options['allow'] & self::ALLOW_DNS)) {
+                return true;
+            }
+        } else if ($this->_options['allow'] & self::ALLOW_DNS) {
+            $this->_error(self::INVALID_HOSTNAME);
+        }
+
+        // Check for URI Syntax (RFC3986)
+        if ($this->_options['allow'] & self::ALLOW_URI) {
+            if (preg_match("/^([a-zA-Z0-9-._~!$&\'()*+,;=]|%[[:xdigit:]]{2}){1,254}$/i", $value)) {
+                return true;
+            } else {
+                $this->_error(self::INVALID_URI);
+            }
+        }
+
+        // Check input against local network name schema; last chance to pass validation
+        $regexLocal = '/^(([a-zA-Z0-9\x2d]{1,63}\x2e)*[a-zA-Z0-9\x2d]{1,63}){1,254}$/';
+        $status = @preg_match($regexLocal, $value);
+
+        // If the input passes as a local network name, and local network names are allowed, then the
+        // hostname passes validation
+        $allowLocal = $this->_options['allow'] & self::ALLOW_LOCAL;
+        if ($status && $allowLocal) {
+            return true;
+        }
+
+        // If the input does not pass as a local network name, add a message
+        if (!$status) {
+            $this->_error(self::INVALID_LOCAL_NAME);
+        }
+
+        // If local network names are not allowed, add a message
+        if ($status && !$allowLocal) {
+            $this->_error(self::LOCAL_NAME_NOT_ALLOWED);
+        }
+
+        return false;
+    }
+
+    /**
+     * Decodes a punycode encoded string to it's original utf8 string
+     * In case of a decoding failure the original string is returned
+     *
+     * @param  string $encoded Punycode encoded string to decode
+     * @return string
+     */
+    protected function decodePunycode($encoded)
+    {
+        $found = preg_match('/([^a-z0-9\x2d]{1,10})$/i', $encoded);
+        if (empty($encoded) || ($found > 0)) {
+            // no punycode encoded string, return as is
+            $this->_error(self::CANNOT_DECODE_PUNYCODE);
+            return false;
+        }
+
+        $separator = strrpos($encoded, '-');
+        if ($separator > 0) {
+            for ($x = 0; $x < $separator; ++$x) {
+                // prepare decoding matrix
+                $decoded[] = ord($encoded[$x]);
+            }
+        } else {
+            $this->_error(self::CANNOT_DECODE_PUNYCODE);
+            return false;
+        }
+
+        $lengthd = count($decoded);
+        $lengthe = strlen($encoded);
+
+        // decoding
+        $init  = true;
+        $base  = 72;
+        $index = 0;
+        $char  = 0x80;
+
+        for ($indexe = ($separator) ? ($separator + 1) : 0; $indexe < $lengthe; ++$lengthd) {
+            for ($old_index = $index, $pos = 1, $key = 36; 1 ; $key += 36) {
+                $hex   = ord($encoded[$indexe++]);
+                $digit = ($hex - 48 < 10) ? $hex - 22
+                       : (($hex - 65 < 26) ? $hex - 65
+                       : (($hex - 97 < 26) ? $hex - 97
+                       : 36));
+
+                $index += $digit * $pos;
+                $tag    = ($key <= $base) ? 1 : (($key >= $base + 26) ? 26 : ($key - $base));
+                if ($digit < $tag) {
+                    break;
+                }
+
+                $pos = (int) ($pos * (36 - $tag));
+            }
+
+            $delta   = intval($init ? (($index - $old_index) / 700) : (($index - $old_index) / 2));
+            $delta  += intval($delta / ($lengthd + 1));
+            for ($key = 0; $delta > 910 / 2; $key += 36) {
+                $delta = intval($delta / 35);
+            }
+
+            $base   = intval($key + 36 * $delta / ($delta + 38));
+            $init   = false;
+            $char  += (int) ($index / ($lengthd + 1));
+            $index %= ($lengthd + 1);
+            if ($lengthd > 0) {
+                for ($i = $lengthd; $i > $index; $i--) {
+                    $decoded[$i] = $decoded[($i - 1)];
+                }
+            }
+
+            $decoded[$index++] = $char;
+        }
+
+        // convert decoded ucs4 to utf8 string
+        foreach ($decoded as $key => $value) {
+            if ($value < 128) {
+                $decoded[$key] = chr($value);
+            } elseif ($value < (1 << 11)) {
+                $decoded[$key]  = chr(192 + ($value >> 6));
+                $decoded[$key] .= chr(128 + ($value & 63));
+            } elseif ($value < (1 << 16)) {
+                $decoded[$key]  = chr(224 + ($value >> 12));
+                $decoded[$key] .= chr(128 + (($value >> 6) & 63));
+                $decoded[$key] .= chr(128 + ($value & 63));
+            } elseif ($value < (1 << 21)) {
+                $decoded[$key]  = chr(240 + ($value >> 18));
+                $decoded[$key] .= chr(128 + (($value >> 12) & 63));
+                $decoded[$key] .= chr(128 + (($value >> 6) & 63));
+                $decoded[$key] .= chr(128 + ($value & 63));
+            } else {
+                $this->_error(self::CANNOT_DECODE_PUNYCODE);
+                return false;
+            }
+        }
+
+        return implode($decoded);
+    }
+}
Index: trunk/library/Zend/Validate/Digits.php
===================================================================
--- trunk/library/Zend/Validate/Digits.php	(revision 5146)
+++ trunk/library/Zend/Validate/Digits.php	(revision 5146)
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Digits.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Digits extends Zend_Validate_Abstract
+{
+    const NOT_DIGITS   = 'notDigits';
+    const STRING_EMPTY = 'digitsStringEmpty';
+    const INVALID      = 'digitsInvalid';
+
+    /**
+     * Digits filter used for validation
+     *
+     * @var Zend_Filter_Digits
+     */
+    protected static $_filter = null;
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOT_DIGITS   => "'%value%' must contain only digits",
+        self::STRING_EMPTY => "'%value%' is an empty string",
+        self::INVALID      => "Invalid type given. String, integer or float expected",
+    );
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value only contains digit characters
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value) && !is_int($value) && !is_float($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue((string) $value);
+
+        if ('' === $this->_value) {
+            $this->_error(self::STRING_EMPTY);
+            return false;
+        }
+
+        if (null === self::$_filter) {
+            require_once 'Zend/Filter/Digits.php';
+            self::$_filter = new Zend_Filter_Digits();
+        }
+
+        if ($this->_value !== self::$_filter->filter($this->_value)) {
+            $this->_error(self::NOT_DIGITS);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: trunk/library/Zend/Validate/Hex.php
===================================================================
--- trunk/library/Zend/Validate/Hex.php	(revision 5146)
+++ trunk/library/Zend/Validate/Hex.php	(revision 5146)
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Hex.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Validate_Hex extends Zend_Validate_Abstract
+{
+    const INVALID = 'hexInvalid';
+    const NOT_HEX = 'notHex';
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID => "Invalid type given. String expected",
+        self::NOT_HEX => "'%value%' has not only hexadecimal digit characters",
+    );
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value contains only hexadecimal digit characters
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value) && !is_int($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+        if (!ctype_xdigit((string) $value)) {
+            $this->_error(self::NOT_HEX);
+            return false;
+        }
+
+        return true;
+    }
+
+}
Index: trunk/library/Zend/Validate/Hostname/Jp.php
===================================================================
--- trunk/library/Zend/Validate/Hostname/Jp.php	(revision 5146)
+++ trunk/library/Zend/Validate/Hostname/Jp.php	(revision 5146)
@@ -0,0 +1,739 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Jp.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Ressource file for japanese idn validation
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+return array(
+    1  => '/^[\x{002d}0-9a-z\x{3005}-\x{3007}\x{3041}-\x{3093}\x{309D}\x{309E}' .
+'\x{30A1}-\x{30F6}\x{30FC}' .
+'\x{30FD}\x{30FE}\x{4E00}\x{4E01}\x{4E03}\x{4E07}\x{4E08}\x{4E09}\x{4E0A}' .
+'\x{4E0B}\x{4E0D}\x{4E0E}\x{4E10}\x{4E11}\x{4E14}\x{4E15}\x{4E16}\x{4E17}' .
+'\x{4E18}\x{4E19}\x{4E1E}\x{4E21}\x{4E26}\x{4E2A}\x{4E2D}\x{4E31}\x{4E32}' .
+'\x{4E36}\x{4E38}\x{4E39}\x{4E3B}\x{4E3C}\x{4E3F}\x{4E42}\x{4E43}\x{4E45}' .
+'\x{4E4B}\x{4E4D}\x{4E4E}\x{4E4F}\x{4E55}\x{4E56}\x{4E57}\x{4E58}\x{4E59}' .
+'\x{4E5D}\x{4E5E}\x{4E5F}\x{4E62}\x{4E71}\x{4E73}\x{4E7E}\x{4E80}\x{4E82}' .
+'\x{4E85}\x{4E86}\x{4E88}\x{4E89}\x{4E8A}\x{4E8B}\x{4E8C}\x{4E8E}\x{4E91}' .
+'\x{4E92}\x{4E94}\x{4E95}\x{4E98}\x{4E99}\x{4E9B}\x{4E9C}\x{4E9E}\x{4E9F}' .
+'\x{4EA0}\x{4EA1}\x{4EA2}\x{4EA4}\x{4EA5}\x{4EA6}\x{4EA8}\x{4EAB}\x{4EAC}' .
+'\x{4EAD}\x{4EAE}\x{4EB0}\x{4EB3}\x{4EB6}\x{4EBA}\x{4EC0}\x{4EC1}\x{4EC2}' .
+'\x{4EC4}\x{4EC6}\x{4EC7}\x{4ECA}\x{4ECB}\x{4ECD}\x{4ECE}\x{4ECF}\x{4ED4}' .
+'\x{4ED5}\x{4ED6}\x{4ED7}\x{4ED8}\x{4ED9}\x{4EDD}\x{4EDE}\x{4EDF}\x{4EE3}' .
+'\x{4EE4}\x{4EE5}\x{4EED}\x{4EEE}\x{4EF0}\x{4EF2}\x{4EF6}\x{4EF7}\x{4EFB}' .
+'\x{4F01}\x{4F09}\x{4F0A}\x{4F0D}\x{4F0E}\x{4F0F}\x{4F10}\x{4F11}\x{4F1A}' .
+'\x{4F1C}\x{4F1D}\x{4F2F}\x{4F30}\x{4F34}\x{4F36}\x{4F38}\x{4F3A}\x{4F3C}' .
+'\x{4F3D}\x{4F43}\x{4F46}\x{4F47}\x{4F4D}\x{4F4E}\x{4F4F}\x{4F50}\x{4F51}' .
+'\x{4F53}\x{4F55}\x{4F57}\x{4F59}\x{4F5A}\x{4F5B}\x{4F5C}\x{4F5D}\x{4F5E}' .
+'\x{4F69}\x{4F6F}\x{4F70}\x{4F73}\x{4F75}\x{4F76}\x{4F7B}\x{4F7C}\x{4F7F}' .
+'\x{4F83}\x{4F86}\x{4F88}\x{4F8B}\x{4F8D}\x{4F8F}\x{4F91}\x{4F96}\x{4F98}' .
+'\x{4F9B}\x{4F9D}\x{4FA0}\x{4FA1}\x{4FAB}\x{4FAD}\x{4FAE}\x{4FAF}\x{4FB5}' .
+'\x{4FB6}\x{4FBF}\x{4FC2}\x{4FC3}\x{4FC4}\x{4FCA}\x{4FCE}\x{4FD0}\x{4FD1}' .
+'\x{4FD4}\x{4FD7}\x{4FD8}\x{4FDA}\x{4FDB}\x{4FDD}\x{4FDF}\x{4FE1}\x{4FE3}' .
+'\x{4FE4}\x{4FE5}\x{4FEE}\x{4FEF}\x{4FF3}\x{4FF5}\x{4FF6}\x{4FF8}\x{4FFA}' .
+'\x{4FFE}\x{5005}\x{5006}\x{5009}\x{500B}\x{500D}\x{500F}\x{5011}\x{5012}' .
+'\x{5014}\x{5016}\x{5019}\x{501A}\x{501F}\x{5021}\x{5023}\x{5024}\x{5025}' .
+'\x{5026}\x{5028}\x{5029}\x{502A}\x{502B}\x{502C}\x{502D}\x{5036}\x{5039}' .
+'\x{5043}\x{5047}\x{5048}\x{5049}\x{504F}\x{5050}\x{5055}\x{5056}\x{505A}' .
+'\x{505C}\x{5065}\x{506C}\x{5072}\x{5074}\x{5075}\x{5076}\x{5078}\x{507D}' .
+'\x{5080}\x{5085}\x{508D}\x{5091}\x{5098}\x{5099}\x{509A}\x{50AC}\x{50AD}' .
+'\x{50B2}\x{50B3}\x{50B4}\x{50B5}\x{50B7}\x{50BE}\x{50C2}\x{50C5}\x{50C9}' .
+'\x{50CA}\x{50CD}\x{50CF}\x{50D1}\x{50D5}\x{50D6}\x{50DA}\x{50DE}\x{50E3}' .
+'\x{50E5}\x{50E7}\x{50ED}\x{50EE}\x{50F5}\x{50F9}\x{50FB}\x{5100}\x{5101}' .
+'\x{5102}\x{5104}\x{5109}\x{5112}\x{5114}\x{5115}\x{5116}\x{5118}\x{511A}' .
+'\x{511F}\x{5121}\x{512A}\x{5132}\x{5137}\x{513A}\x{513B}\x{513C}\x{513F}' .
+'\x{5140}\x{5141}\x{5143}\x{5144}\x{5145}\x{5146}\x{5147}\x{5148}\x{5149}' .
+'\x{514B}\x{514C}\x{514D}\x{514E}\x{5150}\x{5152}\x{5154}\x{515A}\x{515C}' .
+'\x{5162}\x{5165}\x{5168}\x{5169}\x{516A}\x{516B}\x{516C}\x{516D}\x{516E}' .
+'\x{5171}\x{5175}\x{5176}\x{5177}\x{5178}\x{517C}\x{5180}\x{5182}\x{5185}' .
+'\x{5186}\x{5189}\x{518A}\x{518C}\x{518D}\x{518F}\x{5190}\x{5191}\x{5192}' .
+'\x{5193}\x{5195}\x{5196}\x{5197}\x{5199}\x{51A0}\x{51A2}\x{51A4}\x{51A5}' .
+'\x{51A6}\x{51A8}\x{51A9}\x{51AA}\x{51AB}\x{51AC}\x{51B0}\x{51B1}\x{51B2}' .
+'\x{51B3}\x{51B4}\x{51B5}\x{51B6}\x{51B7}\x{51BD}\x{51C4}\x{51C5}\x{51C6}' .
+'\x{51C9}\x{51CB}\x{51CC}\x{51CD}\x{51D6}\x{51DB}\x{51DC}\x{51DD}\x{51E0}' .
+'\x{51E1}\x{51E6}\x{51E7}\x{51E9}\x{51EA}\x{51ED}\x{51F0}\x{51F1}\x{51F5}' .
+'\x{51F6}\x{51F8}\x{51F9}\x{51FA}\x{51FD}\x{51FE}\x{5200}\x{5203}\x{5204}' .
+'\x{5206}\x{5207}\x{5208}\x{520A}\x{520B}\x{520E}\x{5211}\x{5214}\x{5217}' .
+'\x{521D}\x{5224}\x{5225}\x{5227}\x{5229}\x{522A}\x{522E}\x{5230}\x{5233}' .
+'\x{5236}\x{5237}\x{5238}\x{5239}\x{523A}\x{523B}\x{5243}\x{5244}\x{5247}' .
+'\x{524A}\x{524B}\x{524C}\x{524D}\x{524F}\x{5254}\x{5256}\x{525B}\x{525E}' .
+'\x{5263}\x{5264}\x{5265}\x{5269}\x{526A}\x{526F}\x{5270}\x{5271}\x{5272}' .
+'\x{5273}\x{5274}\x{5275}\x{527D}\x{527F}\x{5283}\x{5287}\x{5288}\x{5289}' .
+'\x{528D}\x{5291}\x{5292}\x{5294}\x{529B}\x{529F}\x{52A0}\x{52A3}\x{52A9}' .
+'\x{52AA}\x{52AB}\x{52AC}\x{52AD}\x{52B1}\x{52B4}\x{52B5}\x{52B9}\x{52BC}' .
+'\x{52BE}\x{52C1}\x{52C3}\x{52C5}\x{52C7}\x{52C9}\x{52CD}\x{52D2}\x{52D5}' .
+'\x{52D7}\x{52D8}\x{52D9}\x{52DD}\x{52DE}\x{52DF}\x{52E0}\x{52E2}\x{52E3}' .
+'\x{52E4}\x{52E6}\x{52E7}\x{52F2}\x{52F3}\x{52F5}\x{52F8}\x{52F9}\x{52FA}' .
+'\x{52FE}\x{52FF}\x{5301}\x{5302}\x{5305}\x{5306}\x{5308}\x{530D}\x{530F}' .
+'\x{5310}\x{5315}\x{5316}\x{5317}\x{5319}\x{531A}\x{531D}\x{5320}\x{5321}' .
+'\x{5323}\x{532A}\x{532F}\x{5331}\x{5333}\x{5338}\x{5339}\x{533A}\x{533B}' .
+'\x{533F}\x{5340}\x{5341}\x{5343}\x{5345}\x{5346}\x{5347}\x{5348}\x{5349}' .
+'\x{534A}\x{534D}\x{5351}\x{5352}\x{5353}\x{5354}\x{5357}\x{5358}\x{535A}' .
+'\x{535C}\x{535E}\x{5360}\x{5366}\x{5369}\x{536E}\x{536F}\x{5370}\x{5371}' .
+'\x{5373}\x{5374}\x{5375}\x{5377}\x{5378}\x{537B}\x{537F}\x{5382}\x{5384}' .
+'\x{5396}\x{5398}\x{539A}\x{539F}\x{53A0}\x{53A5}\x{53A6}\x{53A8}\x{53A9}' .
+'\x{53AD}\x{53AE}\x{53B0}\x{53B3}\x{53B6}\x{53BB}\x{53C2}\x{53C3}\x{53C8}' .
+'\x{53C9}\x{53CA}\x{53CB}\x{53CC}\x{53CD}\x{53CE}\x{53D4}\x{53D6}\x{53D7}' .
+'\x{53D9}\x{53DB}\x{53DF}\x{53E1}\x{53E2}\x{53E3}\x{53E4}\x{53E5}\x{53E8}' .
+'\x{53E9}\x{53EA}\x{53EB}\x{53EC}\x{53ED}\x{53EE}\x{53EF}\x{53F0}\x{53F1}' .
+'\x{53F2}\x{53F3}\x{53F6}\x{53F7}\x{53F8}\x{53FA}\x{5401}\x{5403}\x{5404}' .
+'\x{5408}\x{5409}\x{540A}\x{540B}\x{540C}\x{540D}\x{540E}\x{540F}\x{5410}' .
+'\x{5411}\x{541B}\x{541D}\x{541F}\x{5420}\x{5426}\x{5429}\x{542B}\x{542C}' .
+'\x{542D}\x{542E}\x{5436}\x{5438}\x{5439}\x{543B}\x{543C}\x{543D}\x{543E}' .
+'\x{5440}\x{5442}\x{5446}\x{5448}\x{5449}\x{544A}\x{544E}\x{5451}\x{545F}' .
+'\x{5468}\x{546A}\x{5470}\x{5471}\x{5473}\x{5475}\x{5476}\x{5477}\x{547B}' .
+'\x{547C}\x{547D}\x{5480}\x{5484}\x{5486}\x{548B}\x{548C}\x{548E}\x{548F}' .
+'\x{5490}\x{5492}\x{54A2}\x{54A4}\x{54A5}\x{54A8}\x{54AB}\x{54AC}\x{54AF}' .
+'\x{54B2}\x{54B3}\x{54B8}\x{54BC}\x{54BD}\x{54BE}\x{54C0}\x{54C1}\x{54C2}' .
+'\x{54C4}\x{54C7}\x{54C8}\x{54C9}\x{54D8}\x{54E1}\x{54E2}\x{54E5}\x{54E6}' .
+'\x{54E8}\x{54E9}\x{54ED}\x{54EE}\x{54F2}\x{54FA}\x{54FD}\x{5504}\x{5506}' .
+'\x{5507}\x{550F}\x{5510}\x{5514}\x{5516}\x{552E}\x{552F}\x{5531}\x{5533}' .
+'\x{5538}\x{5539}\x{553E}\x{5540}\x{5544}\x{5545}\x{5546}\x{554C}\x{554F}' .
+'\x{5553}\x{5556}\x{5557}\x{555C}\x{555D}\x{5563}\x{557B}\x{557C}\x{557E}' .
+'\x{5580}\x{5583}\x{5584}\x{5587}\x{5589}\x{558A}\x{558B}\x{5598}\x{5599}' .
+'\x{559A}\x{559C}\x{559D}\x{559E}\x{559F}\x{55A7}\x{55A8}\x{55A9}\x{55AA}' .
+'\x{55AB}\x{55AC}\x{55AE}\x{55B0}\x{55B6}\x{55C4}\x{55C5}\x{55C7}\x{55D4}' .
+'\x{55DA}\x{55DC}\x{55DF}\x{55E3}\x{55E4}\x{55F7}\x{55F9}\x{55FD}\x{55FE}' .
+'\x{5606}\x{5609}\x{5614}\x{5616}\x{5617}\x{5618}\x{561B}\x{5629}\x{562F}' .
+'\x{5631}\x{5632}\x{5634}\x{5636}\x{5638}\x{5642}\x{564C}\x{564E}\x{5650}' .
+'\x{565B}\x{5664}\x{5668}\x{566A}\x{566B}\x{566C}\x{5674}\x{5678}\x{567A}' .
+'\x{5680}\x{5686}\x{5687}\x{568A}\x{568F}\x{5694}\x{56A0}\x{56A2}\x{56A5}' .
+'\x{56AE}\x{56B4}\x{56B6}\x{56BC}\x{56C0}\x{56C1}\x{56C2}\x{56C3}\x{56C8}' .
+'\x{56CE}\x{56D1}\x{56D3}\x{56D7}\x{56D8}\x{56DA}\x{56DB}\x{56DE}\x{56E0}' .
+'\x{56E3}\x{56EE}\x{56F0}\x{56F2}\x{56F3}\x{56F9}\x{56FA}\x{56FD}\x{56FF}' .
+'\x{5700}\x{5703}\x{5704}\x{5708}\x{5709}\x{570B}\x{570D}\x{570F}\x{5712}' .
+'\x{5713}\x{5716}\x{5718}\x{571C}\x{571F}\x{5726}\x{5727}\x{5728}\x{572D}' .
+'\x{5730}\x{5737}\x{5738}\x{573B}\x{5740}\x{5742}\x{5747}\x{574A}\x{574E}' .
+'\x{574F}\x{5750}\x{5751}\x{5761}\x{5764}\x{5766}\x{5769}\x{576A}\x{577F}' .
+'\x{5782}\x{5788}\x{5789}\x{578B}\x{5793}\x{57A0}\x{57A2}\x{57A3}\x{57A4}' .
+'\x{57AA}\x{57B0}\x{57B3}\x{57C0}\x{57C3}\x{57C6}\x{57CB}\x{57CE}\x{57D2}' .
+'\x{57D3}\x{57D4}\x{57D6}\x{57DC}\x{57DF}\x{57E0}\x{57E3}\x{57F4}\x{57F7}' .
+'\x{57F9}\x{57FA}\x{57FC}\x{5800}\x{5802}\x{5805}\x{5806}\x{580A}\x{580B}' .
+'\x{5815}\x{5819}\x{581D}\x{5821}\x{5824}\x{582A}\x{582F}\x{5830}\x{5831}' .
+'\x{5834}\x{5835}\x{583A}\x{583D}\x{5840}\x{5841}\x{584A}\x{584B}\x{5851}' .
+'\x{5852}\x{5854}\x{5857}\x{5858}\x{5859}\x{585A}\x{585E}\x{5862}\x{5869}' .
+'\x{586B}\x{5870}\x{5872}\x{5875}\x{5879}\x{587E}\x{5883}\x{5885}\x{5893}' .
+'\x{5897}\x{589C}\x{589F}\x{58A8}\x{58AB}\x{58AE}\x{58B3}\x{58B8}\x{58B9}' .
+'\x{58BA}\x{58BB}\x{58BE}\x{58C1}\x{58C5}\x{58C7}\x{58CA}\x{58CC}\x{58D1}' .
+'\x{58D3}\x{58D5}\x{58D7}\x{58D8}\x{58D9}\x{58DC}\x{58DE}\x{58DF}\x{58E4}' .
+'\x{58E5}\x{58EB}\x{58EC}\x{58EE}\x{58EF}\x{58F0}\x{58F1}\x{58F2}\x{58F7}' .
+'\x{58F9}\x{58FA}\x{58FB}\x{58FC}\x{58FD}\x{5902}\x{5909}\x{590A}\x{590F}' .
+'\x{5910}\x{5915}\x{5916}\x{5918}\x{5919}\x{591A}\x{591B}\x{591C}\x{5922}' .
+'\x{5925}\x{5927}\x{5929}\x{592A}\x{592B}\x{592C}\x{592D}\x{592E}\x{5931}' .
+'\x{5932}\x{5937}\x{5938}\x{593E}\x{5944}\x{5947}\x{5948}\x{5949}\x{594E}' .
+'\x{594F}\x{5950}\x{5951}\x{5954}\x{5955}\x{5957}\x{5958}\x{595A}\x{5960}' .
+'\x{5962}\x{5965}\x{5967}\x{5968}\x{5969}\x{596A}\x{596C}\x{596E}\x{5973}' .
+'\x{5974}\x{5978}\x{597D}\x{5981}\x{5982}\x{5983}\x{5984}\x{598A}\x{598D}' .
+'\x{5993}\x{5996}\x{5999}\x{599B}\x{599D}\x{59A3}\x{59A5}\x{59A8}\x{59AC}' .
+'\x{59B2}\x{59B9}\x{59BB}\x{59BE}\x{59C6}\x{59C9}\x{59CB}\x{59D0}\x{59D1}' .
+'\x{59D3}\x{59D4}\x{59D9}\x{59DA}\x{59DC}\x{59E5}\x{59E6}\x{59E8}\x{59EA}' .
+'\x{59EB}\x{59F6}\x{59FB}\x{59FF}\x{5A01}\x{5A03}\x{5A09}\x{5A11}\x{5A18}' .
+'\x{5A1A}\x{5A1C}\x{5A1F}\x{5A20}\x{5A25}\x{5A29}\x{5A2F}\x{5A35}\x{5A36}' .
+'\x{5A3C}\x{5A40}\x{5A41}\x{5A46}\x{5A49}\x{5A5A}\x{5A62}\x{5A66}\x{5A6A}' .
+'\x{5A6C}\x{5A7F}\x{5A92}\x{5A9A}\x{5A9B}\x{5ABC}\x{5ABD}\x{5ABE}\x{5AC1}' .
+'\x{5AC2}\x{5AC9}\x{5ACB}\x{5ACC}\x{5AD0}\x{5AD6}\x{5AD7}\x{5AE1}\x{5AE3}' .
+'\x{5AE6}\x{5AE9}\x{5AFA}\x{5AFB}\x{5B09}\x{5B0B}\x{5B0C}\x{5B16}\x{5B22}' .
+'\x{5B2A}\x{5B2C}\x{5B30}\x{5B32}\x{5B36}\x{5B3E}\x{5B40}\x{5B43}\x{5B45}' .
+'\x{5B50}\x{5B51}\x{5B54}\x{5B55}\x{5B57}\x{5B58}\x{5B5A}\x{5B5B}\x{5B5C}' .
+'\x{5B5D}\x{5B5F}\x{5B63}\x{5B64}\x{5B65}\x{5B66}\x{5B69}\x{5B6B}\x{5B70}' .
+'\x{5B71}\x{5B73}\x{5B75}\x{5B78}\x{5B7A}\x{5B80}\x{5B83}\x{5B85}\x{5B87}' .
+'\x{5B88}\x{5B89}\x{5B8B}\x{5B8C}\x{5B8D}\x{5B8F}\x{5B95}\x{5B97}\x{5B98}' .
+'\x{5B99}\x{5B9A}\x{5B9B}\x{5B9C}\x{5B9D}\x{5B9F}\x{5BA2}\x{5BA3}\x{5BA4}' .
+'\x{5BA5}\x{5BA6}\x{5BAE}\x{5BB0}\x{5BB3}\x{5BB4}\x{5BB5}\x{5BB6}\x{5BB8}' .
+'\x{5BB9}\x{5BBF}\x{5BC2}\x{5BC3}\x{5BC4}\x{5BC5}\x{5BC6}\x{5BC7}\x{5BC9}' .
+'\x{5BCC}\x{5BD0}\x{5BD2}\x{5BD3}\x{5BD4}\x{5BDB}\x{5BDD}\x{5BDE}\x{5BDF}' .
+'\x{5BE1}\x{5BE2}\x{5BE4}\x{5BE5}\x{5BE6}\x{5BE7}\x{5BE8}\x{5BE9}\x{5BEB}' .
+'\x{5BEE}\x{5BF0}\x{5BF3}\x{5BF5}\x{5BF6}\x{5BF8}\x{5BFA}\x{5BFE}\x{5BFF}' .
+'\x{5C01}\x{5C02}\x{5C04}\x{5C05}\x{5C06}\x{5C07}\x{5C08}\x{5C09}\x{5C0A}' .
+'\x{5C0B}\x{5C0D}\x{5C0E}\x{5C0F}\x{5C11}\x{5C13}\x{5C16}\x{5C1A}\x{5C20}' .
+'\x{5C22}\x{5C24}\x{5C28}\x{5C2D}\x{5C31}\x{5C38}\x{5C39}\x{5C3A}\x{5C3B}' .
+'\x{5C3C}\x{5C3D}\x{5C3E}\x{5C3F}\x{5C40}\x{5C41}\x{5C45}\x{5C46}\x{5C48}' .
+'\x{5C4A}\x{5C4B}\x{5C4D}\x{5C4E}\x{5C4F}\x{5C50}\x{5C51}\x{5C53}\x{5C55}' .
+'\x{5C5E}\x{5C60}\x{5C61}\x{5C64}\x{5C65}\x{5C6C}\x{5C6E}\x{5C6F}\x{5C71}' .
+'\x{5C76}\x{5C79}\x{5C8C}\x{5C90}\x{5C91}\x{5C94}\x{5CA1}\x{5CA8}\x{5CA9}' .
+'\x{5CAB}\x{5CAC}\x{5CB1}\x{5CB3}\x{5CB6}\x{5CB7}\x{5CB8}\x{5CBB}\x{5CBC}' .
+'\x{5CBE}\x{5CC5}\x{5CC7}\x{5CD9}\x{5CE0}\x{5CE1}\x{5CE8}\x{5CE9}\x{5CEA}' .
+'\x{5CED}\x{5CEF}\x{5CF0}\x{5CF6}\x{5CFA}\x{5CFB}\x{5CFD}\x{5D07}\x{5D0B}' .
+'\x{5D0E}\x{5D11}\x{5D14}\x{5D15}\x{5D16}\x{5D17}\x{5D18}\x{5D19}\x{5D1A}' .
+'\x{5D1B}\x{5D1F}\x{5D22}\x{5D29}\x{5D4B}\x{5D4C}\x{5D4E}\x{5D50}\x{5D52}' .
+'\x{5D5C}\x{5D69}\x{5D6C}\x{5D6F}\x{5D73}\x{5D76}\x{5D82}\x{5D84}\x{5D87}' .
+'\x{5D8B}\x{5D8C}\x{5D90}\x{5D9D}\x{5DA2}\x{5DAC}\x{5DAE}\x{5DB7}\x{5DBA}' .
+'\x{5DBC}\x{5DBD}\x{5DC9}\x{5DCC}\x{5DCD}\x{5DD2}\x{5DD3}\x{5DD6}\x{5DDB}' .
+'\x{5DDD}\x{5DDE}\x{5DE1}\x{5DE3}\x{5DE5}\x{5DE6}\x{5DE7}\x{5DE8}\x{5DEB}' .
+'\x{5DEE}\x{5DF1}\x{5DF2}\x{5DF3}\x{5DF4}\x{5DF5}\x{5DF7}\x{5DFB}\x{5DFD}' .
+'\x{5DFE}\x{5E02}\x{5E03}\x{5E06}\x{5E0B}\x{5E0C}\x{5E11}\x{5E16}\x{5E19}' .
+'\x{5E1A}\x{5E1B}\x{5E1D}\x{5E25}\x{5E2B}\x{5E2D}\x{5E2F}\x{5E30}\x{5E33}' .
+'\x{5E36}\x{5E37}\x{5E38}\x{5E3D}\x{5E40}\x{5E43}\x{5E44}\x{5E45}\x{5E47}' .
+'\x{5E4C}\x{5E4E}\x{5E54}\x{5E55}\x{5E57}\x{5E5F}\x{5E61}\x{5E62}\x{5E63}' .
+'\x{5E64}\x{5E72}\x{5E73}\x{5E74}\x{5E75}\x{5E76}\x{5E78}\x{5E79}\x{5E7A}' .
+'\x{5E7B}\x{5E7C}\x{5E7D}\x{5E7E}\x{5E7F}\x{5E81}\x{5E83}\x{5E84}\x{5E87}' .
+'\x{5E8A}\x{5E8F}\x{5E95}\x{5E96}\x{5E97}\x{5E9A}\x{5E9C}\x{5EA0}\x{5EA6}' .
+'\x{5EA7}\x{5EAB}\x{5EAD}\x{5EB5}\x{5EB6}\x{5EB7}\x{5EB8}\x{5EC1}\x{5EC2}' .
+'\x{5EC3}\x{5EC8}\x{5EC9}\x{5ECA}\x{5ECF}\x{5ED0}\x{5ED3}\x{5ED6}\x{5EDA}' .
+'\x{5EDB}\x{5EDD}\x{5EDF}\x{5EE0}\x{5EE1}\x{5EE2}\x{5EE3}\x{5EE8}\x{5EE9}' .
+'\x{5EEC}\x{5EF0}\x{5EF1}\x{5EF3}\x{5EF4}\x{5EF6}\x{5EF7}\x{5EF8}\x{5EFA}' .
+'\x{5EFB}\x{5EFC}\x{5EFE}\x{5EFF}\x{5F01}\x{5F03}\x{5F04}\x{5F09}\x{5F0A}' .
+'\x{5F0B}\x{5F0C}\x{5F0D}\x{5F0F}\x{5F10}\x{5F11}\x{5F13}\x{5F14}\x{5F15}' .
+'\x{5F16}\x{5F17}\x{5F18}\x{5F1B}\x{5F1F}\x{5F25}\x{5F26}\x{5F27}\x{5F29}' .
+'\x{5F2D}\x{5F2F}\x{5F31}\x{5F35}\x{5F37}\x{5F38}\x{5F3C}\x{5F3E}\x{5F41}' .
+'\x{5F48}\x{5F4A}\x{5F4C}\x{5F4E}\x{5F51}\x{5F53}\x{5F56}\x{5F57}\x{5F59}' .
+'\x{5F5C}\x{5F5D}\x{5F61}\x{5F62}\x{5F66}\x{5F69}\x{5F6A}\x{5F6B}\x{5F6C}' .
+'\x{5F6D}\x{5F70}\x{5F71}\x{5F73}\x{5F77}\x{5F79}\x{5F7C}\x{5F7F}\x{5F80}' .
+'\x{5F81}\x{5F82}\x{5F83}\x{5F84}\x{5F85}\x{5F87}\x{5F88}\x{5F8A}\x{5F8B}' .
+'\x{5F8C}\x{5F90}\x{5F91}\x{5F92}\x{5F93}\x{5F97}\x{5F98}\x{5F99}\x{5F9E}' .
+'\x{5FA0}\x{5FA1}\x{5FA8}\x{5FA9}\x{5FAA}\x{5FAD}\x{5FAE}\x{5FB3}\x{5FB4}' .
+'\x{5FB9}\x{5FBC}\x{5FBD}\x{5FC3}\x{5FC5}\x{5FCC}\x{5FCD}\x{5FD6}\x{5FD7}' .
+'\x{5FD8}\x{5FD9}\x{5FDC}\x{5FDD}\x{5FE0}\x{5FE4}\x{5FEB}\x{5FF0}\x{5FF1}' .
+'\x{5FF5}\x{5FF8}\x{5FFB}\x{5FFD}\x{5FFF}\x{600E}\x{600F}\x{6010}\x{6012}' .
+'\x{6015}\x{6016}\x{6019}\x{601B}\x{601C}\x{601D}\x{6020}\x{6021}\x{6025}' .
+'\x{6026}\x{6027}\x{6028}\x{6029}\x{602A}\x{602B}\x{602F}\x{6031}\x{603A}' .
+'\x{6041}\x{6042}\x{6043}\x{6046}\x{604A}\x{604B}\x{604D}\x{6050}\x{6052}' .
+'\x{6055}\x{6059}\x{605A}\x{605F}\x{6060}\x{6062}\x{6063}\x{6064}\x{6065}' .
+'\x{6068}\x{6069}\x{606A}\x{606B}\x{606C}\x{606D}\x{606F}\x{6070}\x{6075}' .
+'\x{6077}\x{6081}\x{6083}\x{6084}\x{6089}\x{608B}\x{608C}\x{608D}\x{6092}' .
+'\x{6094}\x{6096}\x{6097}\x{609A}\x{609B}\x{609F}\x{60A0}\x{60A3}\x{60A6}' .
+'\x{60A7}\x{60A9}\x{60AA}\x{60B2}\x{60B3}\x{60B4}\x{60B5}\x{60B6}\x{60B8}' .
+'\x{60BC}\x{60BD}\x{60C5}\x{60C6}\x{60C7}\x{60D1}\x{60D3}\x{60D8}\x{60DA}' .
+'\x{60DC}\x{60DF}\x{60E0}\x{60E1}\x{60E3}\x{60E7}\x{60E8}\x{60F0}\x{60F1}' .
+'\x{60F3}\x{60F4}\x{60F6}\x{60F7}\x{60F9}\x{60FA}\x{60FB}\x{6100}\x{6101}' .
+'\x{6103}\x{6106}\x{6108}\x{6109}\x{610D}\x{610E}\x{610F}\x{6115}\x{611A}' .
+'\x{611B}\x{611F}\x{6121}\x{6127}\x{6128}\x{612C}\x{6134}\x{613C}\x{613D}' .
+'\x{613E}\x{613F}\x{6142}\x{6144}\x{6147}\x{6148}\x{614A}\x{614B}\x{614C}' .
+'\x{614D}\x{614E}\x{6153}\x{6155}\x{6158}\x{6159}\x{615A}\x{615D}\x{615F}' .
+'\x{6162}\x{6163}\x{6165}\x{6167}\x{6168}\x{616B}\x{616E}\x{616F}\x{6170}' .
+'\x{6171}\x{6173}\x{6174}\x{6175}\x{6176}\x{6177}\x{617E}\x{6182}\x{6187}' .
+'\x{618A}\x{618E}\x{6190}\x{6191}\x{6194}\x{6196}\x{6199}\x{619A}\x{61A4}' .
+'\x{61A7}\x{61A9}\x{61AB}\x{61AC}\x{61AE}\x{61B2}\x{61B6}\x{61BA}\x{61BE}' .
+'\x{61C3}\x{61C6}\x{61C7}\x{61C8}\x{61C9}\x{61CA}\x{61CB}\x{61CC}\x{61CD}' .
+'\x{61D0}\x{61E3}\x{61E6}\x{61F2}\x{61F4}\x{61F6}\x{61F7}\x{61F8}\x{61FA}' .
+'\x{61FC}\x{61FD}\x{61FE}\x{61FF}\x{6200}\x{6208}\x{6209}\x{620A}\x{620C}' .
+'\x{620D}\x{620E}\x{6210}\x{6211}\x{6212}\x{6214}\x{6216}\x{621A}\x{621B}' .
+'\x{621D}\x{621E}\x{621F}\x{6221}\x{6226}\x{622A}\x{622E}\x{622F}\x{6230}' .
+'\x{6232}\x{6233}\x{6234}\x{6238}\x{623B}\x{623F}\x{6240}\x{6241}\x{6247}' .
+'\x{6248}\x{6249}\x{624B}\x{624D}\x{624E}\x{6253}\x{6255}\x{6258}\x{625B}' .
+'\x{625E}\x{6260}\x{6263}\x{6268}\x{626E}\x{6271}\x{6276}\x{6279}\x{627C}' .
+'\x{627E}\x{627F}\x{6280}\x{6282}\x{6283}\x{6284}\x{6289}\x{628A}\x{6291}' .
+'\x{6292}\x{6293}\x{6294}\x{6295}\x{6296}\x{6297}\x{6298}\x{629B}\x{629C}' .
+'\x{629E}\x{62AB}\x{62AC}\x{62B1}\x{62B5}\x{62B9}\x{62BB}\x{62BC}\x{62BD}' .
+'\x{62C2}\x{62C5}\x{62C6}\x{62C7}\x{62C8}\x{62C9}\x{62CA}\x{62CC}\x{62CD}' .
+'\x{62CF}\x{62D0}\x{62D1}\x{62D2}\x{62D3}\x{62D4}\x{62D7}\x{62D8}\x{62D9}' .
+'\x{62DB}\x{62DC}\x{62DD}\x{62E0}\x{62E1}\x{62EC}\x{62ED}\x{62EE}\x{62EF}' .
+'\x{62F1}\x{62F3}\x{62F5}\x{62F6}\x{62F7}\x{62FE}\x{62FF}\x{6301}\x{6302}' .
+'\x{6307}\x{6308}\x{6309}\x{630C}\x{6311}\x{6319}\x{631F}\x{6327}\x{6328}' .
+'\x{632B}\x{632F}\x{633A}\x{633D}\x{633E}\x{633F}\x{6349}\x{634C}\x{634D}' .
+'\x{634F}\x{6350}\x{6355}\x{6357}\x{635C}\x{6367}\x{6368}\x{6369}\x{636B}' .
+'\x{636E}\x{6372}\x{6376}\x{6377}\x{637A}\x{637B}\x{6380}\x{6383}\x{6388}' .
+'\x{6389}\x{638C}\x{638E}\x{638F}\x{6392}\x{6396}\x{6398}\x{639B}\x{639F}' .
+'\x{63A0}\x{63A1}\x{63A2}\x{63A3}\x{63A5}\x{63A7}\x{63A8}\x{63A9}\x{63AA}' .
+'\x{63AB}\x{63AC}\x{63B2}\x{63B4}\x{63B5}\x{63BB}\x{63BE}\x{63C0}\x{63C3}' .
+'\x{63C4}\x{63C6}\x{63C9}\x{63CF}\x{63D0}\x{63D2}\x{63D6}\x{63DA}\x{63DB}' .
+'\x{63E1}\x{63E3}\x{63E9}\x{63EE}\x{63F4}\x{63F6}\x{63FA}\x{6406}\x{640D}' .
+'\x{640F}\x{6413}\x{6416}\x{6417}\x{641C}\x{6426}\x{6428}\x{642C}\x{642D}' .
+'\x{6434}\x{6436}\x{643A}\x{643E}\x{6442}\x{644E}\x{6458}\x{6467}\x{6469}' .
+'\x{646F}\x{6476}\x{6478}\x{647A}\x{6483}\x{6488}\x{6492}\x{6493}\x{6495}' .
+'\x{649A}\x{649E}\x{64A4}\x{64A5}\x{64A9}\x{64AB}\x{64AD}\x{64AE}\x{64B0}' .
+'\x{64B2}\x{64B9}\x{64BB}\x{64BC}\x{64C1}\x{64C2}\x{64C5}\x{64C7}\x{64CD}' .
+'\x{64D2}\x{64D4}\x{64D8}\x{64DA}\x{64E0}\x{64E1}\x{64E2}\x{64E3}\x{64E6}' .
+'\x{64E7}\x{64EC}\x{64EF}\x{64F1}\x{64F2}\x{64F4}\x{64F6}\x{64FA}\x{64FD}' .
+'\x{64FE}\x{6500}\x{6505}\x{6518}\x{651C}\x{651D}\x{6523}\x{6524}\x{652A}' .
+'\x{652B}\x{652C}\x{652F}\x{6534}\x{6535}\x{6536}\x{6537}\x{6538}\x{6539}' .
+'\x{653B}\x{653E}\x{653F}\x{6545}\x{6548}\x{654D}\x{654F}\x{6551}\x{6555}' .
+'\x{6556}\x{6557}\x{6558}\x{6559}\x{655D}\x{655E}\x{6562}\x{6563}\x{6566}' .
+'\x{656C}\x{6570}\x{6572}\x{6574}\x{6575}\x{6577}\x{6578}\x{6582}\x{6583}' .
+'\x{6587}\x{6588}\x{6589}\x{658C}\x{658E}\x{6590}\x{6591}\x{6597}\x{6599}' .
+'\x{659B}\x{659C}\x{659F}\x{65A1}\x{65A4}\x{65A5}\x{65A7}\x{65AB}\x{65AC}' .
+'\x{65AD}\x{65AF}\x{65B0}\x{65B7}\x{65B9}\x{65BC}\x{65BD}\x{65C1}\x{65C3}' .
+'\x{65C4}\x{65C5}\x{65C6}\x{65CB}\x{65CC}\x{65CF}\x{65D2}\x{65D7}\x{65D9}' .
+'\x{65DB}\x{65E0}\x{65E1}\x{65E2}\x{65E5}\x{65E6}\x{65E7}\x{65E8}\x{65E9}' .
+'\x{65EC}\x{65ED}\x{65F1}\x{65FA}\x{65FB}\x{6602}\x{6603}\x{6606}\x{6607}' .
+'\x{660A}\x{660C}\x{660E}\x{660F}\x{6613}\x{6614}\x{661C}\x{661F}\x{6620}' .
+'\x{6625}\x{6627}\x{6628}\x{662D}\x{662F}\x{6634}\x{6635}\x{6636}\x{663C}' .
+'\x{663F}\x{6641}\x{6642}\x{6643}\x{6644}\x{6649}\x{664B}\x{664F}\x{6652}' .
+'\x{665D}\x{665E}\x{665F}\x{6662}\x{6664}\x{6666}\x{6667}\x{6668}\x{6669}' .
+'\x{666E}\x{666F}\x{6670}\x{6674}\x{6676}\x{667A}\x{6681}\x{6683}\x{6684}' .
+'\x{6687}\x{6688}\x{6689}\x{668E}\x{6691}\x{6696}\x{6697}\x{6698}\x{669D}' .
+'\x{66A2}\x{66A6}\x{66AB}\x{66AE}\x{66B4}\x{66B8}\x{66B9}\x{66BC}\x{66BE}' .
+'\x{66C1}\x{66C4}\x{66C7}\x{66C9}\x{66D6}\x{66D9}\x{66DA}\x{66DC}\x{66DD}' .
+'\x{66E0}\x{66E6}\x{66E9}\x{66F0}\x{66F2}\x{66F3}\x{66F4}\x{66F5}\x{66F7}' .
+'\x{66F8}\x{66F9}\x{66FC}\x{66FD}\x{66FE}\x{66FF}\x{6700}\x{6703}\x{6708}' .
+'\x{6709}\x{670B}\x{670D}\x{670F}\x{6714}\x{6715}\x{6716}\x{6717}\x{671B}' .
+'\x{671D}\x{671E}\x{671F}\x{6726}\x{6727}\x{6728}\x{672A}\x{672B}\x{672C}' .
+'\x{672D}\x{672E}\x{6731}\x{6734}\x{6736}\x{6737}\x{6738}\x{673A}\x{673D}' .
+'\x{673F}\x{6741}\x{6746}\x{6749}\x{674E}\x{674F}\x{6750}\x{6751}\x{6753}' .
+'\x{6756}\x{6759}\x{675C}\x{675E}\x{675F}\x{6760}\x{6761}\x{6762}\x{6763}' .
+'\x{6764}\x{6765}\x{676A}\x{676D}\x{676F}\x{6770}\x{6771}\x{6772}\x{6773}' .
+'\x{6775}\x{6777}\x{677C}\x{677E}\x{677F}\x{6785}\x{6787}\x{6789}\x{678B}' .
+'\x{678C}\x{6790}\x{6795}\x{6797}\x{679A}\x{679C}\x{679D}\x{67A0}\x{67A1}' .
+'\x{67A2}\x{67A6}\x{67A9}\x{67AF}\x{67B3}\x{67B4}\x{67B6}\x{67B7}\x{67B8}' .
+'\x{67B9}\x{67C1}\x{67C4}\x{67C6}\x{67CA}\x{67CE}\x{67CF}\x{67D0}\x{67D1}' .
+'\x{67D3}\x{67D4}\x{67D8}\x{67DA}\x{67DD}\x{67DE}\x{67E2}\x{67E4}\x{67E7}' .
+'\x{67E9}\x{67EC}\x{67EE}\x{67EF}\x{67F1}\x{67F3}\x{67F4}\x{67F5}\x{67FB}' .
+'\x{67FE}\x{67FF}\x{6802}\x{6803}\x{6804}\x{6813}\x{6816}\x{6817}\x{681E}' .
+'\x{6821}\x{6822}\x{6829}\x{682A}\x{682B}\x{6832}\x{6834}\x{6838}\x{6839}' .
+'\x{683C}\x{683D}\x{6840}\x{6841}\x{6842}\x{6843}\x{6846}\x{6848}\x{684D}' .
+'\x{684E}\x{6850}\x{6851}\x{6853}\x{6854}\x{6859}\x{685C}\x{685D}\x{685F}' .
+'\x{6863}\x{6867}\x{6874}\x{6876}\x{6877}\x{687E}\x{687F}\x{6881}\x{6883}' .
+'\x{6885}\x{688D}\x{688F}\x{6893}\x{6894}\x{6897}\x{689B}\x{689D}\x{689F}' .
+'\x{68A0}\x{68A2}\x{68A6}\x{68A7}\x{68A8}\x{68AD}\x{68AF}\x{68B0}\x{68B1}' .
+'\x{68B3}\x{68B5}\x{68B6}\x{68B9}\x{68BA}\x{68BC}\x{68C4}\x{68C6}\x{68C9}' .
+'\x{68CA}\x{68CB}\x{68CD}\x{68D2}\x{68D4}\x{68D5}\x{68D7}\x{68D8}\x{68DA}' .
+'\x{68DF}\x{68E0}\x{68E1}\x{68E3}\x{68E7}\x{68EE}\x{68EF}\x{68F2}\x{68F9}' .
+'\x{68FA}\x{6900}\x{6901}\x{6904}\x{6905}\x{6908}\x{690B}\x{690C}\x{690D}' .
+'\x{690E}\x{690F}\x{6912}\x{6919}\x{691A}\x{691B}\x{691C}\x{6921}\x{6922}' .
+'\x{6923}\x{6925}\x{6926}\x{6928}\x{692A}\x{6930}\x{6934}\x{6936}\x{6939}' .
+'\x{693D}\x{693F}\x{694A}\x{6953}\x{6954}\x{6955}\x{6959}\x{695A}\x{695C}' .
+'\x{695D}\x{695E}\x{6960}\x{6961}\x{6962}\x{696A}\x{696B}\x{696D}\x{696E}' .
+'\x{696F}\x{6973}\x{6974}\x{6975}\x{6977}\x{6978}\x{6979}\x{697C}\x{697D}' .
+'\x{697E}\x{6981}\x{6982}\x{698A}\x{698E}\x{6991}\x{6994}\x{6995}\x{699B}' .
+'\x{699C}\x{69A0}\x{69A7}\x{69AE}\x{69B1}\x{69B2}\x{69B4}\x{69BB}\x{69BE}' .
+'\x{69BF}\x{69C1}\x{69C3}\x{69C7}\x{69CA}\x{69CB}\x{69CC}\x{69CD}\x{69CE}' .
+'\x{69D0}\x{69D3}\x{69D8}\x{69D9}\x{69DD}\x{69DE}\x{69E7}\x{69E8}\x{69EB}' .
+'\x{69ED}\x{69F2}\x{69F9}\x{69FB}\x{69FD}\x{69FF}\x{6A02}\x{6A05}\x{6A0A}' .
+'\x{6A0B}\x{6A0C}\x{6A12}\x{6A13}\x{6A14}\x{6A17}\x{6A19}\x{6A1B}\x{6A1E}' .
+'\x{6A1F}\x{6A21}\x{6A22}\x{6A23}\x{6A29}\x{6A2A}\x{6A2B}\x{6A2E}\x{6A35}' .
+'\x{6A36}\x{6A38}\x{6A39}\x{6A3A}\x{6A3D}\x{6A44}\x{6A47}\x{6A48}\x{6A4B}' .
+'\x{6A58}\x{6A59}\x{6A5F}\x{6A61}\x{6A62}\x{6A66}\x{6A72}\x{6A78}\x{6A7F}' .
+'\x{6A80}\x{6A84}\x{6A8D}\x{6A8E}\x{6A90}\x{6A97}\x{6A9C}\x{6AA0}\x{6AA2}' .
+'\x{6AA3}\x{6AAA}\x{6AAC}\x{6AAE}\x{6AB3}\x{6AB8}\x{6ABB}\x{6AC1}\x{6AC2}' .
+'\x{6AC3}\x{6AD1}\x{6AD3}\x{6ADA}\x{6ADB}\x{6ADE}\x{6ADF}\x{6AE8}\x{6AEA}' .
+'\x{6AFA}\x{6AFB}\x{6B04}\x{6B05}\x{6B0A}\x{6B12}\x{6B16}\x{6B1D}\x{6B1F}' .
+'\x{6B20}\x{6B21}\x{6B23}\x{6B27}\x{6B32}\x{6B37}\x{6B38}\x{6B39}\x{6B3A}' .
+'\x{6B3D}\x{6B3E}\x{6B43}\x{6B47}\x{6B49}\x{6B4C}\x{6B4E}\x{6B50}\x{6B53}' .
+'\x{6B54}\x{6B59}\x{6B5B}\x{6B5F}\x{6B61}\x{6B62}\x{6B63}\x{6B64}\x{6B66}' .
+'\x{6B69}\x{6B6A}\x{6B6F}\x{6B73}\x{6B74}\x{6B78}\x{6B79}\x{6B7B}\x{6B7F}' .
+'\x{6B80}\x{6B83}\x{6B84}\x{6B86}\x{6B89}\x{6B8A}\x{6B8B}\x{6B8D}\x{6B95}' .
+'\x{6B96}\x{6B98}\x{6B9E}\x{6BA4}\x{6BAA}\x{6BAB}\x{6BAF}\x{6BB1}\x{6BB2}' .
+'\x{6BB3}\x{6BB4}\x{6BB5}\x{6BB7}\x{6BBA}\x{6BBB}\x{6BBC}\x{6BBF}\x{6BC0}' .
+'\x{6BC5}\x{6BC6}\x{6BCB}\x{6BCD}\x{6BCE}\x{6BD2}\x{6BD3}\x{6BD4}\x{6BD8}' .
+'\x{6BDB}\x{6BDF}\x{6BEB}\x{6BEC}\x{6BEF}\x{6BF3}\x{6C08}\x{6C0F}\x{6C11}' .
+'\x{6C13}\x{6C14}\x{6C17}\x{6C1B}\x{6C23}\x{6C24}\x{6C34}\x{6C37}\x{6C38}' .
+'\x{6C3E}\x{6C40}\x{6C41}\x{6C42}\x{6C4E}\x{6C50}\x{6C55}\x{6C57}\x{6C5A}' .
+'\x{6C5D}\x{6C5E}\x{6C5F}\x{6C60}\x{6C62}\x{6C68}\x{6C6A}\x{6C70}\x{6C72}' .
+'\x{6C73}\x{6C7A}\x{6C7D}\x{6C7E}\x{6C81}\x{6C82}\x{6C83}\x{6C88}\x{6C8C}' .
+'\x{6C8D}\x{6C90}\x{6C92}\x{6C93}\x{6C96}\x{6C99}\x{6C9A}\x{6C9B}\x{6CA1}' .
+'\x{6CA2}\x{6CAB}\x{6CAE}\x{6CB1}\x{6CB3}\x{6CB8}\x{6CB9}\x{6CBA}\x{6CBB}' .
+'\x{6CBC}\x{6CBD}\x{6CBE}\x{6CBF}\x{6CC1}\x{6CC4}\x{6CC5}\x{6CC9}\x{6CCA}' .
+'\x{6CCC}\x{6CD3}\x{6CD5}\x{6CD7}\x{6CD9}\x{6CDB}\x{6CDD}\x{6CE1}\x{6CE2}' .
+'\x{6CE3}\x{6CE5}\x{6CE8}\x{6CEA}\x{6CEF}\x{6CF0}\x{6CF1}\x{6CF3}\x{6D0B}' .
+'\x{6D0C}\x{6D12}\x{6D17}\x{6D19}\x{6D1B}\x{6D1E}\x{6D1F}\x{6D25}\x{6D29}' .
+'\x{6D2A}\x{6D2B}\x{6D32}\x{6D33}\x{6D35}\x{6D36}\x{6D38}\x{6D3B}\x{6D3D}' .
+'\x{6D3E}\x{6D41}\x{6D44}\x{6D45}\x{6D59}\x{6D5A}\x{6D5C}\x{6D63}\x{6D64}' .
+'\x{6D66}\x{6D69}\x{6D6A}\x{6D6C}\x{6D6E}\x{6D74}\x{6D77}\x{6D78}\x{6D79}' .
+'\x{6D85}\x{6D88}\x{6D8C}\x{6D8E}\x{6D93}\x{6D95}\x{6D99}\x{6D9B}\x{6D9C}' .
+'\x{6DAF}\x{6DB2}\x{6DB5}\x{6DB8}\x{6DBC}\x{6DC0}\x{6DC5}\x{6DC6}\x{6DC7}' .
+'\x{6DCB}\x{6DCC}\x{6DD1}\x{6DD2}\x{6DD5}\x{6DD8}\x{6DD9}\x{6DDE}\x{6DE1}' .
+'\x{6DE4}\x{6DE6}\x{6DE8}\x{6DEA}\x{6DEB}\x{6DEC}\x{6DEE}\x{6DF1}\x{6DF3}' .
+'\x{6DF5}\x{6DF7}\x{6DF9}\x{6DFA}\x{6DFB}\x{6E05}\x{6E07}\x{6E08}\x{6E09}' .
+'\x{6E0A}\x{6E0B}\x{6E13}\x{6E15}\x{6E19}\x{6E1A}\x{6E1B}\x{6E1D}\x{6E1F}' .
+'\x{6E20}\x{6E21}\x{6E23}\x{6E24}\x{6E25}\x{6E26}\x{6E29}\x{6E2B}\x{6E2C}' .
+'\x{6E2D}\x{6E2E}\x{6E2F}\x{6E38}\x{6E3A}\x{6E3E}\x{6E43}\x{6E4A}\x{6E4D}' .
+'\x{6E4E}\x{6E56}\x{6E58}\x{6E5B}\x{6E5F}\x{6E67}\x{6E6B}\x{6E6E}\x{6E6F}' .
+'\x{6E72}\x{6E76}\x{6E7E}\x{6E7F}\x{6E80}\x{6E82}\x{6E8C}\x{6E8F}\x{6E90}' .
+'\x{6E96}\x{6E98}\x{6E9C}\x{6E9D}\x{6E9F}\x{6EA2}\x{6EA5}\x{6EAA}\x{6EAF}' .
+'\x{6EB2}\x{6EB6}\x{6EB7}\x{6EBA}\x{6EBD}\x{6EC2}\x{6EC4}\x{6EC5}\x{6EC9}' .
+'\x{6ECB}\x{6ECC}\x{6ED1}\x{6ED3}\x{6ED4}\x{6ED5}\x{6EDD}\x{6EDE}\x{6EEC}' .
+'\x{6EEF}\x{6EF2}\x{6EF4}\x{6EF7}\x{6EF8}\x{6EFE}\x{6EFF}\x{6F01}\x{6F02}' .
+'\x{6F06}\x{6F09}\x{6F0F}\x{6F11}\x{6F13}\x{6F14}\x{6F15}\x{6F20}\x{6F22}' .
+'\x{6F23}\x{6F2B}\x{6F2C}\x{6F31}\x{6F32}\x{6F38}\x{6F3E}\x{6F3F}\x{6F41}' .
+'\x{6F45}\x{6F54}\x{6F58}\x{6F5B}\x{6F5C}\x{6F5F}\x{6F64}\x{6F66}\x{6F6D}' .
+'\x{6F6E}\x{6F6F}\x{6F70}\x{6F74}\x{6F78}\x{6F7A}\x{6F7C}\x{6F80}\x{6F81}' .
+'\x{6F82}\x{6F84}\x{6F86}\x{6F8E}\x{6F91}\x{6F97}\x{6FA1}\x{6FA3}\x{6FA4}' .
+'\x{6FAA}\x{6FB1}\x{6FB3}\x{6FB9}\x{6FC0}\x{6FC1}\x{6FC2}\x{6FC3}\x{6FC6}' .
+'\x{6FD4}\x{6FD5}\x{6FD8}\x{6FDB}\x{6FDF}\x{6FE0}\x{6FE1}\x{6FE4}\x{6FEB}' .
+'\x{6FEC}\x{6FEE}\x{6FEF}\x{6FF1}\x{6FF3}\x{6FF6}\x{6FFA}\x{6FFE}\x{7001}' .
+'\x{7009}\x{700B}\x{700F}\x{7011}\x{7015}\x{7018}\x{701A}\x{701B}\x{701D}' .
+'\x{701E}\x{701F}\x{7026}\x{7027}\x{702C}\x{7030}\x{7032}\x{703E}\x{704C}' .
+'\x{7051}\x{7058}\x{7063}\x{706B}\x{706F}\x{7070}\x{7078}\x{707C}\x{707D}' .
+'\x{7089}\x{708A}\x{708E}\x{7092}\x{7099}\x{70AC}\x{70AD}\x{70AE}\x{70AF}' .
+'\x{70B3}\x{70B8}\x{70B9}\x{70BA}\x{70C8}\x{70CB}\x{70CF}\x{70D9}\x{70DD}' .
+'\x{70DF}\x{70F1}\x{70F9}\x{70FD}\x{7109}\x{7114}\x{7119}\x{711A}\x{711C}' .
+'\x{7121}\x{7126}\x{7136}\x{713C}\x{7149}\x{714C}\x{714E}\x{7155}\x{7156}' .
+'\x{7159}\x{7162}\x{7164}\x{7165}\x{7166}\x{7167}\x{7169}\x{716C}\x{716E}' .
+'\x{717D}\x{7184}\x{7188}\x{718A}\x{718F}\x{7194}\x{7195}\x{7199}\x{719F}' .
+'\x{71A8}\x{71AC}\x{71B1}\x{71B9}\x{71BE}\x{71C3}\x{71C8}\x{71C9}\x{71CE}' .
+'\x{71D0}\x{71D2}\x{71D4}\x{71D5}\x{71D7}\x{71DF}\x{71E0}\x{71E5}\x{71E6}' .
+'\x{71E7}\x{71EC}\x{71ED}\x{71EE}\x{71F5}\x{71F9}\x{71FB}\x{71FC}\x{71FF}' .
+'\x{7206}\x{720D}\x{7210}\x{721B}\x{7228}\x{722A}\x{722C}\x{722D}\x{7230}' .
+'\x{7232}\x{7235}\x{7236}\x{723A}\x{723B}\x{723C}\x{723D}\x{723E}\x{723F}' .
+'\x{7240}\x{7246}\x{7247}\x{7248}\x{724B}\x{724C}\x{7252}\x{7258}\x{7259}' .
+'\x{725B}\x{725D}\x{725F}\x{7261}\x{7262}\x{7267}\x{7269}\x{7272}\x{7274}' .
+'\x{7279}\x{727D}\x{727E}\x{7280}\x{7281}\x{7282}\x{7287}\x{7292}\x{7296}' .
+'\x{72A0}\x{72A2}\x{72A7}\x{72AC}\x{72AF}\x{72B2}\x{72B6}\x{72B9}\x{72C2}' .
+'\x{72C3}\x{72C4}\x{72C6}\x{72CE}\x{72D0}\x{72D2}\x{72D7}\x{72D9}\x{72DB}' .
+'\x{72E0}\x{72E1}\x{72E2}\x{72E9}\x{72EC}\x{72ED}\x{72F7}\x{72F8}\x{72F9}' .
+'\x{72FC}\x{72FD}\x{730A}\x{7316}\x{7317}\x{731B}\x{731C}\x{731D}\x{731F}' .
+'\x{7325}\x{7329}\x{732A}\x{732B}\x{732E}\x{732F}\x{7334}\x{7336}\x{7337}' .
+'\x{733E}\x{733F}\x{7344}\x{7345}\x{734E}\x{734F}\x{7357}\x{7363}\x{7368}' .
+'\x{736A}\x{7370}\x{7372}\x{7375}\x{7378}\x{737A}\x{737B}\x{7384}\x{7387}' .
+'\x{7389}\x{738B}\x{7396}\x{73A9}\x{73B2}\x{73B3}\x{73BB}\x{73C0}\x{73C2}' .
+'\x{73C8}\x{73CA}\x{73CD}\x{73CE}\x{73DE}\x{73E0}\x{73E5}\x{73EA}\x{73ED}' .
+'\x{73EE}\x{73F1}\x{73F8}\x{73FE}\x{7403}\x{7405}\x{7406}\x{7409}\x{7422}' .
+'\x{7425}\x{7432}\x{7433}\x{7434}\x{7435}\x{7436}\x{743A}\x{743F}\x{7441}' .
+'\x{7455}\x{7459}\x{745A}\x{745B}\x{745C}\x{745E}\x{745F}\x{7460}\x{7463}' .
+'\x{7464}\x{7469}\x{746A}\x{746F}\x{7470}\x{7473}\x{7476}\x{747E}\x{7483}' .
+'\x{748B}\x{749E}\x{74A2}\x{74A7}\x{74B0}\x{74BD}\x{74CA}\x{74CF}\x{74D4}' .
+'\x{74DC}\x{74E0}\x{74E2}\x{74E3}\x{74E6}\x{74E7}\x{74E9}\x{74EE}\x{74F0}' .
+'\x{74F1}\x{74F2}\x{74F6}\x{74F7}\x{74F8}\x{7503}\x{7504}\x{7505}\x{750C}' .
+'\x{750D}\x{750E}\x{7511}\x{7513}\x{7515}\x{7518}\x{751A}\x{751C}\x{751E}' .
+'\x{751F}\x{7523}\x{7525}\x{7526}\x{7528}\x{752B}\x{752C}\x{7530}\x{7531}' .
+'\x{7532}\x{7533}\x{7537}\x{7538}\x{753A}\x{753B}\x{753C}\x{7544}\x{7546}' .
+'\x{7549}\x{754A}\x{754B}\x{754C}\x{754D}\x{754F}\x{7551}\x{7554}\x{7559}' .
+'\x{755A}\x{755B}\x{755C}\x{755D}\x{7560}\x{7562}\x{7564}\x{7565}\x{7566}' .
+'\x{7567}\x{7569}\x{756A}\x{756B}\x{756D}\x{7570}\x{7573}\x{7574}\x{7576}' .
+'\x{7577}\x{7578}\x{757F}\x{7582}\x{7586}\x{7587}\x{7589}\x{758A}\x{758B}' .
+'\x{758E}\x{758F}\x{7591}\x{7594}\x{759A}\x{759D}\x{75A3}\x{75A5}\x{75AB}' .
+'\x{75B1}\x{75B2}\x{75B3}\x{75B5}\x{75B8}\x{75B9}\x{75BC}\x{75BD}\x{75BE}' .
+'\x{75C2}\x{75C3}\x{75C5}\x{75C7}\x{75CA}\x{75CD}\x{75D2}\x{75D4}\x{75D5}' .
+'\x{75D8}\x{75D9}\x{75DB}\x{75DE}\x{75E2}\x{75E3}\x{75E9}\x{75F0}\x{75F2}' .
+'\x{75F3}\x{75F4}\x{75FA}\x{75FC}\x{75FE}\x{75FF}\x{7601}\x{7609}\x{760B}' .
+'\x{760D}\x{761F}\x{7620}\x{7621}\x{7622}\x{7624}\x{7627}\x{7630}\x{7634}' .
+'\x{763B}\x{7642}\x{7646}\x{7647}\x{7648}\x{764C}\x{7652}\x{7656}\x{7658}' .
+'\x{765C}\x{7661}\x{7662}\x{7667}\x{7668}\x{7669}\x{766A}\x{766C}\x{7670}' .
+'\x{7672}\x{7676}\x{7678}\x{767A}\x{767B}\x{767C}\x{767D}\x{767E}\x{7680}' .
+'\x{7683}\x{7684}\x{7686}\x{7687}\x{7688}\x{768B}\x{768E}\x{7690}\x{7693}' .
+'\x{7696}\x{7699}\x{769A}\x{76AE}\x{76B0}\x{76B4}\x{76B7}\x{76B8}\x{76B9}' .
+'\x{76BA}\x{76BF}\x{76C2}\x{76C3}\x{76C6}\x{76C8}\x{76CA}\x{76CD}\x{76D2}' .
+'\x{76D6}\x{76D7}\x{76DB}\x{76DC}\x{76DE}\x{76DF}\x{76E1}\x{76E3}\x{76E4}' .
+'\x{76E5}\x{76E7}\x{76EA}\x{76EE}\x{76F2}\x{76F4}\x{76F8}\x{76FB}\x{76FE}' .
+'\x{7701}\x{7704}\x{7707}\x{7708}\x{7709}\x{770B}\x{770C}\x{771B}\x{771E}' .
+'\x{771F}\x{7720}\x{7724}\x{7725}\x{7726}\x{7729}\x{7737}\x{7738}\x{773A}' .
+'\x{773C}\x{7740}\x{7747}\x{775A}\x{775B}\x{7761}\x{7763}\x{7765}\x{7766}' .
+'\x{7768}\x{776B}\x{7779}\x{777E}\x{777F}\x{778B}\x{778E}\x{7791}\x{779E}' .
+'\x{77A0}\x{77A5}\x{77AC}\x{77AD}\x{77B0}\x{77B3}\x{77B6}\x{77B9}\x{77BB}' .
+'\x{77BC}\x{77BD}\x{77BF}\x{77C7}\x{77CD}\x{77D7}\x{77DA}\x{77DB}\x{77DC}' .
+'\x{77E2}\x{77E3}\x{77E5}\x{77E7}\x{77E9}\x{77ED}\x{77EE}\x{77EF}\x{77F3}' .
+'\x{77FC}\x{7802}\x{780C}\x{7812}\x{7814}\x{7815}\x{7820}\x{7825}\x{7826}' .
+'\x{7827}\x{7832}\x{7834}\x{783A}\x{783F}\x{7845}\x{785D}\x{786B}\x{786C}' .
+'\x{786F}\x{7872}\x{7874}\x{787C}\x{7881}\x{7886}\x{7887}\x{788C}\x{788D}' .
+'\x{788E}\x{7891}\x{7893}\x{7895}\x{7897}\x{789A}\x{78A3}\x{78A7}\x{78A9}' .
+'\x{78AA}\x{78AF}\x{78B5}\x{78BA}\x{78BC}\x{78BE}\x{78C1}\x{78C5}\x{78C6}' .
+'\x{78CA}\x{78CB}\x{78D0}\x{78D1}\x{78D4}\x{78DA}\x{78E7}\x{78E8}\x{78EC}' .
+'\x{78EF}\x{78F4}\x{78FD}\x{7901}\x{7907}\x{790E}\x{7911}\x{7912}\x{7919}' .
+'\x{7926}\x{792A}\x{792B}\x{792C}\x{793A}\x{793C}\x{793E}\x{7940}\x{7941}' .
+'\x{7947}\x{7948}\x{7949}\x{7950}\x{7953}\x{7955}\x{7956}\x{7957}\x{795A}' .
+'\x{795D}\x{795E}\x{795F}\x{7960}\x{7962}\x{7965}\x{7968}\x{796D}\x{7977}' .
+'\x{797A}\x{797F}\x{7980}\x{7981}\x{7984}\x{7985}\x{798A}\x{798D}\x{798E}' .
+'\x{798F}\x{799D}\x{79A6}\x{79A7}\x{79AA}\x{79AE}\x{79B0}\x{79B3}\x{79B9}' .
+'\x{79BA}\x{79BD}\x{79BE}\x{79BF}\x{79C0}\x{79C1}\x{79C9}\x{79CB}\x{79D1}' .
+'\x{79D2}\x{79D5}\x{79D8}\x{79DF}\x{79E1}\x{79E3}\x{79E4}\x{79E6}\x{79E7}' .
+'\x{79E9}\x{79EC}\x{79F0}\x{79FB}\x{7A00}\x{7A08}\x{7A0B}\x{7A0D}\x{7A0E}' .
+'\x{7A14}\x{7A17}\x{7A18}\x{7A19}\x{7A1A}\x{7A1C}\x{7A1F}\x{7A20}\x{7A2E}' .
+'\x{7A31}\x{7A32}\x{7A37}\x{7A3B}\x{7A3C}\x{7A3D}\x{7A3E}\x{7A3F}\x{7A40}' .
+'\x{7A42}\x{7A43}\x{7A46}\x{7A49}\x{7A4D}\x{7A4E}\x{7A4F}\x{7A50}\x{7A57}' .
+'\x{7A61}\x{7A62}\x{7A63}\x{7A69}\x{7A6B}\x{7A70}\x{7A74}\x{7A76}\x{7A79}' .
+'\x{7A7A}\x{7A7D}\x{7A7F}\x{7A81}\x{7A83}\x{7A84}\x{7A88}\x{7A92}\x{7A93}' .
+'\x{7A95}\x{7A96}\x{7A97}\x{7A98}\x{7A9F}\x{7AA9}\x{7AAA}\x{7AAE}\x{7AAF}' .
+'\x{7AB0}\x{7AB6}\x{7ABA}\x{7ABF}\x{7AC3}\x{7AC4}\x{7AC5}\x{7AC7}\x{7AC8}' .
+'\x{7ACA}\x{7ACB}\x{7ACD}\x{7ACF}\x{7AD2}\x{7AD3}\x{7AD5}\x{7AD9}\x{7ADA}' .
+'\x{7ADC}\x{7ADD}\x{7ADF}\x{7AE0}\x{7AE1}\x{7AE2}\x{7AE3}\x{7AE5}\x{7AE6}' .
+'\x{7AEA}\x{7AED}\x{7AEF}\x{7AF0}\x{7AF6}\x{7AF8}\x{7AF9}\x{7AFA}\x{7AFF}' .
+'\x{7B02}\x{7B04}\x{7B06}\x{7B08}\x{7B0A}\x{7B0B}\x{7B0F}\x{7B11}\x{7B18}' .
+'\x{7B19}\x{7B1B}\x{7B1E}\x{7B20}\x{7B25}\x{7B26}\x{7B28}\x{7B2C}\x{7B33}' .
+'\x{7B35}\x{7B36}\x{7B39}\x{7B45}\x{7B46}\x{7B48}\x{7B49}\x{7B4B}\x{7B4C}' .
+'\x{7B4D}\x{7B4F}\x{7B50}\x{7B51}\x{7B52}\x{7B54}\x{7B56}\x{7B5D}\x{7B65}' .
+'\x{7B67}\x{7B6C}\x{7B6E}\x{7B70}\x{7B71}\x{7B74}\x{7B75}\x{7B7A}\x{7B86}' .
+'\x{7B87}\x{7B8B}\x{7B8D}\x{7B8F}\x{7B92}\x{7B94}\x{7B95}\x{7B97}\x{7B98}' .
+'\x{7B99}\x{7B9A}\x{7B9C}\x{7B9D}\x{7B9F}\x{7BA1}\x{7BAA}\x{7BAD}\x{7BB1}' .
+'\x{7BB4}\x{7BB8}\x{7BC0}\x{7BC1}\x{7BC4}\x{7BC6}\x{7BC7}\x{7BC9}\x{7BCB}' .
+'\x{7BCC}\x{7BCF}\x{7BDD}\x{7BE0}\x{7BE4}\x{7BE5}\x{7BE6}\x{7BE9}\x{7BED}' .
+'\x{7BF3}\x{7BF6}\x{7BF7}\x{7C00}\x{7C07}\x{7C0D}\x{7C11}\x{7C12}\x{7C13}' .
+'\x{7C14}\x{7C17}\x{7C1F}\x{7C21}\x{7C23}\x{7C27}\x{7C2A}\x{7C2B}\x{7C37}' .
+'\x{7C38}\x{7C3D}\x{7C3E}\x{7C3F}\x{7C40}\x{7C43}\x{7C4C}\x{7C4D}\x{7C4F}' .
+'\x{7C50}\x{7C54}\x{7C56}\x{7C58}\x{7C5F}\x{7C60}\x{7C64}\x{7C65}\x{7C6C}' .
+'\x{7C73}\x{7C75}\x{7C7E}\x{7C81}\x{7C82}\x{7C83}\x{7C89}\x{7C8B}\x{7C8D}' .
+'\x{7C90}\x{7C92}\x{7C95}\x{7C97}\x{7C98}\x{7C9B}\x{7C9F}\x{7CA1}\x{7CA2}' .
+'\x{7CA4}\x{7CA5}\x{7CA7}\x{7CA8}\x{7CAB}\x{7CAD}\x{7CAE}\x{7CB1}\x{7CB2}' .
+'\x{7CB3}\x{7CB9}\x{7CBD}\x{7CBE}\x{7CC0}\x{7CC2}\x{7CC5}\x{7CCA}\x{7CCE}' .
+'\x{7CD2}\x{7CD6}\x{7CD8}\x{7CDC}\x{7CDE}\x{7CDF}\x{7CE0}\x{7CE2}\x{7CE7}' .
+'\x{7CEF}\x{7CF2}\x{7CF4}\x{7CF6}\x{7CF8}\x{7CFA}\x{7CFB}\x{7CFE}\x{7D00}' .
+'\x{7D02}\x{7D04}\x{7D05}\x{7D06}\x{7D0A}\x{7D0B}\x{7D0D}\x{7D10}\x{7D14}' .
+'\x{7D15}\x{7D17}\x{7D18}\x{7D19}\x{7D1A}\x{7D1B}\x{7D1C}\x{7D20}\x{7D21}' .
+'\x{7D22}\x{7D2B}\x{7D2C}\x{7D2E}\x{7D2F}\x{7D30}\x{7D32}\x{7D33}\x{7D35}' .
+'\x{7D39}\x{7D3A}\x{7D3F}\x{7D42}\x{7D43}\x{7D44}\x{7D45}\x{7D46}\x{7D4B}' .
+'\x{7D4C}\x{7D4E}\x{7D4F}\x{7D50}\x{7D56}\x{7D5B}\x{7D5E}\x{7D61}\x{7D62}' .
+'\x{7D63}\x{7D66}\x{7D68}\x{7D6E}\x{7D71}\x{7D72}\x{7D73}\x{7D75}\x{7D76}' .
+'\x{7D79}\x{7D7D}\x{7D89}\x{7D8F}\x{7D93}\x{7D99}\x{7D9A}\x{7D9B}\x{7D9C}' .
+'\x{7D9F}\x{7DA2}\x{7DA3}\x{7DAB}\x{7DAC}\x{7DAD}\x{7DAE}\x{7DAF}\x{7DB0}' .
+'\x{7DB1}\x{7DB2}\x{7DB4}\x{7DB5}\x{7DB8}\x{7DBA}\x{7DBB}\x{7DBD}\x{7DBE}' .
+'\x{7DBF}\x{7DC7}\x{7DCA}\x{7DCB}\x{7DCF}\x{7DD1}\x{7DD2}\x{7DD5}\x{7DD8}' .
+'\x{7DDA}\x{7DDC}\x{7DDD}\x{7DDE}\x{7DE0}\x{7DE1}\x{7DE4}\x{7DE8}\x{7DE9}' .
+'\x{7DEC}\x{7DEF}\x{7DF2}\x{7DF4}\x{7DFB}\x{7E01}\x{7E04}\x{7E05}\x{7E09}' .
+'\x{7E0A}\x{7E0B}\x{7E12}\x{7E1B}\x{7E1E}\x{7E1F}\x{7E21}\x{7E22}\x{7E23}' .
+'\x{7E26}\x{7E2B}\x{7E2E}\x{7E31}\x{7E32}\x{7E35}\x{7E37}\x{7E39}\x{7E3A}' .
+'\x{7E3B}\x{7E3D}\x{7E3E}\x{7E41}\x{7E43}\x{7E46}\x{7E4A}\x{7E4B}\x{7E4D}' .
+'\x{7E54}\x{7E55}\x{7E56}\x{7E59}\x{7E5A}\x{7E5D}\x{7E5E}\x{7E66}\x{7E67}' .
+'\x{7E69}\x{7E6A}\x{7E6D}\x{7E70}\x{7E79}\x{7E7B}\x{7E7C}\x{7E7D}\x{7E7F}' .
+'\x{7E82}\x{7E83}\x{7E88}\x{7E89}\x{7E8C}\x{7E8E}\x{7E8F}\x{7E90}\x{7E92}' .
+'\x{7E93}\x{7E94}\x{7E96}\x{7E9B}\x{7E9C}\x{7F36}\x{7F38}\x{7F3A}\x{7F45}' .
+'\x{7F4C}\x{7F4D}\x{7F4E}\x{7F50}\x{7F51}\x{7F54}\x{7F55}\x{7F58}\x{7F5F}' .
+'\x{7F60}\x{7F67}\x{7F68}\x{7F69}\x{7F6A}\x{7F6B}\x{7F6E}\x{7F70}\x{7F72}' .
+'\x{7F75}\x{7F77}\x{7F78}\x{7F79}\x{7F82}\x{7F83}\x{7F85}\x{7F86}\x{7F87}' .
+'\x{7F88}\x{7F8A}\x{7F8C}\x{7F8E}\x{7F94}\x{7F9A}\x{7F9D}\x{7F9E}\x{7FA3}' .
+'\x{7FA4}\x{7FA8}\x{7FA9}\x{7FAE}\x{7FAF}\x{7FB2}\x{7FB6}\x{7FB8}\x{7FB9}' .
+'\x{7FBD}\x{7FC1}\x{7FC5}\x{7FC6}\x{7FCA}\x{7FCC}\x{7FD2}\x{7FD4}\x{7FD5}' .
+'\x{7FE0}\x{7FE1}\x{7FE6}\x{7FE9}\x{7FEB}\x{7FF0}\x{7FF3}\x{7FF9}\x{7FFB}' .
+'\x{7FFC}\x{8000}\x{8001}\x{8003}\x{8004}\x{8005}\x{8006}\x{800B}\x{800C}' .
+'\x{8010}\x{8012}\x{8015}\x{8017}\x{8018}\x{8019}\x{801C}\x{8021}\x{8028}' .
+'\x{8033}\x{8036}\x{803B}\x{803D}\x{803F}\x{8046}\x{804A}\x{8052}\x{8056}' .
+'\x{8058}\x{805A}\x{805E}\x{805F}\x{8061}\x{8062}\x{8068}\x{806F}\x{8070}' .
+'\x{8072}\x{8073}\x{8074}\x{8076}\x{8077}\x{8079}\x{807D}\x{807E}\x{807F}' .
+'\x{8084}\x{8085}\x{8086}\x{8087}\x{8089}\x{808B}\x{808C}\x{8093}\x{8096}' .
+'\x{8098}\x{809A}\x{809B}\x{809D}\x{80A1}\x{80A2}\x{80A5}\x{80A9}\x{80AA}' .
+'\x{80AC}\x{80AD}\x{80AF}\x{80B1}\x{80B2}\x{80B4}\x{80BA}\x{80C3}\x{80C4}' .
+'\x{80C6}\x{80CC}\x{80CE}\x{80D6}\x{80D9}\x{80DA}\x{80DB}\x{80DD}\x{80DE}' .
+'\x{80E1}\x{80E4}\x{80E5}\x{80EF}\x{80F1}\x{80F4}\x{80F8}\x{80FC}\x{80FD}' .
+'\x{8102}\x{8105}\x{8106}\x{8107}\x{8108}\x{8109}\x{810A}\x{811A}\x{811B}' .
+'\x{8123}\x{8129}\x{812F}\x{8131}\x{8133}\x{8139}\x{813E}\x{8146}\x{814B}' .
+'\x{814E}\x{8150}\x{8151}\x{8153}\x{8154}\x{8155}\x{815F}\x{8165}\x{8166}' .
+'\x{816B}\x{816E}\x{8170}\x{8171}\x{8174}\x{8178}\x{8179}\x{817A}\x{817F}' .
+'\x{8180}\x{8182}\x{8183}\x{8188}\x{818A}\x{818F}\x{8193}\x{8195}\x{819A}' .
+'\x{819C}\x{819D}\x{81A0}\x{81A3}\x{81A4}\x{81A8}\x{81A9}\x{81B0}\x{81B3}' .
+'\x{81B5}\x{81B8}\x{81BA}\x{81BD}\x{81BE}\x{81BF}\x{81C0}\x{81C2}\x{81C6}' .
+'\x{81C8}\x{81C9}\x{81CD}\x{81D1}\x{81D3}\x{81D8}\x{81D9}\x{81DA}\x{81DF}' .
+'\x{81E0}\x{81E3}\x{81E5}\x{81E7}\x{81E8}\x{81EA}\x{81ED}\x{81F3}\x{81F4}' .
+'\x{81FA}\x{81FB}\x{81FC}\x{81FE}\x{8201}\x{8202}\x{8205}\x{8207}\x{8208}' .
+'\x{8209}\x{820A}\x{820C}\x{820D}\x{820E}\x{8210}\x{8212}\x{8216}\x{8217}' .
+'\x{8218}\x{821B}\x{821C}\x{821E}\x{821F}\x{8229}\x{822A}\x{822B}\x{822C}' .
+'\x{822E}\x{8233}\x{8235}\x{8236}\x{8237}\x{8238}\x{8239}\x{8240}\x{8247}' .
+'\x{8258}\x{8259}\x{825A}\x{825D}\x{825F}\x{8262}\x{8264}\x{8266}\x{8268}' .
+'\x{826A}\x{826B}\x{826E}\x{826F}\x{8271}\x{8272}\x{8276}\x{8277}\x{8278}' .
+'\x{827E}\x{828B}\x{828D}\x{8292}\x{8299}\x{829D}\x{829F}\x{82A5}\x{82A6}' .
+'\x{82AB}\x{82AC}\x{82AD}\x{82AF}\x{82B1}\x{82B3}\x{82B8}\x{82B9}\x{82BB}' .
+'\x{82BD}\x{82C5}\x{82D1}\x{82D2}\x{82D3}\x{82D4}\x{82D7}\x{82D9}\x{82DB}' .
+'\x{82DC}\x{82DE}\x{82DF}\x{82E1}\x{82E3}\x{82E5}\x{82E6}\x{82E7}\x{82EB}' .
+'\x{82F1}\x{82F3}\x{82F4}\x{82F9}\x{82FA}\x{82FB}\x{8302}\x{8303}\x{8304}' .
+'\x{8305}\x{8306}\x{8309}\x{830E}\x{8316}\x{8317}\x{8318}\x{831C}\x{8323}' .
+'\x{8328}\x{832B}\x{832F}\x{8331}\x{8332}\x{8334}\x{8335}\x{8336}\x{8338}' .
+'\x{8339}\x{8340}\x{8345}\x{8349}\x{834A}\x{834F}\x{8350}\x{8352}\x{8358}' .
+'\x{8373}\x{8375}\x{8377}\x{837B}\x{837C}\x{8385}\x{8387}\x{8389}\x{838A}' .
+'\x{838E}\x{8393}\x{8396}\x{839A}\x{839E}\x{839F}\x{83A0}\x{83A2}\x{83A8}' .
+'\x{83AA}\x{83AB}\x{83B1}\x{83B5}\x{83BD}\x{83C1}\x{83C5}\x{83CA}\x{83CC}' .
+'\x{83CE}\x{83D3}\x{83D6}\x{83D8}\x{83DC}\x{83DF}\x{83E0}\x{83E9}\x{83EB}' .
+'\x{83EF}\x{83F0}\x{83F1}\x{83F2}\x{83F4}\x{83F7}\x{83FB}\x{83FD}\x{8403}' .
+'\x{8404}\x{8407}\x{840B}\x{840C}\x{840D}\x{840E}\x{8413}\x{8420}\x{8422}' .
+'\x{8429}\x{842A}\x{842C}\x{8431}\x{8435}\x{8438}\x{843C}\x{843D}\x{8446}' .
+'\x{8449}\x{844E}\x{8457}\x{845B}\x{8461}\x{8462}\x{8463}\x{8466}\x{8469}' .
+'\x{846B}\x{846C}\x{846D}\x{846E}\x{846F}\x{8471}\x{8475}\x{8477}\x{8479}' .
+'\x{847A}\x{8482}\x{8484}\x{848B}\x{8490}\x{8494}\x{8499}\x{849C}\x{849F}' .
+'\x{84A1}\x{84AD}\x{84B2}\x{84B8}\x{84B9}\x{84BB}\x{84BC}\x{84BF}\x{84C1}' .
+'\x{84C4}\x{84C6}\x{84C9}\x{84CA}\x{84CB}\x{84CD}\x{84D0}\x{84D1}\x{84D6}' .
+'\x{84D9}\x{84DA}\x{84EC}\x{84EE}\x{84F4}\x{84FC}\x{84FF}\x{8500}\x{8506}' .
+'\x{8511}\x{8513}\x{8514}\x{8515}\x{8517}\x{8518}\x{851A}\x{851F}\x{8521}' .
+'\x{8526}\x{852C}\x{852D}\x{8535}\x{853D}\x{8540}\x{8541}\x{8543}\x{8548}' .
+'\x{8549}\x{854A}\x{854B}\x{854E}\x{8555}\x{8557}\x{8558}\x{855A}\x{8563}' .
+'\x{8568}\x{8569}\x{856A}\x{856D}\x{8577}\x{857E}\x{8580}\x{8584}\x{8587}' .
+'\x{8588}\x{858A}\x{8590}\x{8591}\x{8594}\x{8597}\x{8599}\x{859B}\x{859C}' .
+'\x{85A4}\x{85A6}\x{85A8}\x{85A9}\x{85AA}\x{85AB}\x{85AC}\x{85AE}\x{85AF}' .
+'\x{85B9}\x{85BA}\x{85C1}\x{85C9}\x{85CD}\x{85CF}\x{85D0}\x{85D5}\x{85DC}' .
+'\x{85DD}\x{85E4}\x{85E5}\x{85E9}\x{85EA}\x{85F7}\x{85F9}\x{85FA}\x{85FB}' .
+'\x{85FE}\x{8602}\x{8606}\x{8607}\x{860A}\x{860B}\x{8613}\x{8616}\x{8617}' .
+'\x{861A}\x{8622}\x{862D}\x{862F}\x{8630}\x{863F}\x{864D}\x{864E}\x{8650}' .
+'\x{8654}\x{8655}\x{865A}\x{865C}\x{865E}\x{865F}\x{8667}\x{866B}\x{8671}' .
+'\x{8679}\x{867B}\x{868A}\x{868B}\x{868C}\x{8693}\x{8695}\x{86A3}\x{86A4}' .
+'\x{86A9}\x{86AA}\x{86AB}\x{86AF}\x{86B0}\x{86B6}\x{86C4}\x{86C6}\x{86C7}' .
+'\x{86C9}\x{86CB}\x{86CD}\x{86CE}\x{86D4}\x{86D9}\x{86DB}\x{86DE}\x{86DF}' .
+'\x{86E4}\x{86E9}\x{86EC}\x{86ED}\x{86EE}\x{86EF}\x{86F8}\x{86F9}\x{86FB}' .
+'\x{86FE}\x{8700}\x{8702}\x{8703}\x{8706}\x{8708}\x{8709}\x{870A}\x{870D}' .
+'\x{8711}\x{8712}\x{8718}\x{871A}\x{871C}\x{8725}\x{8729}\x{8734}\x{8737}' .
+'\x{873B}\x{873F}\x{8749}\x{874B}\x{874C}\x{874E}\x{8753}\x{8755}\x{8757}' .
+'\x{8759}\x{875F}\x{8760}\x{8763}\x{8766}\x{8768}\x{876A}\x{876E}\x{8774}' .
+'\x{8776}\x{8778}\x{877F}\x{8782}\x{878D}\x{879F}\x{87A2}\x{87AB}\x{87AF}' .
+'\x{87B3}\x{87BA}\x{87BB}\x{87BD}\x{87C0}\x{87C4}\x{87C6}\x{87C7}\x{87CB}' .
+'\x{87D0}\x{87D2}\x{87E0}\x{87EF}\x{87F2}\x{87F6}\x{87F7}\x{87F9}\x{87FB}' .
+'\x{87FE}\x{8805}\x{880D}\x{880E}\x{880F}\x{8811}\x{8815}\x{8816}\x{8821}' .
+'\x{8822}\x{8823}\x{8827}\x{8831}\x{8836}\x{8839}\x{883B}\x{8840}\x{8842}' .
+'\x{8844}\x{8846}\x{884C}\x{884D}\x{8852}\x{8853}\x{8857}\x{8859}\x{885B}' .
+'\x{885D}\x{885E}\x{8861}\x{8862}\x{8863}\x{8868}\x{886B}\x{8870}\x{8872}' .
+'\x{8875}\x{8877}\x{887D}\x{887E}\x{887F}\x{8881}\x{8882}\x{8888}\x{888B}' .
+'\x{888D}\x{8892}\x{8896}\x{8897}\x{8899}\x{889E}\x{88A2}\x{88A4}\x{88AB}' .
+'\x{88AE}\x{88B0}\x{88B1}\x{88B4}\x{88B5}\x{88B7}\x{88BF}\x{88C1}\x{88C2}' .
+'\x{88C3}\x{88C4}\x{88C5}\x{88CF}\x{88D4}\x{88D5}\x{88D8}\x{88D9}\x{88DC}' .
+'\x{88DD}\x{88DF}\x{88E1}\x{88E8}\x{88F2}\x{88F3}\x{88F4}\x{88F8}\x{88F9}' .
+'\x{88FC}\x{88FD}\x{88FE}\x{8902}\x{8904}\x{8907}\x{890A}\x{890C}\x{8910}' .
+'\x{8912}\x{8913}\x{891D}\x{891E}\x{8925}\x{892A}\x{892B}\x{8936}\x{8938}' .
+'\x{893B}\x{8941}\x{8943}\x{8944}\x{894C}\x{894D}\x{8956}\x{895E}\x{895F}' .
+'\x{8960}\x{8964}\x{8966}\x{896A}\x{896D}\x{896F}\x{8972}\x{8974}\x{8977}' .
+'\x{897E}\x{897F}\x{8981}\x{8983}\x{8986}\x{8987}\x{8988}\x{898A}\x{898B}' .
+'\x{898F}\x{8993}\x{8996}\x{8997}\x{8998}\x{899A}\x{89A1}\x{89A6}\x{89A7}' .
+'\x{89A9}\x{89AA}\x{89AC}\x{89AF}\x{89B2}\x{89B3}\x{89BA}\x{89BD}\x{89BF}' .
+'\x{89C0}\x{89D2}\x{89DA}\x{89DC}\x{89DD}\x{89E3}\x{89E6}\x{89E7}\x{89F4}' .
+'\x{89F8}\x{8A00}\x{8A02}\x{8A03}\x{8A08}\x{8A0A}\x{8A0C}\x{8A0E}\x{8A10}' .
+'\x{8A13}\x{8A16}\x{8A17}\x{8A18}\x{8A1B}\x{8A1D}\x{8A1F}\x{8A23}\x{8A25}' .
+'\x{8A2A}\x{8A2D}\x{8A31}\x{8A33}\x{8A34}\x{8A36}\x{8A3A}\x{8A3B}\x{8A3C}' .
+'\x{8A41}\x{8A46}\x{8A48}\x{8A50}\x{8A51}\x{8A52}\x{8A54}\x{8A55}\x{8A5B}' .
+'\x{8A5E}\x{8A60}\x{8A62}\x{8A63}\x{8A66}\x{8A69}\x{8A6B}\x{8A6C}\x{8A6D}' .
+'\x{8A6E}\x{8A70}\x{8A71}\x{8A72}\x{8A73}\x{8A7C}\x{8A82}\x{8A84}\x{8A85}' .
+'\x{8A87}\x{8A89}\x{8A8C}\x{8A8D}\x{8A91}\x{8A93}\x{8A95}\x{8A98}\x{8A9A}' .
+'\x{8A9E}\x{8AA0}\x{8AA1}\x{8AA3}\x{8AA4}\x{8AA5}\x{8AA6}\x{8AA8}\x{8AAC}' .
+'\x{8AAD}\x{8AB0}\x{8AB2}\x{8AB9}\x{8ABC}\x{8ABF}\x{8AC2}\x{8AC4}\x{8AC7}' .
+'\x{8ACB}\x{8ACC}\x{8ACD}\x{8ACF}\x{8AD2}\x{8AD6}\x{8ADA}\x{8ADB}\x{8ADC}' .
+'\x{8ADE}\x{8AE0}\x{8AE1}\x{8AE2}\x{8AE4}\x{8AE6}\x{8AE7}\x{8AEB}\x{8AED}' .
+'\x{8AEE}\x{8AF1}\x{8AF3}\x{8AF7}\x{8AF8}\x{8AFA}\x{8AFE}\x{8B00}\x{8B01}' .
+'\x{8B02}\x{8B04}\x{8B07}\x{8B0C}\x{8B0E}\x{8B10}\x{8B14}\x{8B16}\x{8B17}' .
+'\x{8B19}\x{8B1A}\x{8B1B}\x{8B1D}\x{8B20}\x{8B21}\x{8B26}\x{8B28}\x{8B2B}' .
+'\x{8B2C}\x{8B33}\x{8B39}\x{8B3E}\x{8B41}\x{8B49}\x{8B4C}\x{8B4E}\x{8B4F}' .
+'\x{8B56}\x{8B58}\x{8B5A}\x{8B5B}\x{8B5C}\x{8B5F}\x{8B66}\x{8B6B}\x{8B6C}' .
+'\x{8B6F}\x{8B70}\x{8B71}\x{8B72}\x{8B74}\x{8B77}\x{8B7D}\x{8B80}\x{8B83}' .
+'\x{8B8A}\x{8B8C}\x{8B8E}\x{8B90}\x{8B92}\x{8B93}\x{8B96}\x{8B99}\x{8B9A}' .
+'\x{8C37}\x{8C3A}\x{8C3F}\x{8C41}\x{8C46}\x{8C48}\x{8C4A}\x{8C4C}\x{8C4E}' .
+'\x{8C50}\x{8C55}\x{8C5A}\x{8C61}\x{8C62}\x{8C6A}\x{8C6B}\x{8C6C}\x{8C78}' .
+'\x{8C79}\x{8C7A}\x{8C7C}\x{8C82}\x{8C85}\x{8C89}\x{8C8A}\x{8C8C}\x{8C8D}' .
+'\x{8C8E}\x{8C94}\x{8C98}\x{8C9D}\x{8C9E}\x{8CA0}\x{8CA1}\x{8CA2}\x{8CA7}' .
+'\x{8CA8}\x{8CA9}\x{8CAA}\x{8CAB}\x{8CAC}\x{8CAD}\x{8CAE}\x{8CAF}\x{8CB0}' .
+'\x{8CB2}\x{8CB3}\x{8CB4}\x{8CB6}\x{8CB7}\x{8CB8}\x{8CBB}\x{8CBC}\x{8CBD}' .
+'\x{8CBF}\x{8CC0}\x{8CC1}\x{8CC2}\x{8CC3}\x{8CC4}\x{8CC7}\x{8CC8}\x{8CCA}' .
+'\x{8CCD}\x{8CCE}\x{8CD1}\x{8CD3}\x{8CDA}\x{8CDB}\x{8CDC}\x{8CDE}\x{8CE0}' .
+'\x{8CE2}\x{8CE3}\x{8CE4}\x{8CE6}\x{8CEA}\x{8CED}\x{8CFA}\x{8CFB}\x{8CFC}' .
+'\x{8CFD}\x{8D04}\x{8D05}\x{8D07}\x{8D08}\x{8D0A}\x{8D0B}\x{8D0D}\x{8D0F}' .
+'\x{8D10}\x{8D13}\x{8D14}\x{8D16}\x{8D64}\x{8D66}\x{8D67}\x{8D6B}\x{8D6D}' .
+'\x{8D70}\x{8D71}\x{8D73}\x{8D74}\x{8D77}\x{8D81}\x{8D85}\x{8D8A}\x{8D99}' .
+'\x{8DA3}\x{8DA8}\x{8DB3}\x{8DBA}\x{8DBE}\x{8DC2}\x{8DCB}\x{8DCC}\x{8DCF}' .
+'\x{8DD6}\x{8DDA}\x{8DDB}\x{8DDD}\x{8DDF}\x{8DE1}\x{8DE3}\x{8DE8}\x{8DEA}' .
+'\x{8DEB}\x{8DEF}\x{8DF3}\x{8DF5}\x{8DFC}\x{8DFF}\x{8E08}\x{8E09}\x{8E0A}' .
+'\x{8E0F}\x{8E10}\x{8E1D}\x{8E1E}\x{8E1F}\x{8E2A}\x{8E30}\x{8E34}\x{8E35}' .
+'\x{8E42}\x{8E44}\x{8E47}\x{8E48}\x{8E49}\x{8E4A}\x{8E4C}\x{8E50}\x{8E55}' .
+'\x{8E59}\x{8E5F}\x{8E60}\x{8E63}\x{8E64}\x{8E72}\x{8E74}\x{8E76}\x{8E7C}' .
+'\x{8E81}\x{8E84}\x{8E85}\x{8E87}\x{8E8A}\x{8E8B}\x{8E8D}\x{8E91}\x{8E93}' .
+'\x{8E94}\x{8E99}\x{8EA1}\x{8EAA}\x{8EAB}\x{8EAC}\x{8EAF}\x{8EB0}\x{8EB1}' .
+'\x{8EBE}\x{8EC5}\x{8EC6}\x{8EC8}\x{8ECA}\x{8ECB}\x{8ECC}\x{8ECD}\x{8ED2}' .
+'\x{8EDB}\x{8EDF}\x{8EE2}\x{8EE3}\x{8EEB}\x{8EF8}\x{8EFB}\x{8EFC}\x{8EFD}' .
+'\x{8EFE}\x{8F03}\x{8F05}\x{8F09}\x{8F0A}\x{8F0C}\x{8F12}\x{8F13}\x{8F14}' .
+'\x{8F15}\x{8F19}\x{8F1B}\x{8F1C}\x{8F1D}\x{8F1F}\x{8F26}\x{8F29}\x{8F2A}' .
+'\x{8F2F}\x{8F33}\x{8F38}\x{8F39}\x{8F3B}\x{8F3E}\x{8F3F}\x{8F42}\x{8F44}' .
+'\x{8F45}\x{8F46}\x{8F49}\x{8F4C}\x{8F4D}\x{8F4E}\x{8F57}\x{8F5C}\x{8F5F}' .
+'\x{8F61}\x{8F62}\x{8F63}\x{8F64}\x{8F9B}\x{8F9C}\x{8F9E}\x{8F9F}\x{8FA3}' .
+'\x{8FA7}\x{8FA8}\x{8FAD}\x{8FAE}\x{8FAF}\x{8FB0}\x{8FB1}\x{8FB2}\x{8FB7}' .
+'\x{8FBA}\x{8FBB}\x{8FBC}\x{8FBF}\x{8FC2}\x{8FC4}\x{8FC5}\x{8FCE}\x{8FD1}' .
+'\x{8FD4}\x{8FDA}\x{8FE2}\x{8FE5}\x{8FE6}\x{8FE9}\x{8FEA}\x{8FEB}\x{8FED}' .
+'\x{8FEF}\x{8FF0}\x{8FF4}\x{8FF7}\x{8FF8}\x{8FF9}\x{8FFA}\x{8FFD}\x{9000}' .
+'\x{9001}\x{9003}\x{9005}\x{9006}\x{900B}\x{900D}\x{900E}\x{900F}\x{9010}' .
+'\x{9011}\x{9013}\x{9014}\x{9015}\x{9016}\x{9017}\x{9019}\x{901A}\x{901D}' .
+'\x{901E}\x{901F}\x{9020}\x{9021}\x{9022}\x{9023}\x{9027}\x{902E}\x{9031}' .
+'\x{9032}\x{9035}\x{9036}\x{9038}\x{9039}\x{903C}\x{903E}\x{9041}\x{9042}' .
+'\x{9045}\x{9047}\x{9049}\x{904A}\x{904B}\x{904D}\x{904E}\x{904F}\x{9050}' .
+'\x{9051}\x{9052}\x{9053}\x{9054}\x{9055}\x{9056}\x{9058}\x{9059}\x{905C}' .
+'\x{905E}\x{9060}\x{9061}\x{9063}\x{9065}\x{9068}\x{9069}\x{906D}\x{906E}' .
+'\x{906F}\x{9072}\x{9075}\x{9076}\x{9077}\x{9078}\x{907A}\x{907C}\x{907D}' .
+'\x{907F}\x{9080}\x{9081}\x{9082}\x{9083}\x{9084}\x{9087}\x{9089}\x{908A}' .
+'\x{908F}\x{9091}\x{90A3}\x{90A6}\x{90A8}\x{90AA}\x{90AF}\x{90B1}\x{90B5}' .
+'\x{90B8}\x{90C1}\x{90CA}\x{90CE}\x{90DB}\x{90E1}\x{90E2}\x{90E4}\x{90E8}' .
+'\x{90ED}\x{90F5}\x{90F7}\x{90FD}\x{9102}\x{9112}\x{9119}\x{912D}\x{9130}' .
+'\x{9132}\x{9149}\x{914A}\x{914B}\x{914C}\x{914D}\x{914E}\x{9152}\x{9154}' .
+'\x{9156}\x{9158}\x{9162}\x{9163}\x{9165}\x{9169}\x{916A}\x{916C}\x{9172}' .
+'\x{9173}\x{9175}\x{9177}\x{9178}\x{9182}\x{9187}\x{9189}\x{918B}\x{918D}' .
+'\x{9190}\x{9192}\x{9197}\x{919C}\x{91A2}\x{91A4}\x{91AA}\x{91AB}\x{91AF}' .
+'\x{91B4}\x{91B5}\x{91B8}\x{91BA}\x{91C0}\x{91C1}\x{91C6}\x{91C7}\x{91C8}' .
+'\x{91C9}\x{91CB}\x{91CC}\x{91CD}\x{91CE}\x{91CF}\x{91D0}\x{91D1}\x{91D6}' .
+'\x{91D8}\x{91DB}\x{91DC}\x{91DD}\x{91DF}\x{91E1}\x{91E3}\x{91E6}\x{91E7}' .
+'\x{91F5}\x{91F6}\x{91FC}\x{91FF}\x{920D}\x{920E}\x{9211}\x{9214}\x{9215}' .
+'\x{921E}\x{9229}\x{922C}\x{9234}\x{9237}\x{923F}\x{9244}\x{9245}\x{9248}' .
+'\x{9249}\x{924B}\x{9250}\x{9257}\x{925A}\x{925B}\x{925E}\x{9262}\x{9264}' .
+'\x{9266}\x{9271}\x{927E}\x{9280}\x{9283}\x{9285}\x{9291}\x{9293}\x{9295}' .
+'\x{9296}\x{9298}\x{929A}\x{929B}\x{929C}\x{92AD}\x{92B7}\x{92B9}\x{92CF}' .
+'\x{92D2}\x{92E4}\x{92E9}\x{92EA}\x{92ED}\x{92F2}\x{92F3}\x{92F8}\x{92FA}' .
+'\x{92FC}\x{9306}\x{930F}\x{9310}\x{9318}\x{9319}\x{931A}\x{9320}\x{9322}' .
+'\x{9323}\x{9326}\x{9328}\x{932B}\x{932C}\x{932E}\x{932F}\x{9332}\x{9335}' .
+'\x{933A}\x{933B}\x{9344}\x{934B}\x{934D}\x{9354}\x{9356}\x{935B}\x{935C}' .
+'\x{9360}\x{936C}\x{936E}\x{9375}\x{937C}\x{937E}\x{938C}\x{9394}\x{9396}' .
+'\x{9397}\x{939A}\x{93A7}\x{93AC}\x{93AD}\x{93AE}\x{93B0}\x{93B9}\x{93C3}' .
+'\x{93C8}\x{93D0}\x{93D1}\x{93D6}\x{93D7}\x{93D8}\x{93DD}\x{93E1}\x{93E4}' .
+'\x{93E5}\x{93E8}\x{9403}\x{9407}\x{9410}\x{9413}\x{9414}\x{9418}\x{9419}' .
+'\x{941A}\x{9421}\x{942B}\x{9435}\x{9436}\x{9438}\x{943A}\x{9441}\x{9444}' .
+'\x{9451}\x{9452}\x{9453}\x{945A}\x{945B}\x{945E}\x{9460}\x{9462}\x{946A}' .
+'\x{9470}\x{9475}\x{9477}\x{947C}\x{947D}\x{947E}\x{947F}\x{9481}\x{9577}' .
+'\x{9580}\x{9582}\x{9583}\x{9587}\x{9589}\x{958A}\x{958B}\x{958F}\x{9591}' .
+'\x{9593}\x{9594}\x{9596}\x{9598}\x{9599}\x{95A0}\x{95A2}\x{95A3}\x{95A4}' .
+'\x{95A5}\x{95A7}\x{95A8}\x{95AD}\x{95B2}\x{95B9}\x{95BB}\x{95BC}\x{95BE}' .
+'\x{95C3}\x{95C7}\x{95CA}\x{95CC}\x{95CD}\x{95D4}\x{95D5}\x{95D6}\x{95D8}' .
+'\x{95DC}\x{95E1}\x{95E2}\x{95E5}\x{961C}\x{9621}\x{9628}\x{962A}\x{962E}' .
+'\x{962F}\x{9632}\x{963B}\x{963F}\x{9640}\x{9642}\x{9644}\x{964B}\x{964C}' .
+'\x{964D}\x{964F}\x{9650}\x{965B}\x{965C}\x{965D}\x{965E}\x{965F}\x{9662}' .
+'\x{9663}\x{9664}\x{9665}\x{9666}\x{966A}\x{966C}\x{9670}\x{9672}\x{9673}' .
+'\x{9675}\x{9676}\x{9677}\x{9678}\x{967A}\x{967D}\x{9685}\x{9686}\x{9688}' .
+'\x{968A}\x{968B}\x{968D}\x{968E}\x{968F}\x{9694}\x{9695}\x{9697}\x{9698}' .
+'\x{9699}\x{969B}\x{969C}\x{96A0}\x{96A3}\x{96A7}\x{96A8}\x{96AA}\x{96B0}' .
+'\x{96B1}\x{96B2}\x{96B4}\x{96B6}\x{96B7}\x{96B8}\x{96B9}\x{96BB}\x{96BC}' .
+'\x{96C0}\x{96C1}\x{96C4}\x{96C5}\x{96C6}\x{96C7}\x{96C9}\x{96CB}\x{96CC}' .
+'\x{96CD}\x{96CE}\x{96D1}\x{96D5}\x{96D6}\x{96D9}\x{96DB}\x{96DC}\x{96E2}' .
+'\x{96E3}\x{96E8}\x{96EA}\x{96EB}\x{96F0}\x{96F2}\x{96F6}\x{96F7}\x{96F9}' .
+'\x{96FB}\x{9700}\x{9704}\x{9706}\x{9707}\x{9708}\x{970A}\x{970D}\x{970E}' .
+'\x{970F}\x{9711}\x{9713}\x{9716}\x{9719}\x{971C}\x{971E}\x{9724}\x{9727}' .
+'\x{972A}\x{9730}\x{9732}\x{9738}\x{9739}\x{973D}\x{973E}\x{9742}\x{9744}' .
+'\x{9746}\x{9748}\x{9749}\x{9752}\x{9756}\x{9759}\x{975C}\x{975E}\x{9760}' .
+'\x{9761}\x{9762}\x{9764}\x{9766}\x{9768}\x{9769}\x{976B}\x{976D}\x{9771}' .
+'\x{9774}\x{9779}\x{977A}\x{977C}\x{9781}\x{9784}\x{9785}\x{9786}\x{978B}' .
+'\x{978D}\x{978F}\x{9790}\x{9798}\x{979C}\x{97A0}\x{97A3}\x{97A6}\x{97A8}' .
+'\x{97AB}\x{97AD}\x{97B3}\x{97B4}\x{97C3}\x{97C6}\x{97C8}\x{97CB}\x{97D3}' .
+'\x{97DC}\x{97ED}\x{97EE}\x{97F2}\x{97F3}\x{97F5}\x{97F6}\x{97FB}\x{97FF}' .
+'\x{9801}\x{9802}\x{9803}\x{9805}\x{9806}\x{9808}\x{980C}\x{980F}\x{9810}' .
+'\x{9811}\x{9812}\x{9813}\x{9817}\x{9818}\x{981A}\x{9821}\x{9824}\x{982C}' .
+'\x{982D}\x{9834}\x{9837}\x{9838}\x{983B}\x{983C}\x{983D}\x{9846}\x{984B}' .
+'\x{984C}\x{984D}\x{984E}\x{984F}\x{9854}\x{9855}\x{9858}\x{985B}\x{985E}' .
+'\x{9867}\x{986B}\x{986F}\x{9870}\x{9871}\x{9873}\x{9874}\x{98A8}\x{98AA}' .
+'\x{98AF}\x{98B1}\x{98B6}\x{98C3}\x{98C4}\x{98C6}\x{98DB}\x{98DC}\x{98DF}' .
+'\x{98E2}\x{98E9}\x{98EB}\x{98ED}\x{98EE}\x{98EF}\x{98F2}\x{98F4}\x{98FC}' .
+'\x{98FD}\x{98FE}\x{9903}\x{9905}\x{9909}\x{990A}\x{990C}\x{9910}\x{9912}' .
+'\x{9913}\x{9914}\x{9918}\x{991D}\x{991E}\x{9920}\x{9921}\x{9924}\x{9928}' .
+'\x{992C}\x{992E}\x{993D}\x{993E}\x{9942}\x{9945}\x{9949}\x{994B}\x{994C}' .
+'\x{9950}\x{9951}\x{9952}\x{9955}\x{9957}\x{9996}\x{9997}\x{9998}\x{9999}' .
+'\x{99A5}\x{99A8}\x{99AC}\x{99AD}\x{99AE}\x{99B3}\x{99B4}\x{99BC}\x{99C1}' .
+'\x{99C4}\x{99C5}\x{99C6}\x{99C8}\x{99D0}\x{99D1}\x{99D2}\x{99D5}\x{99D8}' .
+'\x{99DB}\x{99DD}\x{99DF}\x{99E2}\x{99ED}\x{99EE}\x{99F1}\x{99F2}\x{99F8}' .
+'\x{99FB}\x{99FF}\x{9A01}\x{9A05}\x{9A0E}\x{9A0F}\x{9A12}\x{9A13}\x{9A19}' .
+'\x{9A28}\x{9A2B}\x{9A30}\x{9A37}\x{9A3E}\x{9A40}\x{9A42}\x{9A43}\x{9A45}' .
+'\x{9A4D}\x{9A55}\x{9A57}\x{9A5A}\x{9A5B}\x{9A5F}\x{9A62}\x{9A64}\x{9A65}' .
+'\x{9A69}\x{9A6A}\x{9A6B}\x{9AA8}\x{9AAD}\x{9AB0}\x{9AB8}\x{9ABC}\x{9AC0}' .
+'\x{9AC4}\x{9ACF}\x{9AD1}\x{9AD3}\x{9AD4}\x{9AD8}\x{9ADE}\x{9ADF}\x{9AE2}' .
+'\x{9AE3}\x{9AE6}\x{9AEA}\x{9AEB}\x{9AED}\x{9AEE}\x{9AEF}\x{9AF1}\x{9AF4}' .
+'\x{9AF7}\x{9AFB}\x{9B06}\x{9B18}\x{9B1A}\x{9B1F}\x{9B22}\x{9B23}\x{9B25}' .
+'\x{9B27}\x{9B28}\x{9B29}\x{9B2A}\x{9B2E}\x{9B2F}\x{9B31}\x{9B32}\x{9B3B}' .
+'\x{9B3C}\x{9B41}\x{9B42}\x{9B43}\x{9B44}\x{9B45}\x{9B4D}\x{9B4E}\x{9B4F}' .
+'\x{9B51}\x{9B54}\x{9B58}\x{9B5A}\x{9B6F}\x{9B74}\x{9B83}\x{9B8E}\x{9B91}' .
+'\x{9B92}\x{9B93}\x{9B96}\x{9B97}\x{9B9F}\x{9BA0}\x{9BA8}\x{9BAA}\x{9BAB}' .
+'\x{9BAD}\x{9BAE}\x{9BB4}\x{9BB9}\x{9BC0}\x{9BC6}\x{9BC9}\x{9BCA}\x{9BCF}' .
+'\x{9BD1}\x{9BD2}\x{9BD4}\x{9BD6}\x{9BDB}\x{9BE1}\x{9BE2}\x{9BE3}\x{9BE4}' .
+'\x{9BE8}\x{9BF0}\x{9BF1}\x{9BF2}\x{9BF5}\x{9C04}\x{9C06}\x{9C08}\x{9C09}' .
+'\x{9C0A}\x{9C0C}\x{9C0D}\x{9C10}\x{9C12}\x{9C13}\x{9C14}\x{9C15}\x{9C1B}' .
+'\x{9C21}\x{9C24}\x{9C25}\x{9C2D}\x{9C2E}\x{9C2F}\x{9C30}\x{9C32}\x{9C39}' .
+'\x{9C3A}\x{9C3B}\x{9C3E}\x{9C46}\x{9C47}\x{9C48}\x{9C52}\x{9C57}\x{9C5A}' .
+'\x{9C60}\x{9C67}\x{9C76}\x{9C78}\x{9CE5}\x{9CE7}\x{9CE9}\x{9CEB}\x{9CEC}' .
+'\x{9CF0}\x{9CF3}\x{9CF4}\x{9CF6}\x{9D03}\x{9D06}\x{9D07}\x{9D08}\x{9D09}' .
+'\x{9D0E}\x{9D12}\x{9D15}\x{9D1B}\x{9D1F}\x{9D23}\x{9D26}\x{9D28}\x{9D2A}' .
+'\x{9D2B}\x{9D2C}\x{9D3B}\x{9D3E}\x{9D3F}\x{9D41}\x{9D44}\x{9D46}\x{9D48}' .
+'\x{9D50}\x{9D51}\x{9D59}\x{9D5C}\x{9D5D}\x{9D5E}\x{9D60}\x{9D61}\x{9D64}' .
+'\x{9D6C}\x{9D6F}\x{9D72}\x{9D7A}\x{9D87}\x{9D89}\x{9D8F}\x{9D9A}\x{9DA4}' .
+'\x{9DA9}\x{9DAB}\x{9DAF}\x{9DB2}\x{9DB4}\x{9DB8}\x{9DBA}\x{9DBB}\x{9DC1}' .
+'\x{9DC2}\x{9DC4}\x{9DC6}\x{9DCF}\x{9DD3}\x{9DD9}\x{9DE6}\x{9DED}\x{9DEF}' .
+'\x{9DF2}\x{9DF8}\x{9DF9}\x{9DFA}\x{9DFD}\x{9E1A}\x{9E1B}\x{9E1E}\x{9E75}' .
+'\x{9E78}\x{9E79}\x{9E7D}\x{9E7F}\x{9E81}\x{9E88}\x{9E8B}\x{9E8C}\x{9E91}' .
+'\x{9E92}\x{9E93}\x{9E95}\x{9E97}\x{9E9D}\x{9E9F}\x{9EA5}\x{9EA6}\x{9EA9}' .
+'\x{9EAA}\x{9EAD}\x{9EB8}\x{9EB9}\x{9EBA}\x{9EBB}\x{9EBC}\x{9EBE}\x{9EBF}' .
+'\x{9EC4}\x{9ECC}\x{9ECD}\x{9ECE}\x{9ECF}\x{9ED0}\x{9ED2}\x{9ED4}\x{9ED8}' .
+'\x{9ED9}\x{9EDB}\x{9EDC}\x{9EDD}\x{9EDE}\x{9EE0}\x{9EE5}\x{9EE8}\x{9EEF}' .
+'\x{9EF4}\x{9EF6}\x{9EF7}\x{9EF9}\x{9EFB}\x{9EFC}\x{9EFD}\x{9F07}\x{9F08}' .
+'\x{9F0E}\x{9F13}\x{9F15}\x{9F20}\x{9F21}\x{9F2C}\x{9F3B}\x{9F3E}\x{9F4A}' .
+'\x{9F4B}\x{9F4E}\x{9F4F}\x{9F52}\x{9F54}\x{9F5F}\x{9F60}\x{9F61}\x{9F62}' .
+'\x{9F63}\x{9F66}\x{9F67}\x{9F6A}\x{9F6C}\x{9F72}\x{9F76}\x{9F77}\x{9F8D}' .
+'\x{9F95}\x{9F9C}\x{9F9D}\x{9FA0}]{1,15}$/iu');
Index: trunk/library/Zend/Validate/Hostname/Com.php
===================================================================
--- trunk/library/Zend/Validate/Hostname/Com.php	(revision 5146)
+++ trunk/library/Zend/Validate/Hostname/Com.php	(revision 5146)
@@ -0,0 +1,199 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Com.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Ressource file for com and net idn validation
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+return array(
+    1  => '/^[\x{002d}0-9\x{0400}-\x{052f}]{1,63}$/iu',
+    2  => '/^[\x{002d}0-9\x{0370}-\x{03ff}]{1,63}$/iu',
+    3  => '/^[\x{002d}0-9a-z\x{ac00}-\x{d7a3}]{1,17}$/iu',
+    4  => '/^[\x{002d}0-9a-zÂ·Ã -Ã¶Ãž-Ã¿ÄÄÄ
+ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¡Ä£Ä¥Ä§Ä©Ä«Ä­Ä¯Ä±ÄµÄ·ÄžÄºÄŒÄŸÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ¡Å£Å¥Å§Å©Å«Å­Å¯Å±Å³ÅµÅ·ÅºÅŒÅŸ]{1,63}$/iu',
+    5  => '/^[\x{002d}0-9A-Za-z\x{3400}-\x{3401}\x{3404}-\x{3406}\x{340C}\x{3416}\x{341C}' .
+'\x{3421}\x{3424}\x{3428}-\x{3429}\x{342B}-\x{342E}\x{3430}-\x{3434}\x{3436}' .
+'\x{3438}-\x{343C}\x{343E}\x{3441}-\x{3445}\x{3447}\x{3449}-\x{3451}\x{3453}' .
+'\x{3457}-\x{345F}\x{3463}-\x{3467}\x{346E}-\x{3471}\x{3473}-\x{3477}\x{3479}-\x{348E}\x{3491}-\x{3497}' .
+'\x{3499}-\x{34A1}\x{34A4}-\x{34AD}\x{34AF}-\x{34B0}\x{34B2}-\x{34BF}\x{34C2}-\x{34C5}\x{34C7}-\x{34CC}' .
+'\x{34CE}-\x{34D1}\x{34D3}-\x{34D8}\x{34DA}-\x{34E4}\x{34E7}-\x{34E9}\x{34EC}-\x{34EF}\x{34F1}-\x{34FE}' .
+'\x{3500}-\x{3507}\x{350A}-\x{3513}\x{3515}\x{3517}-\x{351A}\x{351C}-\x{351E}\x{3520}-\x{352A}' .
+'\x{352C}-\x{3552}\x{3554}-\x{355C}\x{355E}-\x{3567}\x{3569}-\x{3573}\x{3575}-\x{357C}\x{3580}-\x{3588}' .
+'\x{358F}-\x{3598}\x{359E}-\x{35AB}\x{35B4}-\x{35CD}\x{35D0}\x{35D3}-\x{35DC}\x{35E2}-\x{35ED}' .
+'\x{35F0}-\x{35F6}\x{35FB}-\x{3602}\x{3605}-\x{360E}\x{3610}-\x{3611}\x{3613}-\x{3616}\x{3619}-\x{362D}' .
+'\x{362F}-\x{3634}\x{3636}-\x{363B}\x{363F}-\x{3645}\x{3647}-\x{364B}\x{364D}-\x{3653}\x{3655}' .
+'\x{3659}-\x{365E}\x{3660}-\x{3665}\x{3667}-\x{367C}\x{367E}\x{3680}-\x{3685}\x{3687}' .
+'\x{3689}-\x{3690}\x{3692}-\x{3698}\x{369A}\x{369C}-\x{36AE}\x{36B0}-\x{36BF}\x{36C1}-\x{36C5}' .
+'\x{36C9}-\x{36CA}\x{36CD}-\x{36DE}\x{36E1}-\x{36E2}\x{36E5}-\x{36FE}\x{3701}-\x{3713}\x{3715}-\x{371E}' .
+'\x{3720}-\x{372C}\x{372E}-\x{3745}\x{3747}-\x{3748}\x{374A}\x{374C}-\x{3759}\x{375B}-\x{3760}' .
+'\x{3762}-\x{3767}\x{3769}-\x{3772}\x{3774}-\x{378C}\x{378F}-\x{379C}\x{379F}\x{37A1}-\x{37AD}' .
+'\x{37AF}-\x{37B7}\x{37B9}-\x{37C1}\x{37C3}-\x{37C5}\x{37C7}-\x{37D4}\x{37D6}-\x{37E0}\x{37E2}' .
+'\x{37E5}-\x{37ED}\x{37EF}-\x{37F6}\x{37F8}-\x{3802}\x{3804}-\x{381D}\x{3820}-\x{3822}\x{3825}-\x{382A}' .
+'\x{382D}-\x{382F}\x{3831}-\x{3832}\x{3834}-\x{384C}\x{384E}-\x{3860}\x{3862}-\x{3863}\x{3865}-\x{386B}' .
+'\x{386D}-\x{3886}\x{3888}-\x{38A1}\x{38A3}\x{38A5}-\x{38AA}\x{38AC}\x{38AE}-\x{38B0}' .
+'\x{38B2}-\x{38B6}\x{38B8}\x{38BA}-\x{38BE}\x{38C0}-\x{38C9}\x{38CB}-\x{38D4}\x{38D8}-\x{38E0}' .
+'\x{38E2}-\x{38E6}\x{38EB}-\x{38ED}\x{38EF}-\x{38F2}\x{38F5}-\x{38F7}\x{38FA}-\x{38FF}\x{3901}-\x{392A}' .
+'\x{392C}\x{392E}-\x{393B}\x{393E}-\x{3956}\x{395A}-\x{3969}\x{396B}-\x{397A}\x{397C}-\x{3987}' .
+'\x{3989}-\x{3998}\x{399A}-\x{39B0}\x{39B2}\x{39B4}-\x{39D0}\x{39D2}-\x{39DA}\x{39DE}-\x{39DF}' .
+'\x{39E1}-\x{39EF}\x{39F1}-\x{3A17}\x{3A19}-\x{3A2A}\x{3A2D}-\x{3A40}\x{3A43}-\x{3A4E}\x{3A50}' .
+'\x{3A52}-\x{3A5E}\x{3A60}-\x{3A6D}\x{3A6F}-\x{3A77}\x{3A79}-\x{3A82}\x{3A84}-\x{3A85}\x{3A87}-\x{3A89}' .
+'\x{3A8B}-\x{3A8F}\x{3A91}-\x{3A93}\x{3A95}-\x{3A96}\x{3A9A}\x{3A9C}-\x{3AA6}\x{3AA8}-\x{3AA9}' .
+'\x{3AAB}-\x{3AB1}\x{3AB4}-\x{3ABC}\x{3ABE}-\x{3AC5}\x{3ACA}-\x{3ACB}\x{3ACD}-\x{3AD5}\x{3AD7}-\x{3AE1}' .
+'\x{3AE4}-\x{3AE7}\x{3AE9}-\x{3AEC}\x{3AEE}-\x{3AFD}\x{3B01}-\x{3B10}\x{3B12}-\x{3B15}\x{3B17}-\x{3B1E}' .
+'\x{3B20}-\x{3B23}\x{3B25}-\x{3B27}\x{3B29}-\x{3B36}\x{3B38}-\x{3B39}\x{3B3B}-\x{3B3C}\x{3B3F}' .
+'\x{3B41}-\x{3B44}\x{3B47}-\x{3B4C}\x{3B4E}\x{3B51}-\x{3B55}\x{3B58}-\x{3B62}\x{3B68}-\x{3B72}' .
+'\x{3B78}-\x{3B88}\x{3B8B}-\x{3B9F}\x{3BA1}\x{3BA3}-\x{3BBA}\x{3BBC}\x{3BBF}-\x{3BD0}' .
+'\x{3BD3}-\x{3BE6}\x{3BEA}-\x{3BFB}\x{3BFE}-\x{3C12}\x{3C14}-\x{3C1B}\x{3C1D}-\x{3C37}\x{3C39}-\x{3C4F}' .
+'\x{3C52}\x{3C54}-\x{3C5C}\x{3C5E}-\x{3C68}\x{3C6A}-\x{3C76}\x{3C78}-\x{3C8F}\x{3C91}-\x{3CA8}' .
+'\x{3CAA}-\x{3CAD}\x{3CAF}-\x{3CBE}\x{3CC0}-\x{3CC8}\x{3CCA}-\x{3CD3}\x{3CD6}-\x{3CE0}\x{3CE4}-\x{3CEE}' .
+'\x{3CF3}-\x{3D0A}\x{3D0E}-\x{3D1E}\x{3D20}-\x{3D21}\x{3D25}-\x{3D38}\x{3D3B}-\x{3D46}\x{3D4A}-\x{3D59}' .
+'\x{3D5D}-\x{3D7B}\x{3D7D}-\x{3D81}\x{3D84}-\x{3D88}\x{3D8C}-\x{3D8F}\x{3D91}-\x{3D98}\x{3D9A}-\x{3D9C}' .
+'\x{3D9E}-\x{3DA1}\x{3DA3}-\x{3DB0}\x{3DB2}-\x{3DB5}\x{3DB9}-\x{3DBC}\x{3DBE}-\x{3DCB}\x{3DCD}-\x{3DDB}' .
+'\x{3DDF}-\x{3DE8}\x{3DEB}-\x{3DF0}\x{3DF3}-\x{3DF9}\x{3DFB}-\x{3DFC}\x{3DFE}-\x{3E05}\x{3E08}-\x{3E33}' .
+'\x{3E35}-\x{3E3E}\x{3E40}-\x{3E47}\x{3E49}-\x{3E67}\x{3E6B}-\x{3E6F}\x{3E71}-\x{3E85}\x{3E87}-\x{3E8C}' .
+'\x{3E8E}-\x{3E98}\x{3E9A}-\x{3EA1}\x{3EA3}-\x{3EAE}\x{3EB0}-\x{3EB5}\x{3EB7}-\x{3EBA}\x{3EBD}' .
+'\x{3EBF}-\x{3EC4}\x{3EC7}-\x{3ECE}\x{3ED1}-\x{3ED7}\x{3ED9}-\x{3EDA}\x{3EDD}-\x{3EE3}\x{3EE7}-\x{3EE8}' .
+'\x{3EEB}-\x{3EF2}\x{3EF5}-\x{3EFF}\x{3F01}-\x{3F02}\x{3F04}-\x{3F07}\x{3F09}-\x{3F44}\x{3F46}-\x{3F4E}' .
+'\x{3F50}-\x{3F53}\x{3F55}-\x{3F72}\x{3F74}-\x{3F75}\x{3F77}-\x{3F7B}\x{3F7D}-\x{3FB0}\x{3FB6}-\x{3FBF}' .
+'\x{3FC1}-\x{3FCF}\x{3FD1}-\x{3FD3}\x{3FD5}-\x{3FDF}\x{3FE1}-\x{400B}\x{400D}-\x{401C}\x{401E}-\x{4024}' .
+'\x{4027}-\x{403F}\x{4041}-\x{4060}\x{4062}-\x{4069}\x{406B}-\x{408A}\x{408C}-\x{40A7}\x{40A9}-\x{40B4}' .
+'\x{40B6}-\x{40C2}\x{40C7}-\x{40CF}\x{40D1}-\x{40DE}\x{40E0}-\x{40E7}\x{40E9}-\x{40EE}\x{40F0}-\x{40FB}' .
+'\x{40FD}-\x{4109}\x{410B}-\x{4115}\x{4118}-\x{411D}\x{411F}-\x{4122}\x{4124}-\x{4133}\x{4136}-\x{4138}' .
+'\x{413A}-\x{4148}\x{414A}-\x{4169}\x{416C}-\x{4185}\x{4188}-\x{418B}\x{418D}-\x{41AD}\x{41AF}-\x{41B3}' .
+'\x{41B5}-\x{41C3}\x{41C5}-\x{41C9}\x{41CB}-\x{41F2}\x{41F5}-\x{41FE}\x{4200}-\x{4227}\x{422A}-\x{4246}' .
+'\x{4248}-\x{4263}\x{4265}-\x{428B}\x{428D}-\x{42A1}\x{42A3}-\x{42C4}\x{42C8}-\x{42DC}\x{42DE}-\x{430A}' .
+'\x{430C}-\x{4335}\x{4337}\x{4342}-\x{435F}\x{4361}-\x{439A}\x{439C}-\x{439D}\x{439F}-\x{43A4}' .
+'\x{43A6}-\x{43EC}\x{43EF}-\x{4405}\x{4407}-\x{4429}\x{442B}-\x{4455}\x{4457}-\x{4468}\x{446A}-\x{446D}' .
+'\x{446F}-\x{4476}\x{4479}-\x{447D}\x{447F}-\x{4486}\x{4488}-\x{4490}\x{4492}-\x{4498}\x{449A}-\x{44AD}' .
+'\x{44B0}-\x{44BD}\x{44C1}-\x{44D3}\x{44D6}-\x{44E7}\x{44EA}\x{44EC}-\x{44FA}\x{44FC}-\x{4541}' .
+'\x{4543}-\x{454F}\x{4551}-\x{4562}\x{4564}-\x{4575}\x{4577}-\x{45AB}\x{45AD}-\x{45BD}\x{45BF}-\x{45D5}' .
+'\x{45D7}-\x{45EC}\x{45EE}-\x{45F2}\x{45F4}-\x{45FA}\x{45FC}-\x{461A}\x{461C}-\x{461D}\x{461F}-\x{4631}' .
+'\x{4633}-\x{4649}\x{464C}\x{464E}-\x{4652}\x{4654}-\x{466A}\x{466C}-\x{4675}\x{4677}-\x{467A}' .
+'\x{467C}-\x{4694}\x{4696}-\x{46A3}\x{46A5}-\x{46AB}\x{46AD}-\x{46D2}\x{46D4}-\x{4723}\x{4729}-\x{4732}' .
+'\x{4734}-\x{4758}\x{475A}\x{475C}-\x{478B}\x{478D}\x{4791}-\x{47B1}\x{47B3}-\x{47F1}' .
+'\x{47F3}-\x{480B}\x{480D}-\x{4815}\x{4817}-\x{4839}\x{483B}-\x{4870}\x{4872}-\x{487A}\x{487C}-\x{487F}' .
+'\x{4883}-\x{488E}\x{4890}-\x{4896}\x{4899}-\x{48A2}\x{48A4}-\x{48B9}\x{48BB}-\x{48C8}\x{48CA}-\x{48D1}' .
+'\x{48D3}-\x{48E5}\x{48E7}-\x{48F2}\x{48F4}-\x{48FF}\x{4901}-\x{4922}\x{4924}-\x{4928}\x{492A}-\x{4931}' .
+'\x{4933}-\x{495B}\x{495D}-\x{4978}\x{497A}\x{497D}\x{4982}-\x{4983}\x{4985}-\x{49A8}' .
+'\x{49AA}-\x{49AF}\x{49B1}-\x{49B7}\x{49B9}-\x{49BD}\x{49C1}-\x{49C7}\x{49C9}-\x{49CE}\x{49D0}-\x{49E8}' .
+'\x{49EA}\x{49EC}\x{49EE}-\x{4A19}\x{4A1B}-\x{4A43}\x{4A45}-\x{4A4D}\x{4A4F}-\x{4A9E}' .
+'\x{4AA0}-\x{4AA9}\x{4AAB}-\x{4B4E}\x{4B50}-\x{4B5B}\x{4B5D}-\x{4B69}\x{4B6B}-\x{4BC2}\x{4BC6}-\x{4BE8}' .
+'\x{4BEA}-\x{4BFA}\x{4BFC}-\x{4C06}\x{4C08}-\x{4C2D}\x{4C2F}-\x{4C32}\x{4C34}-\x{4C35}\x{4C37}-\x{4C69}' .
+'\x{4C6B}-\x{4C73}\x{4C75}-\x{4C86}\x{4C88}-\x{4C97}\x{4C99}-\x{4C9C}\x{4C9F}-\x{4CA3}\x{4CA5}-\x{4CB5}' .
+'\x{4CB7}-\x{4CF8}\x{4CFA}-\x{4D27}\x{4D29}-\x{4DAC}\x{4DAE}-\x{4DB1}\x{4DB3}-\x{4DB5}\x{4E00}-\x{4E54}' .
+'\x{4E56}-\x{4E89}\x{4E8B}-\x{4EEC}\x{4EEE}-\x{4FAC}\x{4FAE}-\x{503C}\x{503E}-\x{51E5}\x{51E7}-\x{5270}' .
+'\x{5272}-\x{56A1}\x{56A3}-\x{5840}\x{5842}-\x{58B5}\x{58B7}-\x{58CB}\x{58CD}-\x{5BC8}\x{5BCA}-\x{5C01}' .
+'\x{5C03}-\x{5C25}\x{5C27}-\x{5D5B}\x{5D5D}-\x{5F08}\x{5F0A}-\x{61F3}\x{61F5}-\x{63BA}\x{63BC}-\x{6441}' .
+'\x{6443}-\x{657C}\x{657E}-\x{663E}\x{6640}-\x{66FC}\x{66FE}-\x{6728}\x{672A}-\x{6766}\x{6768}-\x{67A8}' .
+'\x{67AA}-\x{685B}\x{685D}-\x{685E}\x{6860}-\x{68B9}\x{68BB}-\x{6AC8}\x{6ACA}-\x{6BB0}\x{6BB2}-\x{6C16}' .
+'\x{6C18}-\x{6D9B}\x{6D9D}-\x{6E12}\x{6E14}-\x{6E8B}\x{6E8D}-\x{704D}\x{704F}-\x{7113}\x{7115}-\x{713B}' .
+'\x{713D}-\x{7154}\x{7156}-\x{729F}\x{72A1}-\x{731E}\x{7320}-\x{7362}\x{7364}-\x{7533}\x{7535}-\x{7551}' .
+'\x{7553}-\x{7572}\x{7574}-\x{75E8}\x{75EA}-\x{7679}\x{767B}-\x{783E}\x{7840}-\x{7A62}\x{7A64}-\x{7AC2}' .
+'\x{7AC4}-\x{7B06}\x{7B08}-\x{7B79}\x{7B7B}-\x{7BCE}\x{7BD0}-\x{7D99}\x{7D9B}-\x{7E49}\x{7E4C}-\x{8132}' .
+'\x{8134}\x{8136}-\x{81D2}\x{81D4}-\x{8216}\x{8218}-\x{822D}\x{822F}-\x{83B4}\x{83B6}-\x{841F}' .
+'\x{8421}-\x{86CC}\x{86CE}-\x{874A}\x{874C}-\x{877E}\x{8780}-\x{8A32}\x{8A34}-\x{8B71}\x{8B73}-\x{8B8E}' .
+'\x{8B90}-\x{8DE4}\x{8DE6}-\x{8E9A}\x{8E9C}-\x{8EE1}\x{8EE4}-\x{8F0B}\x{8F0D}-\x{8FB9}\x{8FBB}-\x{9038}' .
+'\x{903A}-\x{9196}\x{9198}-\x{91A3}\x{91A5}-\x{91B7}\x{91B9}-\x{91C7}\x{91C9}-\x{91E0}\x{91E2}-\x{91FB}' .
+'\x{91FD}-\x{922B}\x{922D}-\x{9270}\x{9272}-\x{9420}\x{9422}-\x{9664}\x{9666}-\x{9679}\x{967B}-\x{9770}' .
+'\x{9772}-\x{982B}\x{982D}-\x{98ED}\x{98EF}-\x{99C4}\x{99C6}-\x{9A11}\x{9A14}-\x{9A27}\x{9A29}-\x{9D0D}' .
+'\x{9D0F}-\x{9D2B}\x{9D2D}-\x{9D8E}\x{9D90}-\x{9DC5}\x{9DC7}-\x{9E77}\x{9E79}-\x{9EB8}\x{9EBB}-\x{9F20}' .
+'\x{9F22}-\x{9F61}\x{9F63}-\x{9FA5}\x{FA28}]{1,20}$/iu',
+    6 => '/^[\x{002d}0-9A-Za-z]{1,63}$/iu',
+    7 => '/^[\x{00A1}-\x{00FF}]{1,63}$/iu',
+    8 => '/^[\x{0100}-\x{017f}]{1,63}$/iu',
+    9 => '/^[\x{0180}-\x{024f}]{1,63}$/iu',
+    10 => '/^[\x{0250}-\x{02af}]{1,63}$/iu',
+    11 => '/^[\x{02b0}-\x{02ff}]{1,63}$/iu',
+    12 => '/^[\x{0300}-\x{036f}]{1,63}$/iu',
+    13 => '/^[\x{0370}-\x{03ff}]{1,63}$/iu',
+    14 => '/^[\x{0400}-\x{04ff}]{1,63}$/iu',
+    15 => '/^[\x{0500}-\x{052f}]{1,63}$/iu',
+    16 => '/^[\x{0530}-\x{058F}]{1,63}$/iu',
+    17 => '/^[\x{0590}-\x{05FF}]{1,63}$/iu',
+    18 => '/^[\x{0600}-\x{06FF}]{1,63}$/iu',
+    19 => '/^[\x{0700}-\x{074F}]{1,63}$/iu',
+    20 => '/^[\x{0780}-\x{07BF}]{1,63}$/iu',
+    21 => '/^[\x{0900}-\x{097F}]{1,63}$/iu',
+    22 => '/^[\x{0980}-\x{09FF}]{1,63}$/iu',
+    23 => '/^[\x{0A00}-\x{0A7F}]{1,63}$/iu',
+    24 => '/^[\x{0A80}-\x{0AFF}]{1,63}$/iu',
+    25 => '/^[\x{0B00}-\x{0B7F}]{1,63}$/iu',
+    26 => '/^[\x{0B80}-\x{0BFF}]{1,63}$/iu',
+    27 => '/^[\x{0C00}-\x{0C7F}]{1,63}$/iu',
+    28 => '/^[\x{0C80}-\x{0CFF}]{1,63}$/iu',
+    29 => '/^[\x{0D00}-\x{0D7F}]{1,63}$/iu',
+    30 => '/^[\x{0D80}-\x{0DFF}]{1,63}$/iu',
+    31 => '/^[\x{0E00}-\x{0E7F}]{1,63}$/iu',
+    32 => '/^[\x{0E80}-\x{0EFF}]{1,63}$/iu',
+    33 => '/^[\x{0F00}-\x{0FFF}]{1,63}$/iu',
+    34 => '/^[\x{1000}-\x{109F}]{1,63}$/iu',
+    35 => '/^[\x{10A0}-\x{10FF}]{1,63}$/iu',
+    36 => '/^[\x{1100}-\x{11FF}]{1,63}$/iu',
+    37 => '/^[\x{1200}-\x{137F}]{1,63}$/iu',
+    38 => '/^[\x{13A0}-\x{13FF}]{1,63}$/iu',
+    39 => '/^[\x{1400}-\x{167F}]{1,63}$/iu',
+    40 => '/^[\x{1680}-\x{169F}]{1,63}$/iu',
+    41 => '/^[\x{16A0}-\x{16FF}]{1,63}$/iu',
+    42 => '/^[\x{1700}-\x{171F}]{1,63}$/iu',
+    43 => '/^[\x{1720}-\x{173F}]{1,63}$/iu',
+    44 => '/^[\x{1740}-\x{175F}]{1,63}$/iu',
+    45 => '/^[\x{1760}-\x{177F}]{1,63}$/iu',
+    46 => '/^[\x{1780}-\x{17FF}]{1,63}$/iu',
+    47 => '/^[\x{1800}-\x{18AF}]{1,63}$/iu',
+    48 => '/^[\x{1E00}-\x{1EFF}]{1,63}$/iu',
+    49 => '/^[\x{1F00}-\x{1FFF}]{1,63}$/iu',
+    50 => '/^[\x{2070}-\x{209F}]{1,63}$/iu',
+    51 => '/^[\x{2100}-\x{214F}]{1,63}$/iu',
+    52 => '/^[\x{2150}-\x{218F}]{1,63}$/iu',
+    53 => '/^[\x{2460}-\x{24FF}]{1,63}$/iu',
+    54 => '/^[\x{2E80}-\x{2EFF}]{1,63}$/iu',
+    55 => '/^[\x{2F00}-\x{2FDF}]{1,63}$/iu',
+    56 => '/^[\x{2FF0}-\x{2FFF}]{1,63}$/iu',
+    57 => '/^[\x{3040}-\x{309F}]{1,63}$/iu',
+    58 => '/^[\x{30A0}-\x{30FF}]{1,63}$/iu',
+    59 => '/^[\x{3100}-\x{312F}]{1,63}$/iu',
+    60 => '/^[\x{3130}-\x{318F}]{1,63}$/iu',
+    61 => '/^[\x{3190}-\x{319F}]{1,63}$/iu',
+    62 => '/^[\x{31A0}-\x{31BF}]{1,63}$/iu',
+    63 => '/^[\x{31F0}-\x{31FF}]{1,63}$/iu',
+    64 => '/^[\x{3200}-\x{32FF}]{1,63}$/iu',
+    65 => '/^[\x{3300}-\x{33FF}]{1,63}$/iu',
+    66 => '/^[\x{3400}-\x{4DBF}]{1,63}$/iu',
+    67 => '/^[\x{4E00}-\x{9FFF}]{1,63}$/iu',
+    68 => '/^[\x{A000}-\x{A48F}]{1,63}$/iu',
+    69 => '/^[\x{A490}-\x{A4CF}]{1,63}$/iu',
+    70 => '/^[\x{AC00}-\x{D7AF}]{1,63}$/iu',
+    71 => '/^[\x{D800}-\x{DB7F}]{1,63}$/iu',
+    72 => '/^[\x{DC00}-\x{DFFF}]{1,63}$/iu',
+    73 => '/^[\x{F900}-\x{FAFF}]{1,63}$/iu',
+    74 => '/^[\x{FB00}-\x{FB4F}]{1,63}$/iu',
+    75 => '/^[\x{FB50}-\x{FDFF}]{1,63}$/iu',
+    76 => '/^[\x{FE20}-\x{FE2F}]{1,63}$/iu',
+    77 => '/^[\x{FE70}-\x{FEFF}]{1,63}$/iu',
+    78 => '/^[\x{FF00}-\x{FFEF}]{1,63}$/iu',
+    79 => '/^[\x{20000}-\x{2A6DF}]{1,63}$/iu',
+    80 => '/^[\x{2F800}-\x{2FA1F}]{1,63}$/iu'
+
+);
Index: trunk/library/Zend/Validate/Hostname/Cn.php
===================================================================
--- trunk/library/Zend/Validate/Hostname/Cn.php	(revision 5146)
+++ trunk/library/Zend/Validate/Hostname/Cn.php	(revision 5146)
@@ -0,0 +1,2199 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Cn.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Ressource file for chinese idn validation
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+return array(
+    1  => '/^[\x{002d}0-9a-z\x{3447}\x{3473}\x{359E}\x{360E}\x{361A}\x{3918}\x{396E}\x{39CF}\x{39D0}' .
+'\x{39DF}\x{3A73}\x{3B4E}\x{3C6E}\x{3CE0}\x{4056}\x{415F}\x{4337}\x{43AC}' .
+'\x{43B1}\x{43DD}\x{44D6}\x{464C}\x{4661}\x{4723}\x{4729}\x{477C}\x{478D}' .
+'\x{4947}\x{497A}\x{497D}\x{4982}\x{4983}\x{4985}\x{4986}\x{499B}\x{499F}' .
+'\x{49B6}\x{49B7}\x{4C77}\x{4C9F}\x{4CA0}\x{4CA1}\x{4CA2}\x{4CA3}\x{4D13}' .
+'\x{4D14}\x{4D15}\x{4D16}\x{4D17}\x{4D18}\x{4D19}\x{4DAE}\x{4E00}\x{4E01}' .
+'\x{4E02}\x{4E03}\x{4E04}\x{4E05}\x{4E06}\x{4E07}\x{4E08}\x{4E09}\x{4E0A}' .
+'\x{4E0B}\x{4E0C}\x{4E0D}\x{4E0E}\x{4E0F}\x{4E10}\x{4E11}\x{4E13}\x{4E14}' .
+'\x{4E15}\x{4E16}\x{4E17}\x{4E18}\x{4E19}\x{4E1A}\x{4E1B}\x{4E1C}\x{4E1D}' .
+'\x{4E1E}\x{4E1F}\x{4E20}\x{4E21}\x{4E22}\x{4E23}\x{4E24}\x{4E25}\x{4E26}' .
+'\x{4E27}\x{4E28}\x{4E2A}\x{4E2B}\x{4E2C}\x{4E2D}\x{4E2E}\x{4E2F}\x{4E30}' .
+'\x{4E31}\x{4E32}\x{4E33}\x{4E34}\x{4E35}\x{4E36}\x{4E37}\x{4E38}\x{4E39}' .
+'\x{4E3A}\x{4E3B}\x{4E3C}\x{4E3D}\x{4E3E}\x{4E3F}\x{4E40}\x{4E41}\x{4E42}' .
+'\x{4E43}\x{4E44}\x{4E45}\x{4E46}\x{4E47}\x{4E48}\x{4E49}\x{4E4A}\x{4E4B}' .
+'\x{4E4C}\x{4E4D}\x{4E4E}\x{4E4F}\x{4E50}\x{4E51}\x{4E52}\x{4E53}\x{4E54}' .
+'\x{4E56}\x{4E57}\x{4E58}\x{4E59}\x{4E5A}\x{4E5B}\x{4E5C}\x{4E5D}\x{4E5E}' .
+'\x{4E5F}\x{4E60}\x{4E61}\x{4E62}\x{4E63}\x{4E64}\x{4E65}\x{4E66}\x{4E67}' .
+'\x{4E69}\x{4E6A}\x{4E6B}\x{4E6C}\x{4E6D}\x{4E6E}\x{4E6F}\x{4E70}\x{4E71}' .
+'\x{4E72}\x{4E73}\x{4E74}\x{4E75}\x{4E76}\x{4E77}\x{4E78}\x{4E7A}\x{4E7B}' .
+'\x{4E7C}\x{4E7D}\x{4E7E}\x{4E7F}\x{4E80}\x{4E81}\x{4E82}\x{4E83}\x{4E84}' .
+'\x{4E85}\x{4E86}\x{4E87}\x{4E88}\x{4E89}\x{4E8B}\x{4E8C}\x{4E8D}\x{4E8E}' .
+'\x{4E8F}\x{4E90}\x{4E91}\x{4E92}\x{4E93}\x{4E94}\x{4E95}\x{4E97}\x{4E98}' .
+'\x{4E99}\x{4E9A}\x{4E9B}\x{4E9C}\x{4E9D}\x{4E9E}\x{4E9F}\x{4EA0}\x{4EA1}' .
+'\x{4EA2}\x{4EA4}\x{4EA5}\x{4EA6}\x{4EA7}\x{4EA8}\x{4EA9}\x{4EAA}\x{4EAB}' .
+'\x{4EAC}\x{4EAD}\x{4EAE}\x{4EAF}\x{4EB0}\x{4EB1}\x{4EB2}\x{4EB3}\x{4EB4}' .
+'\x{4EB5}\x{4EB6}\x{4EB7}\x{4EB8}\x{4EB9}\x{4EBA}\x{4EBB}\x{4EBD}\x{4EBE}' .
+'\x{4EBF}\x{4EC0}\x{4EC1}\x{4EC2}\x{4EC3}\x{4EC4}\x{4EC5}\x{4EC6}\x{4EC7}' .
+'\x{4EC8}\x{4EC9}\x{4ECA}\x{4ECB}\x{4ECD}\x{4ECE}\x{4ECF}\x{4ED0}\x{4ED1}' .
+'\x{4ED2}\x{4ED3}\x{4ED4}\x{4ED5}\x{4ED6}\x{4ED7}\x{4ED8}\x{4ED9}\x{4EDA}' .
+'\x{4EDB}\x{4EDC}\x{4EDD}\x{4EDE}\x{4EDF}\x{4EE0}\x{4EE1}\x{4EE2}\x{4EE3}' .
+'\x{4EE4}\x{4EE5}\x{4EE6}\x{4EE8}\x{4EE9}\x{4EEA}\x{4EEB}\x{4EEC}\x{4EEF}' .
+'\x{4EF0}\x{4EF1}\x{4EF2}\x{4EF3}\x{4EF4}\x{4EF5}\x{4EF6}\x{4EF7}\x{4EFB}' .
+'\x{4EFD}\x{4EFF}\x{4F00}\x{4F01}\x{4F02}\x{4F03}\x{4F04}\x{4F05}\x{4F06}' .
+'\x{4F08}\x{4F09}\x{4F0A}\x{4F0B}\x{4F0C}\x{4F0D}\x{4F0E}\x{4F0F}\x{4F10}' .
+'\x{4F11}\x{4F12}\x{4F13}\x{4F14}\x{4F15}\x{4F17}\x{4F18}\x{4F19}\x{4F1A}' .
+'\x{4F1B}\x{4F1C}\x{4F1D}\x{4F1E}\x{4F1F}\x{4F20}\x{4F21}\x{4F22}\x{4F23}' .
+'\x{4F24}\x{4F25}\x{4F26}\x{4F27}\x{4F29}\x{4F2A}\x{4F2B}\x{4F2C}\x{4F2D}' .
+'\x{4F2E}\x{4F2F}\x{4F30}\x{4F32}\x{4F33}\x{4F34}\x{4F36}\x{4F38}\x{4F39}' .
+'\x{4F3A}\x{4F3B}\x{4F3C}\x{4F3D}\x{4F3E}\x{4F3F}\x{4F41}\x{4F42}\x{4F43}' .
+'\x{4F45}\x{4F46}\x{4F47}\x{4F48}\x{4F49}\x{4F4A}\x{4F4B}\x{4F4C}\x{4F4D}' .
+'\x{4F4E}\x{4F4F}\x{4F50}\x{4F51}\x{4F52}\x{4F53}\x{4F54}\x{4F55}\x{4F56}' .
+'\x{4F57}\x{4F58}\x{4F59}\x{4F5A}\x{4F5B}\x{4F5C}\x{4F5D}\x{4F5E}\x{4F5F}' .
+'\x{4F60}\x{4F61}\x{4F62}\x{4F63}\x{4F64}\x{4F65}\x{4F66}\x{4F67}\x{4F68}' .
+'\x{4F69}\x{4F6A}\x{4F6B}\x{4F6C}\x{4F6D}\x{4F6E}\x{4F6F}\x{4F70}\x{4F72}' .
+'\x{4F73}\x{4F74}\x{4F75}\x{4F76}\x{4F77}\x{4F78}\x{4F79}\x{4F7A}\x{4F7B}' .
+'\x{4F7C}\x{4F7D}\x{4F7E}\x{4F7F}\x{4F80}\x{4F81}\x{4F82}\x{4F83}\x{4F84}' .
+'\x{4F85}\x{4F86}\x{4F87}\x{4F88}\x{4F89}\x{4F8A}\x{4F8B}\x{4F8D}\x{4F8F}' .
+'\x{4F90}\x{4F91}\x{4F92}\x{4F93}\x{4F94}\x{4F95}\x{4F96}\x{4F97}\x{4F98}' .
+'\x{4F99}\x{4F9A}\x{4F9B}\x{4F9C}\x{4F9D}\x{4F9E}\x{4F9F}\x{4FA0}\x{4FA1}' .
+'\x{4FA3}\x{4FA4}\x{4FA5}\x{4FA6}\x{4FA7}\x{4FA8}\x{4FA9}\x{4FAA}\x{4FAB}' .
+'\x{4FAC}\x{4FAE}\x{4FAF}\x{4FB0}\x{4FB1}\x{4FB2}\x{4FB3}\x{4FB4}\x{4FB5}' .
+'\x{4FB6}\x{4FB7}\x{4FB8}\x{4FB9}\x{4FBA}\x{4FBB}\x{4FBC}\x{4FBE}\x{4FBF}' .
+'\x{4FC0}\x{4FC1}\x{4FC2}\x{4FC3}\x{4FC4}\x{4FC5}\x{4FC7}\x{4FC9}\x{4FCA}' .
+'\x{4FCB}\x{4FCD}\x{4FCE}\x{4FCF}\x{4FD0}\x{4FD1}\x{4FD2}\x{4FD3}\x{4FD4}' .
+'\x{4FD5}\x{4FD6}\x{4FD7}\x{4FD8}\x{4FD9}\x{4FDA}\x{4FDB}\x{4FDC}\x{4FDD}' .
+'\x{4FDE}\x{4FDF}\x{4FE0}\x{4FE1}\x{4FE3}\x{4FE4}\x{4FE5}\x{4FE6}\x{4FE7}' .
+'\x{4FE8}\x{4FE9}\x{4FEA}\x{4FEB}\x{4FEC}\x{4FED}\x{4FEE}\x{4FEF}\x{4FF0}' .
+'\x{4FF1}\x{4FF2}\x{4FF3}\x{4FF4}\x{4FF5}\x{4FF6}\x{4FF7}\x{4FF8}\x{4FF9}' .
+'\x{4FFA}\x{4FFB}\x{4FFE}\x{4FFF}\x{5000}\x{5001}\x{5002}\x{5003}\x{5004}' .
+'\x{5005}\x{5006}\x{5007}\x{5008}\x{5009}\x{500A}\x{500B}\x{500C}\x{500D}' .
+'\x{500E}\x{500F}\x{5011}\x{5012}\x{5013}\x{5014}\x{5015}\x{5016}\x{5017}' .
+'\x{5018}\x{5019}\x{501A}\x{501B}\x{501C}\x{501D}\x{501E}\x{501F}\x{5020}' .
+'\x{5021}\x{5022}\x{5023}\x{5024}\x{5025}\x{5026}\x{5027}\x{5028}\x{5029}' .
+'\x{502A}\x{502B}\x{502C}\x{502D}\x{502E}\x{502F}\x{5030}\x{5031}\x{5032}' .
+'\x{5033}\x{5035}\x{5036}\x{5037}\x{5039}\x{503A}\x{503B}\x{503C}\x{503E}' .
+'\x{503F}\x{5040}\x{5041}\x{5043}\x{5044}\x{5045}\x{5046}\x{5047}\x{5048}' .
+'\x{5049}\x{504A}\x{504B}\x{504C}\x{504D}\x{504E}\x{504F}\x{5051}\x{5053}' .
+'\x{5054}\x{5055}\x{5056}\x{5057}\x{5059}\x{505A}\x{505B}\x{505C}\x{505D}' .
+'\x{505E}\x{505F}\x{5060}\x{5061}\x{5062}\x{5063}\x{5064}\x{5065}\x{5066}' .
+'\x{5067}\x{5068}\x{5069}\x{506A}\x{506B}\x{506C}\x{506D}\x{506E}\x{506F}' .
+'\x{5070}\x{5071}\x{5072}\x{5073}\x{5074}\x{5075}\x{5076}\x{5077}\x{5078}' .
+'\x{5079}\x{507A}\x{507B}\x{507D}\x{507E}\x{507F}\x{5080}\x{5082}\x{5083}' .
+'\x{5084}\x{5085}\x{5086}\x{5087}\x{5088}\x{5089}\x{508A}\x{508B}\x{508C}' .
+'\x{508D}\x{508E}\x{508F}\x{5090}\x{5091}\x{5092}\x{5094}\x{5095}\x{5096}' .
+'\x{5098}\x{5099}\x{509A}\x{509B}\x{509C}\x{509D}\x{509E}\x{50A2}\x{50A3}' .
+'\x{50A4}\x{50A5}\x{50A6}\x{50A7}\x{50A8}\x{50A9}\x{50AA}\x{50AB}\x{50AC}' .
+'\x{50AD}\x{50AE}\x{50AF}\x{50B0}\x{50B1}\x{50B2}\x{50B3}\x{50B4}\x{50B5}' .
+'\x{50B6}\x{50B7}\x{50B8}\x{50BA}\x{50BB}\x{50BC}\x{50BD}\x{50BE}\x{50BF}' .
+'\x{50C0}\x{50C1}\x{50C2}\x{50C4}\x{50C5}\x{50C6}\x{50C7}\x{50C8}\x{50C9}' .
+'\x{50CA}\x{50CB}\x{50CC}\x{50CD}\x{50CE}\x{50CF}\x{50D0}\x{50D1}\x{50D2}' .
+'\x{50D3}\x{50D4}\x{50D5}\x{50D6}\x{50D7}\x{50D9}\x{50DA}\x{50DB}\x{50DC}' .
+'\x{50DD}\x{50DE}\x{50E0}\x{50E3}\x{50E4}\x{50E5}\x{50E6}\x{50E7}\x{50E8}' .
+'\x{50E9}\x{50EA}\x{50EC}\x{50ED}\x{50EE}\x{50EF}\x{50F0}\x{50F1}\x{50F2}' .
+'\x{50F3}\x{50F5}\x{50F6}\x{50F8}\x{50F9}\x{50FA}\x{50FB}\x{50FC}\x{50FD}' .
+'\x{50FE}\x{50FF}\x{5100}\x{5101}\x{5102}\x{5103}\x{5104}\x{5105}\x{5106}' .
+'\x{5107}\x{5108}\x{5109}\x{510A}\x{510B}\x{510C}\x{510D}\x{510E}\x{510F}' .
+'\x{5110}\x{5111}\x{5112}\x{5113}\x{5114}\x{5115}\x{5116}\x{5117}\x{5118}' .
+'\x{5119}\x{511A}\x{511C}\x{511D}\x{511E}\x{511F}\x{5120}\x{5121}\x{5122}' .
+'\x{5123}\x{5124}\x{5125}\x{5126}\x{5127}\x{5129}\x{512A}\x{512C}\x{512D}' .
+'\x{512E}\x{512F}\x{5130}\x{5131}\x{5132}\x{5133}\x{5134}\x{5135}\x{5136}' .
+'\x{5137}\x{5138}\x{5139}\x{513A}\x{513B}\x{513C}\x{513D}\x{513E}\x{513F}' .
+'\x{5140}\x{5141}\x{5143}\x{5144}\x{5145}\x{5146}\x{5147}\x{5148}\x{5149}' .
+'\x{514B}\x{514C}\x{514D}\x{514E}\x{5150}\x{5151}\x{5152}\x{5154}\x{5155}' .
+'\x{5156}\x{5157}\x{5159}\x{515A}\x{515B}\x{515C}\x{515D}\x{515E}\x{515F}' .
+'\x{5161}\x{5162}\x{5163}\x{5165}\x{5166}\x{5167}\x{5168}\x{5169}\x{516A}' .
+'\x{516B}\x{516C}\x{516D}\x{516E}\x{516F}\x{5170}\x{5171}\x{5173}\x{5174}' .
+'\x{5175}\x{5176}\x{5177}\x{5178}\x{5179}\x{517A}\x{517B}\x{517C}\x{517D}' .
+'\x{517F}\x{5180}\x{5181}\x{5182}\x{5185}\x{5186}\x{5187}\x{5188}\x{5189}' .
+'\x{518A}\x{518B}\x{518C}\x{518D}\x{518F}\x{5190}\x{5191}\x{5192}\x{5193}' .
+'\x{5194}\x{5195}\x{5196}\x{5197}\x{5198}\x{5199}\x{519A}\x{519B}\x{519C}' .
+'\x{519D}\x{519E}\x{519F}\x{51A0}\x{51A2}\x{51A4}\x{51A5}\x{51A6}\x{51A7}' .
+'\x{51A8}\x{51AA}\x{51AB}\x{51AC}\x{51AE}\x{51AF}\x{51B0}\x{51B1}\x{51B2}' .
+'\x{51B3}\x{51B5}\x{51B6}\x{51B7}\x{51B9}\x{51BB}\x{51BC}\x{51BD}\x{51BE}' .
+'\x{51BF}\x{51C0}\x{51C1}\x{51C3}\x{51C4}\x{51C5}\x{51C6}\x{51C7}\x{51C8}' .
+'\x{51C9}\x{51CA}\x{51CB}\x{51CC}\x{51CD}\x{51CE}\x{51CF}\x{51D0}\x{51D1}' .
+'\x{51D4}\x{51D5}\x{51D6}\x{51D7}\x{51D8}\x{51D9}\x{51DA}\x{51DB}\x{51DC}' .
+'\x{51DD}\x{51DE}\x{51E0}\x{51E1}\x{51E2}\x{51E3}\x{51E4}\x{51E5}\x{51E7}' .
+'\x{51E8}\x{51E9}\x{51EA}\x{51EB}\x{51ED}\x{51EF}\x{51F0}\x{51F1}\x{51F3}' .
+'\x{51F4}\x{51F5}\x{51F6}\x{51F7}\x{51F8}\x{51F9}\x{51FA}\x{51FB}\x{51FC}' .
+'\x{51FD}\x{51FE}\x{51FF}\x{5200}\x{5201}\x{5202}\x{5203}\x{5204}\x{5205}' .
+'\x{5206}\x{5207}\x{5208}\x{5209}\x{520A}\x{520B}\x{520C}\x{520D}\x{520E}' .
+'\x{520F}\x{5210}\x{5211}\x{5212}\x{5213}\x{5214}\x{5215}\x{5216}\x{5217}' .
+'\x{5218}\x{5219}\x{521A}\x{521B}\x{521C}\x{521D}\x{521E}\x{521F}\x{5220}' .
+'\x{5221}\x{5222}\x{5223}\x{5224}\x{5225}\x{5226}\x{5228}\x{5229}\x{522A}' .
+'\x{522B}\x{522C}\x{522D}\x{522E}\x{522F}\x{5230}\x{5231}\x{5232}\x{5233}' .
+'\x{5234}\x{5235}\x{5236}\x{5237}\x{5238}\x{5239}\x{523A}\x{523B}\x{523C}' .
+'\x{523D}\x{523E}\x{523F}\x{5240}\x{5241}\x{5242}\x{5243}\x{5244}\x{5245}' .
+'\x{5246}\x{5247}\x{5248}\x{5249}\x{524A}\x{524B}\x{524C}\x{524D}\x{524E}' .
+'\x{5250}\x{5251}\x{5252}\x{5254}\x{5255}\x{5256}\x{5257}\x{5258}\x{5259}' .
+'\x{525A}\x{525B}\x{525C}\x{525D}\x{525E}\x{525F}\x{5260}\x{5261}\x{5262}' .
+'\x{5263}\x{5264}\x{5265}\x{5267}\x{5268}\x{5269}\x{526A}\x{526B}\x{526C}' .
+'\x{526D}\x{526E}\x{526F}\x{5270}\x{5272}\x{5273}\x{5274}\x{5275}\x{5276}' .
+'\x{5277}\x{5278}\x{527A}\x{527B}\x{527C}\x{527D}\x{527E}\x{527F}\x{5280}' .
+'\x{5281}\x{5282}\x{5283}\x{5284}\x{5286}\x{5287}\x{5288}\x{5289}\x{528A}' .
+'\x{528B}\x{528C}\x{528D}\x{528F}\x{5290}\x{5291}\x{5292}\x{5293}\x{5294}' .
+'\x{5295}\x{5296}\x{5297}\x{5298}\x{5299}\x{529A}\x{529B}\x{529C}\x{529D}' .
+'\x{529E}\x{529F}\x{52A0}\x{52A1}\x{52A2}\x{52A3}\x{52A5}\x{52A6}\x{52A7}' .
+'\x{52A8}\x{52A9}\x{52AA}\x{52AB}\x{52AC}\x{52AD}\x{52AE}\x{52AF}\x{52B0}' .
+'\x{52B1}\x{52B2}\x{52B3}\x{52B4}\x{52B5}\x{52B6}\x{52B7}\x{52B8}\x{52B9}' .
+'\x{52BA}\x{52BB}\x{52BC}\x{52BD}\x{52BE}\x{52BF}\x{52C0}\x{52C1}\x{52C2}' .
+'\x{52C3}\x{52C6}\x{52C7}\x{52C9}\x{52CA}\x{52CB}\x{52CD}\x{52CF}\x{52D0}' .
+'\x{52D2}\x{52D3}\x{52D5}\x{52D6}\x{52D7}\x{52D8}\x{52D9}\x{52DA}\x{52DB}' .
+'\x{52DC}\x{52DD}\x{52DE}\x{52DF}\x{52E0}\x{52E2}\x{52E3}\x{52E4}\x{52E6}' .
+'\x{52E7}\x{52E8}\x{52E9}\x{52EA}\x{52EB}\x{52EC}\x{52ED}\x{52EF}\x{52F0}' .
+'\x{52F1}\x{52F2}\x{52F3}\x{52F4}\x{52F5}\x{52F6}\x{52F7}\x{52F8}\x{52F9}' .
+'\x{52FA}\x{52FB}\x{52FC}\x{52FD}\x{52FE}\x{52FF}\x{5300}\x{5301}\x{5302}' .
+'\x{5305}\x{5306}\x{5307}\x{5308}\x{5309}\x{530A}\x{530B}\x{530C}\x{530D}' .
+'\x{530E}\x{530F}\x{5310}\x{5311}\x{5312}\x{5313}\x{5314}\x{5315}\x{5316}' .
+'\x{5317}\x{5319}\x{531A}\x{531C}\x{531D}\x{531F}\x{5320}\x{5321}\x{5322}' .
+'\x{5323}\x{5324}\x{5325}\x{5326}\x{5328}\x{532A}\x{532B}\x{532C}\x{532D}' .
+'\x{532E}\x{532F}\x{5330}\x{5331}\x{5333}\x{5334}\x{5337}\x{5339}\x{533A}' .
+'\x{533B}\x{533C}\x{533D}\x{533E}\x{533F}\x{5340}\x{5341}\x{5343}\x{5344}' .
+'\x{5345}\x{5346}\x{5347}\x{5348}\x{5349}\x{534A}\x{534B}\x{534C}\x{534D}' .
+'\x{534E}\x{534F}\x{5350}\x{5351}\x{5352}\x{5353}\x{5354}\x{5355}\x{5356}' .
+'\x{5357}\x{5358}\x{5359}\x{535A}\x{535C}\x{535E}\x{535F}\x{5360}\x{5361}' .
+'\x{5362}\x{5363}\x{5364}\x{5365}\x{5366}\x{5367}\x{5369}\x{536B}\x{536C}' .
+'\x{536E}\x{536F}\x{5370}\x{5371}\x{5372}\x{5373}\x{5374}\x{5375}\x{5376}' .
+'\x{5377}\x{5378}\x{5379}\x{537A}\x{537B}\x{537C}\x{537D}\x{537E}\x{537F}' .
+'\x{5381}\x{5382}\x{5383}\x{5384}\x{5385}\x{5386}\x{5387}\x{5388}\x{5389}' .
+'\x{538A}\x{538B}\x{538C}\x{538D}\x{538E}\x{538F}\x{5390}\x{5391}\x{5392}' .
+'\x{5393}\x{5394}\x{5395}\x{5396}\x{5397}\x{5398}\x{5399}\x{539A}\x{539B}' .
+'\x{539C}\x{539D}\x{539E}\x{539F}\x{53A0}\x{53A2}\x{53A3}\x{53A4}\x{53A5}' .
+'\x{53A6}\x{53A7}\x{53A8}\x{53A9}\x{53AC}\x{53AD}\x{53AE}\x{53B0}\x{53B1}' .
+'\x{53B2}\x{53B3}\x{53B4}\x{53B5}\x{53B6}\x{53B7}\x{53B8}\x{53B9}\x{53BB}' .
+'\x{53BC}\x{53BD}\x{53BE}\x{53BF}\x{53C0}\x{53C1}\x{53C2}\x{53C3}\x{53C4}' .
+'\x{53C6}\x{53C7}\x{53C8}\x{53C9}\x{53CA}\x{53CB}\x{53CC}\x{53CD}\x{53CE}' .
+'\x{53D0}\x{53D1}\x{53D2}\x{53D3}\x{53D4}\x{53D5}\x{53D6}\x{53D7}\x{53D8}' .
+'\x{53D9}\x{53DB}\x{53DC}\x{53DF}\x{53E0}\x{53E1}\x{53E2}\x{53E3}\x{53E4}' .
+'\x{53E5}\x{53E6}\x{53E8}\x{53E9}\x{53EA}\x{53EB}\x{53EC}\x{53ED}\x{53EE}' .
+'\x{53EF}\x{53F0}\x{53F1}\x{53F2}\x{53F3}\x{53F4}\x{53F5}\x{53F6}\x{53F7}' .
+'\x{53F8}\x{53F9}\x{53FA}\x{53FB}\x{53FC}\x{53FD}\x{53FE}\x{5401}\x{5402}' .
+'\x{5403}\x{5404}\x{5405}\x{5406}\x{5407}\x{5408}\x{5409}\x{540A}\x{540B}' .
+'\x{540C}\x{540D}\x{540E}\x{540F}\x{5410}\x{5411}\x{5412}\x{5413}\x{5414}' .
+'\x{5415}\x{5416}\x{5417}\x{5418}\x{5419}\x{541B}\x{541C}\x{541D}\x{541E}' .
+'\x{541F}\x{5420}\x{5421}\x{5423}\x{5424}\x{5425}\x{5426}\x{5427}\x{5428}' .
+'\x{5429}\x{542A}\x{542B}\x{542C}\x{542D}\x{542E}\x{542F}\x{5430}\x{5431}' .
+'\x{5432}\x{5433}\x{5434}\x{5435}\x{5436}\x{5437}\x{5438}\x{5439}\x{543A}' .
+'\x{543B}\x{543C}\x{543D}\x{543E}\x{543F}\x{5440}\x{5441}\x{5442}\x{5443}' .
+'\x{5444}\x{5445}\x{5446}\x{5447}\x{5448}\x{5449}\x{544A}\x{544B}\x{544D}' .
+'\x{544E}\x{544F}\x{5450}\x{5451}\x{5452}\x{5453}\x{5454}\x{5455}\x{5456}' .
+'\x{5457}\x{5458}\x{5459}\x{545A}\x{545B}\x{545C}\x{545E}\x{545F}\x{5460}' .
+'\x{5461}\x{5462}\x{5463}\x{5464}\x{5465}\x{5466}\x{5467}\x{5468}\x{546A}' .
+'\x{546B}\x{546C}\x{546D}\x{546E}\x{546F}\x{5470}\x{5471}\x{5472}\x{5473}' .
+'\x{5474}\x{5475}\x{5476}\x{5477}\x{5478}\x{5479}\x{547A}\x{547B}\x{547C}' .
+'\x{547D}\x{547E}\x{547F}\x{5480}\x{5481}\x{5482}\x{5483}\x{5484}\x{5485}' .
+'\x{5486}\x{5487}\x{5488}\x{5489}\x{548B}\x{548C}\x{548D}\x{548E}\x{548F}' .
+'\x{5490}\x{5491}\x{5492}\x{5493}\x{5494}\x{5495}\x{5496}\x{5497}\x{5498}' .
+'\x{5499}\x{549A}\x{549B}\x{549C}\x{549D}\x{549E}\x{549F}\x{54A0}\x{54A1}' .
+'\x{54A2}\x{54A3}\x{54A4}\x{54A5}\x{54A6}\x{54A7}\x{54A8}\x{54A9}\x{54AA}' .
+'\x{54AB}\x{54AC}\x{54AD}\x{54AE}\x{54AF}\x{54B0}\x{54B1}\x{54B2}\x{54B3}' .
+'\x{54B4}\x{54B6}\x{54B7}\x{54B8}\x{54B9}\x{54BA}\x{54BB}\x{54BC}\x{54BD}' .
+'\x{54BE}\x{54BF}\x{54C0}\x{54C1}\x{54C2}\x{54C3}\x{54C4}\x{54C5}\x{54C6}' .
+'\x{54C7}\x{54C8}\x{54C9}\x{54CA}\x{54CB}\x{54CC}\x{54CD}\x{54CE}\x{54CF}' .
+'\x{54D0}\x{54D1}\x{54D2}\x{54D3}\x{54D4}\x{54D5}\x{54D6}\x{54D7}\x{54D8}' .
+'\x{54D9}\x{54DA}\x{54DB}\x{54DC}\x{54DD}\x{54DE}\x{54DF}\x{54E0}\x{54E1}' .
+'\x{54E2}\x{54E3}\x{54E4}\x{54E5}\x{54E6}\x{54E7}\x{54E8}\x{54E9}\x{54EA}' .
+'\x{54EB}\x{54EC}\x{54ED}\x{54EE}\x{54EF}\x{54F0}\x{54F1}\x{54F2}\x{54F3}' .
+'\x{54F4}\x{54F5}\x{54F7}\x{54F8}\x{54F9}\x{54FA}\x{54FB}\x{54FC}\x{54FD}' .
+'\x{54FE}\x{54FF}\x{5500}\x{5501}\x{5502}\x{5503}\x{5504}\x{5505}\x{5506}' .
+'\x{5507}\x{5508}\x{5509}\x{550A}\x{550B}\x{550C}\x{550D}\x{550E}\x{550F}' .
+'\x{5510}\x{5511}\x{5512}\x{5513}\x{5514}\x{5516}\x{5517}\x{551A}\x{551B}' .
+'\x{551C}\x{551D}\x{551E}\x{551F}\x{5520}\x{5521}\x{5522}\x{5523}\x{5524}' .
+'\x{5525}\x{5526}\x{5527}\x{5528}\x{5529}\x{552A}\x{552B}\x{552C}\x{552D}' .
+'\x{552E}\x{552F}\x{5530}\x{5531}\x{5532}\x{5533}\x{5534}\x{5535}\x{5536}' .
+'\x{5537}\x{5538}\x{5539}\x{553A}\x{553B}\x{553C}\x{553D}\x{553E}\x{553F}' .
+'\x{5540}\x{5541}\x{5542}\x{5543}\x{5544}\x{5545}\x{5546}\x{5548}\x{5549}' .
+'\x{554A}\x{554B}\x{554C}\x{554D}\x{554E}\x{554F}\x{5550}\x{5551}\x{5552}' .
+'\x{5553}\x{5554}\x{5555}\x{5556}\x{5557}\x{5558}\x{5559}\x{555A}\x{555B}' .
+'\x{555C}\x{555D}\x{555E}\x{555F}\x{5561}\x{5562}\x{5563}\x{5564}\x{5565}' .
+'\x{5566}\x{5567}\x{5568}\x{5569}\x{556A}\x{556B}\x{556C}\x{556D}\x{556E}' .
+'\x{556F}\x{5570}\x{5571}\x{5572}\x{5573}\x{5574}\x{5575}\x{5576}\x{5577}' .
+'\x{5578}\x{5579}\x{557B}\x{557C}\x{557D}\x{557E}\x{557F}\x{5580}\x{5581}' .
+'\x{5582}\x{5583}\x{5584}\x{5585}\x{5586}\x{5587}\x{5588}\x{5589}\x{558A}' .
+'\x{558B}\x{558C}\x{558D}\x{558E}\x{558F}\x{5590}\x{5591}\x{5592}\x{5593}' .
+'\x{5594}\x{5595}\x{5596}\x{5597}\x{5598}\x{5599}\x{559A}\x{559B}\x{559C}' .
+'\x{559D}\x{559E}\x{559F}\x{55A0}\x{55A1}\x{55A2}\x{55A3}\x{55A4}\x{55A5}' .
+'\x{55A6}\x{55A7}\x{55A8}\x{55A9}\x{55AA}\x{55AB}\x{55AC}\x{55AD}\x{55AE}' .
+'\x{55AF}\x{55B0}\x{55B1}\x{55B2}\x{55B3}\x{55B4}\x{55B5}\x{55B6}\x{55B7}' .
+'\x{55B8}\x{55B9}\x{55BA}\x{55BB}\x{55BC}\x{55BD}\x{55BE}\x{55BF}\x{55C0}' .
+'\x{55C1}\x{55C2}\x{55C3}\x{55C4}\x{55C5}\x{55C6}\x{55C7}\x{55C8}\x{55C9}' .
+'\x{55CA}\x{55CB}\x{55CC}\x{55CD}\x{55CE}\x{55CF}\x{55D0}\x{55D1}\x{55D2}' .
+'\x{55D3}\x{55D4}\x{55D5}\x{55D6}\x{55D7}\x{55D8}\x{55D9}\x{55DA}\x{55DB}' .
+'\x{55DC}\x{55DD}\x{55DE}\x{55DF}\x{55E1}\x{55E2}\x{55E3}\x{55E4}\x{55E5}' .
+'\x{55E6}\x{55E7}\x{55E8}\x{55E9}\x{55EA}\x{55EB}\x{55EC}\x{55ED}\x{55EE}' .
+'\x{55EF}\x{55F0}\x{55F1}\x{55F2}\x{55F3}\x{55F4}\x{55F5}\x{55F6}\x{55F7}' .
+'\x{55F9}\x{55FA}\x{55FB}\x{55FC}\x{55FD}\x{55FE}\x{55FF}\x{5600}\x{5601}' .
+'\x{5602}\x{5603}\x{5604}\x{5606}\x{5607}\x{5608}\x{5609}\x{560C}\x{560D}' .
+'\x{560E}\x{560F}\x{5610}\x{5611}\x{5612}\x{5613}\x{5614}\x{5615}\x{5616}' .
+'\x{5617}\x{5618}\x{5619}\x{561A}\x{561B}\x{561C}\x{561D}\x{561E}\x{561F}' .
+'\x{5621}\x{5622}\x{5623}\x{5624}\x{5625}\x{5626}\x{5627}\x{5628}\x{5629}' .
+'\x{562A}\x{562C}\x{562D}\x{562E}\x{562F}\x{5630}\x{5631}\x{5632}\x{5633}' .
+'\x{5634}\x{5635}\x{5636}\x{5638}\x{5639}\x{563A}\x{563B}\x{563D}\x{563E}' .
+'\x{563F}\x{5640}\x{5641}\x{5642}\x{5643}\x{5645}\x{5646}\x{5647}\x{5648}' .
+'\x{5649}\x{564A}\x{564C}\x{564D}\x{564E}\x{564F}\x{5650}\x{5652}\x{5653}' .
+'\x{5654}\x{5655}\x{5657}\x{5658}\x{5659}\x{565A}\x{565B}\x{565C}\x{565D}' .
+'\x{565E}\x{5660}\x{5662}\x{5663}\x{5664}\x{5665}\x{5666}\x{5667}\x{5668}' .
+'\x{5669}\x{566A}\x{566B}\x{566C}\x{566D}\x{566E}\x{566F}\x{5670}\x{5671}' .
+'\x{5672}\x{5673}\x{5674}\x{5676}\x{5677}\x{5678}\x{5679}\x{567A}\x{567B}' .
+'\x{567C}\x{567E}\x{567F}\x{5680}\x{5681}\x{5682}\x{5683}\x{5684}\x{5685}' .
+'\x{5686}\x{5687}\x{568A}\x{568C}\x{568D}\x{568E}\x{568F}\x{5690}\x{5691}' .
+'\x{5692}\x{5693}\x{5694}\x{5695}\x{5697}\x{5698}\x{5699}\x{569A}\x{569B}' .
+'\x{569C}\x{569D}\x{569F}\x{56A0}\x{56A1}\x{56A3}\x{56A4}\x{56A5}\x{56A6}' .
+'\x{56A7}\x{56A8}\x{56A9}\x{56AA}\x{56AB}\x{56AC}\x{56AD}\x{56AE}\x{56AF}' .
+'\x{56B0}\x{56B1}\x{56B2}\x{56B3}\x{56B4}\x{56B5}\x{56B6}\x{56B7}\x{56B8}' .
+'\x{56B9}\x{56BB}\x{56BC}\x{56BD}\x{56BE}\x{56BF}\x{56C0}\x{56C1}\x{56C2}' .
+'\x{56C3}\x{56C4}\x{56C5}\x{56C6}\x{56C7}\x{56C8}\x{56C9}\x{56CA}\x{56CB}' .
+'\x{56CC}\x{56CD}\x{56CE}\x{56D0}\x{56D1}\x{56D2}\x{56D3}\x{56D4}\x{56D5}' .
+'\x{56D6}\x{56D7}\x{56D8}\x{56DA}\x{56DB}\x{56DC}\x{56DD}\x{56DE}\x{56DF}' .
+'\x{56E0}\x{56E1}\x{56E2}\x{56E3}\x{56E4}\x{56E5}\x{56E7}\x{56E8}\x{56E9}' .
+'\x{56EA}\x{56EB}\x{56EC}\x{56ED}\x{56EE}\x{56EF}\x{56F0}\x{56F1}\x{56F2}' .
+'\x{56F3}\x{56F4}\x{56F5}\x{56F7}\x{56F9}\x{56FA}\x{56FD}\x{56FE}\x{56FF}' .
+'\x{5700}\x{5701}\x{5702}\x{5703}\x{5704}\x{5706}\x{5707}\x{5708}\x{5709}' .
+'\x{570A}\x{570B}\x{570C}\x{570D}\x{570E}\x{570F}\x{5710}\x{5712}\x{5713}' .
+'\x{5714}\x{5715}\x{5716}\x{5718}\x{5719}\x{571A}\x{571B}\x{571C}\x{571D}' .
+'\x{571E}\x{571F}\x{5720}\x{5722}\x{5723}\x{5725}\x{5726}\x{5727}\x{5728}' .
+'\x{5729}\x{572A}\x{572B}\x{572C}\x{572D}\x{572E}\x{572F}\x{5730}\x{5731}' .
+'\x{5732}\x{5733}\x{5734}\x{5735}\x{5736}\x{5737}\x{5738}\x{5739}\x{573A}' .
+'\x{573B}\x{573C}\x{573E}\x{573F}\x{5740}\x{5741}\x{5742}\x{5744}\x{5745}' .
+'\x{5746}\x{5747}\x{5749}\x{574A}\x{574B}\x{574C}\x{574D}\x{574E}\x{574F}' .
+'\x{5750}\x{5751}\x{5752}\x{5753}\x{5754}\x{5757}\x{5759}\x{575A}\x{575B}' .
+'\x{575C}\x{575D}\x{575E}\x{575F}\x{5760}\x{5761}\x{5762}\x{5764}\x{5765}' .
+'\x{5766}\x{5767}\x{5768}\x{5769}\x{576A}\x{576B}\x{576C}\x{576D}\x{576F}' .
+'\x{5770}\x{5771}\x{5772}\x{5773}\x{5774}\x{5775}\x{5776}\x{5777}\x{5779}' .
+'\x{577A}\x{577B}\x{577C}\x{577D}\x{577E}\x{577F}\x{5780}\x{5782}\x{5783}' .
+'\x{5784}\x{5785}\x{5786}\x{5788}\x{5789}\x{578A}\x{578B}\x{578C}\x{578D}' .
+'\x{578E}\x{578F}\x{5790}\x{5791}\x{5792}\x{5793}\x{5794}\x{5795}\x{5797}' .
+'\x{5798}\x{5799}\x{579A}\x{579B}\x{579C}\x{579D}\x{579E}\x{579F}\x{57A0}' .
+'\x{57A1}\x{57A2}\x{57A3}\x{57A4}\x{57A5}\x{57A6}\x{57A7}\x{57A9}\x{57AA}' .
+'\x{57AB}\x{57AC}\x{57AD}\x{57AE}\x{57AF}\x{57B0}\x{57B1}\x{57B2}\x{57B3}' .
+'\x{57B4}\x{57B5}\x{57B6}\x{57B7}\x{57B8}\x{57B9}\x{57BA}\x{57BB}\x{57BC}' .
+'\x{57BD}\x{57BE}\x{57BF}\x{57C0}\x{57C1}\x{57C2}\x{57C3}\x{57C4}\x{57C5}' .
+'\x{57C6}\x{57C7}\x{57C8}\x{57C9}\x{57CB}\x{57CC}\x{57CD}\x{57CE}\x{57CF}' .
+'\x{57D0}\x{57D2}\x{57D3}\x{57D4}\x{57D5}\x{57D6}\x{57D8}\x{57D9}\x{57DA}' .
+'\x{57DC}\x{57DD}\x{57DF}\x{57E0}\x{57E1}\x{57E2}\x{57E3}\x{57E4}\x{57E5}' .
+'\x{57E6}\x{57E7}\x{57E8}\x{57E9}\x{57EA}\x{57EB}\x{57EC}\x{57ED}\x{57EE}' .
+'\x{57EF}\x{57F0}\x{57F1}\x{57F2}\x{57F3}\x{57F4}\x{57F5}\x{57F6}\x{57F7}' .
+'\x{57F8}\x{57F9}\x{57FA}\x{57FB}\x{57FC}\x{57FD}\x{57FE}\x{57FF}\x{5800}' .
+'\x{5801}\x{5802}\x{5803}\x{5804}\x{5805}\x{5806}\x{5807}\x{5808}\x{5809}' .
+'\x{580A}\x{580B}\x{580C}\x{580D}\x{580E}\x{580F}\x{5810}\x{5811}\x{5812}' .
+'\x{5813}\x{5814}\x{5815}\x{5816}\x{5819}\x{581A}\x{581B}\x{581C}\x{581D}' .
+'\x{581E}\x{581F}\x{5820}\x{5821}\x{5822}\x{5823}\x{5824}\x{5825}\x{5826}' .
+'\x{5827}\x{5828}\x{5829}\x{582A}\x{582B}\x{582C}\x{582D}\x{582E}\x{582F}' .
+'\x{5830}\x{5831}\x{5832}\x{5833}\x{5834}\x{5835}\x{5836}\x{5837}\x{5838}' .
+'\x{5839}\x{583A}\x{583B}\x{583C}\x{583D}\x{583E}\x{583F}\x{5840}\x{5842}' .
+'\x{5843}\x{5844}\x{5845}\x{5846}\x{5847}\x{5848}\x{5849}\x{584A}\x{584B}' .
+'\x{584C}\x{584D}\x{584E}\x{584F}\x{5851}\x{5852}\x{5853}\x{5854}\x{5855}' .
+'\x{5857}\x{5858}\x{5859}\x{585A}\x{585B}\x{585C}\x{585D}\x{585E}\x{585F}' .
+'\x{5861}\x{5862}\x{5863}\x{5864}\x{5865}\x{5868}\x{5869}\x{586A}\x{586B}' .
+'\x{586C}\x{586D}\x{586E}\x{586F}\x{5870}\x{5871}\x{5872}\x{5873}\x{5874}' .
+'\x{5875}\x{5876}\x{5878}\x{5879}\x{587A}\x{587B}\x{587C}\x{587D}\x{587E}' .
+'\x{587F}\x{5880}\x{5881}\x{5882}\x{5883}\x{5884}\x{5885}\x{5886}\x{5887}' .
+'\x{5888}\x{5889}\x{588A}\x{588B}\x{588C}\x{588D}\x{588E}\x{588F}\x{5890}' .
+'\x{5891}\x{5892}\x{5893}\x{5894}\x{5896}\x{5897}\x{5898}\x{5899}\x{589A}' .
+'\x{589B}\x{589C}\x{589D}\x{589E}\x{589F}\x{58A0}\x{58A1}\x{58A2}\x{58A3}' .
+'\x{58A4}\x{58A5}\x{58A6}\x{58A7}\x{58A8}\x{58A9}\x{58AB}\x{58AC}\x{58AD}' .
+'\x{58AE}\x{58AF}\x{58B0}\x{58B1}\x{58B2}\x{58B3}\x{58B4}\x{58B7}\x{58B8}' .
+'\x{58B9}\x{58BA}\x{58BB}\x{58BC}\x{58BD}\x{58BE}\x{58BF}\x{58C1}\x{58C2}' .
+'\x{58C5}\x{58C6}\x{58C7}\x{58C8}\x{58C9}\x{58CA}\x{58CB}\x{58CE}\x{58CF}' .
+'\x{58D1}\x{58D2}\x{58D3}\x{58D4}\x{58D5}\x{58D6}\x{58D7}\x{58D8}\x{58D9}' .
+'\x{58DA}\x{58DB}\x{58DD}\x{58DE}\x{58DF}\x{58E0}\x{58E2}\x{58E3}\x{58E4}' .
+'\x{58E5}\x{58E7}\x{58E8}\x{58E9}\x{58EA}\x{58EB}\x{58EC}\x{58ED}\x{58EE}' .
+'\x{58EF}\x{58F0}\x{58F1}\x{58F2}\x{58F3}\x{58F4}\x{58F6}\x{58F7}\x{58F8}' .
+'\x{58F9}\x{58FA}\x{58FB}\x{58FC}\x{58FD}\x{58FE}\x{58FF}\x{5900}\x{5902}' .
+'\x{5903}\x{5904}\x{5906}\x{5907}\x{5909}\x{590A}\x{590B}\x{590C}\x{590D}' .
+'\x{590E}\x{590F}\x{5910}\x{5912}\x{5914}\x{5915}\x{5916}\x{5917}\x{5918}' .
+'\x{5919}\x{591A}\x{591B}\x{591C}\x{591D}\x{591E}\x{591F}\x{5920}\x{5921}' .
+'\x{5922}\x{5924}\x{5925}\x{5926}\x{5927}\x{5928}\x{5929}\x{592A}\x{592B}' .
+'\x{592C}\x{592D}\x{592E}\x{592F}\x{5930}\x{5931}\x{5932}\x{5934}\x{5935}' .
+'\x{5937}\x{5938}\x{5939}\x{593A}\x{593B}\x{593C}\x{593D}\x{593E}\x{593F}' .
+'\x{5940}\x{5941}\x{5942}\x{5943}\x{5944}\x{5945}\x{5946}\x{5947}\x{5948}' .
+'\x{5949}\x{594A}\x{594B}\x{594C}\x{594D}\x{594E}\x{594F}\x{5950}\x{5951}' .
+'\x{5952}\x{5953}\x{5954}\x{5955}\x{5956}\x{5957}\x{5958}\x{595A}\x{595C}' .
+'\x{595D}\x{595E}\x{595F}\x{5960}\x{5961}\x{5962}\x{5963}\x{5964}\x{5965}' .
+'\x{5966}\x{5967}\x{5968}\x{5969}\x{596A}\x{596B}\x{596C}\x{596D}\x{596E}' .
+'\x{596F}\x{5970}\x{5971}\x{5972}\x{5973}\x{5974}\x{5975}\x{5976}\x{5977}' .
+'\x{5978}\x{5979}\x{597A}\x{597B}\x{597C}\x{597D}\x{597E}\x{597F}\x{5980}' .
+'\x{5981}\x{5982}\x{5983}\x{5984}\x{5985}\x{5986}\x{5987}\x{5988}\x{5989}' .
+'\x{598A}\x{598B}\x{598C}\x{598D}\x{598E}\x{598F}\x{5990}\x{5991}\x{5992}' .
+'\x{5993}\x{5994}\x{5995}\x{5996}\x{5997}\x{5998}\x{5999}\x{599A}\x{599C}' .
+'\x{599D}\x{599E}\x{599F}\x{59A0}\x{59A1}\x{59A2}\x{59A3}\x{59A4}\x{59A5}' .
+'\x{59A6}\x{59A7}\x{59A8}\x{59A9}\x{59AA}\x{59AB}\x{59AC}\x{59AD}\x{59AE}' .
+'\x{59AF}\x{59B0}\x{59B1}\x{59B2}\x{59B3}\x{59B4}\x{59B5}\x{59B6}\x{59B8}' .
+'\x{59B9}\x{59BA}\x{59BB}\x{59BC}\x{59BD}\x{59BE}\x{59BF}\x{59C0}\x{59C1}' .
+'\x{59C2}\x{59C3}\x{59C4}\x{59C5}\x{59C6}\x{59C7}\x{59C8}\x{59C9}\x{59CA}' .
+'\x{59CB}\x{59CC}\x{59CD}\x{59CE}\x{59CF}\x{59D0}\x{59D1}\x{59D2}\x{59D3}' .
+'\x{59D4}\x{59D5}\x{59D6}\x{59D7}\x{59D8}\x{59D9}\x{59DA}\x{59DB}\x{59DC}' .
+'\x{59DD}\x{59DE}\x{59DF}\x{59E0}\x{59E1}\x{59E2}\x{59E3}\x{59E4}\x{59E5}' .
+'\x{59E6}\x{59E8}\x{59E9}\x{59EA}\x{59EB}\x{59EC}\x{59ED}\x{59EE}\x{59EF}' .
+'\x{59F0}\x{59F1}\x{59F2}\x{59F3}\x{59F4}\x{59F5}\x{59F6}\x{59F7}\x{59F8}' .
+'\x{59F9}\x{59FA}\x{59FB}\x{59FC}\x{59FD}\x{59FE}\x{59FF}\x{5A00}\x{5A01}' .
+'\x{5A02}\x{5A03}\x{5A04}\x{5A05}\x{5A06}\x{5A07}\x{5A08}\x{5A09}\x{5A0A}' .
+'\x{5A0B}\x{5A0C}\x{5A0D}\x{5A0E}\x{5A0F}\x{5A10}\x{5A11}\x{5A12}\x{5A13}' .
+'\x{5A14}\x{5A15}\x{5A16}\x{5A17}\x{5A18}\x{5A19}\x{5A1A}\x{5A1B}\x{5A1C}' .
+'\x{5A1D}\x{5A1E}\x{5A1F}\x{5A20}\x{5A21}\x{5A22}\x{5A23}\x{5A25}\x{5A27}' .
+'\x{5A28}\x{5A29}\x{5A2A}\x{5A2B}\x{5A2D}\x{5A2E}\x{5A2F}\x{5A31}\x{5A32}' .
+'\x{5A33}\x{5A34}\x{5A35}\x{5A36}\x{5A37}\x{5A38}\x{5A39}\x{5A3A}\x{5A3B}' .
+'\x{5A3C}\x{5A3D}\x{5A3E}\x{5A3F}\x{5A40}\x{5A41}\x{5A42}\x{5A43}\x{5A44}' .
+'\x{5A45}\x{5A46}\x{5A47}\x{5A48}\x{5A49}\x{5A4A}\x{5A4B}\x{5A4C}\x{5A4D}' .
+'\x{5A4E}\x{5A4F}\x{5A50}\x{5A51}\x{5A52}\x{5A53}\x{5A55}\x{5A56}\x{5A57}' .
+'\x{5A58}\x{5A5A}\x{5A5B}\x{5A5C}\x{5A5D}\x{5A5E}\x{5A5F}\x{5A60}\x{5A61}' .
+'\x{5A62}\x{5A63}\x{5A64}\x{5A65}\x{5A66}\x{5A67}\x{5A68}\x{5A69}\x{5A6A}' .
+'\x{5A6B}\x{5A6C}\x{5A6D}\x{5A6E}\x{5A70}\x{5A72}\x{5A73}\x{5A74}\x{5A75}' .
+'\x{5A76}\x{5A77}\x{5A78}\x{5A79}\x{5A7A}\x{5A7B}\x{5A7C}\x{5A7D}\x{5A7E}' .
+'\x{5A7F}\x{5A80}\x{5A81}\x{5A82}\x{5A83}\x{5A84}\x{5A85}\x{5A86}\x{5A88}' .
+'\x{5A89}\x{5A8A}\x{5A8B}\x{5A8C}\x{5A8E}\x{5A8F}\x{5A90}\x{5A91}\x{5A92}' .
+'\x{5A93}\x{5A94}\x{5A95}\x{5A96}\x{5A97}\x{5A98}\x{5A99}\x{5A9A}\x{5A9B}' .
+'\x{5A9C}\x{5A9D}\x{5A9E}\x{5A9F}\x{5AA0}\x{5AA1}\x{5AA2}\x{5AA3}\x{5AA4}' .
+'\x{5AA5}\x{5AA6}\x{5AA7}\x{5AA8}\x{5AA9}\x{5AAA}\x{5AAC}\x{5AAD}\x{5AAE}' .
+'\x{5AAF}\x{5AB0}\x{5AB1}\x{5AB2}\x{5AB3}\x{5AB4}\x{5AB5}\x{5AB6}\x{5AB7}' .
+'\x{5AB8}\x{5AB9}\x{5ABA}\x{5ABB}\x{5ABC}\x{5ABD}\x{5ABE}\x{5ABF}\x{5AC0}' .
+'\x{5AC1}\x{5AC2}\x{5AC3}\x{5AC4}\x{5AC5}\x{5AC6}\x{5AC7}\x{5AC8}\x{5AC9}' .
+'\x{5ACA}\x{5ACB}\x{5ACC}\x{5ACD}\x{5ACE}\x{5ACF}\x{5AD1}\x{5AD2}\x{5AD4}' .
+'\x{5AD5}\x{5AD6}\x{5AD7}\x{5AD8}\x{5AD9}\x{5ADA}\x{5ADB}\x{5ADC}\x{5ADD}' .
+'\x{5ADE}\x{5ADF}\x{5AE0}\x{5AE1}\x{5AE2}\x{5AE3}\x{5AE4}\x{5AE5}\x{5AE6}' .
+'\x{5AE7}\x{5AE8}\x{5AE9}\x{5AEA}\x{5AEB}\x{5AEC}\x{5AED}\x{5AEE}\x{5AF1}' .
+'\x{5AF2}\x{5AF3}\x{5AF4}\x{5AF5}\x{5AF6}\x{5AF7}\x{5AF8}\x{5AF9}\x{5AFA}' .
+'\x{5AFB}\x{5AFC}\x{5AFD}\x{5AFE}\x{5AFF}\x{5B00}\x{5B01}\x{5B02}\x{5B03}' .
+'\x{5B04}\x{5B05}\x{5B06}\x{5B07}\x{5B08}\x{5B09}\x{5B0B}\x{5B0C}\x{5B0E}' .
+'\x{5B0F}\x{5B10}\x{5B11}\x{5B12}\x{5B13}\x{5B14}\x{5B15}\x{5B16}\x{5B17}' .
+'\x{5B18}\x{5B19}\x{5B1A}\x{5B1B}\x{5B1C}\x{5B1D}\x{5B1E}\x{5B1F}\x{5B20}' .
+'\x{5B21}\x{5B22}\x{5B23}\x{5B24}\x{5B25}\x{5B26}\x{5B27}\x{5B28}\x{5B29}' .
+'\x{5B2A}\x{5B2B}\x{5B2C}\x{5B2D}\x{5B2E}\x{5B2F}\x{5B30}\x{5B31}\x{5B32}' .
+'\x{5B33}\x{5B34}\x{5B35}\x{5B36}\x{5B37}\x{5B38}\x{5B3A}\x{5B3B}\x{5B3C}' .
+'\x{5B3D}\x{5B3E}\x{5B3F}\x{5B40}\x{5B41}\x{5B42}\x{5B43}\x{5B44}\x{5B45}' .
+'\x{5B47}\x{5B48}\x{5B49}\x{5B4A}\x{5B4B}\x{5B4C}\x{5B4D}\x{5B4E}\x{5B50}' .
+'\x{5B51}\x{5B53}\x{5B54}\x{5B55}\x{5B56}\x{5B57}\x{5B58}\x{5B59}\x{5B5A}' .
+'\x{5B5B}\x{5B5C}\x{5B5D}\x{5B5E}\x{5B5F}\x{5B62}\x{5B63}\x{5B64}\x{5B65}' .
+'\x{5B66}\x{5B67}\x{5B68}\x{5B69}\x{5B6A}\x{5B6B}\x{5B6C}\x{5B6D}\x{5B6E}' .
+'\x{5B70}\x{5B71}\x{5B72}\x{5B73}\x{5B74}\x{5B75}\x{5B76}\x{5B77}\x{5B78}' .
+'\x{5B7A}\x{5B7B}\x{5B7C}\x{5B7D}\x{5B7F}\x{5B80}\x{5B81}\x{5B82}\x{5B83}' .
+'\x{5B84}\x{5B85}\x{5B87}\x{5B88}\x{5B89}\x{5B8A}\x{5B8B}\x{5B8C}\x{5B8D}' .
+'\x{5B8E}\x{5B8F}\x{5B91}\x{5B92}\x{5B93}\x{5B94}\x{5B95}\x{5B96}\x{5B97}' .
+'\x{5B98}\x{5B99}\x{5B9A}\x{5B9B}\x{5B9C}\x{5B9D}\x{5B9E}\x{5B9F}\x{5BA0}' .
+'\x{5BA1}\x{5BA2}\x{5BA3}\x{5BA4}\x{5BA5}\x{5BA6}\x{5BA7}\x{5BA8}\x{5BAA}' .
+'\x{5BAB}\x{5BAC}\x{5BAD}\x{5BAE}\x{5BAF}\x{5BB0}\x{5BB1}\x{5BB3}\x{5BB4}' .
+'\x{5BB5}\x{5BB6}\x{5BB8}\x{5BB9}\x{5BBA}\x{5BBB}\x{5BBD}\x{5BBE}\x{5BBF}' .
+'\x{5BC0}\x{5BC1}\x{5BC2}\x{5BC3}\x{5BC4}\x{5BC5}\x{5BC6}\x{5BC7}\x{5BCA}' .
+'\x{5BCB}\x{5BCC}\x{5BCD}\x{5BCE}\x{5BCF}\x{5BD0}\x{5BD1}\x{5BD2}\x{5BD3}' .
+'\x{5BD4}\x{5BD5}\x{5BD6}\x{5BD8}\x{5BD9}\x{5BDB}\x{5BDC}\x{5BDD}\x{5BDE}' .
+'\x{5BDF}\x{5BE0}\x{5BE1}\x{5BE2}\x{5BE3}\x{5BE4}\x{5BE5}\x{5BE6}\x{5BE7}' .
+'\x{5BE8}\x{5BE9}\x{5BEA}\x{5BEB}\x{5BEC}\x{5BED}\x{5BEE}\x{5BEF}\x{5BF0}' .
+'\x{5BF1}\x{5BF2}\x{5BF3}\x{5BF4}\x{5BF5}\x{5BF6}\x{5BF7}\x{5BF8}\x{5BF9}' .
+'\x{5BFA}\x{5BFB}\x{5BFC}\x{5BFD}\x{5BFF}\x{5C01}\x{5C03}\x{5C04}\x{5C05}' .
+'\x{5C06}\x{5C07}\x{5C08}\x{5C09}\x{5C0A}\x{5C0B}\x{5C0C}\x{5C0D}\x{5C0E}' .
+'\x{5C0F}\x{5C10}\x{5C11}\x{5C12}\x{5C13}\x{5C14}\x{5C15}\x{5C16}\x{5C17}' .
+'\x{5C18}\x{5C19}\x{5C1A}\x{5C1C}\x{5C1D}\x{5C1E}\x{5C1F}\x{5C20}\x{5C21}' .
+'\x{5C22}\x{5C24}\x{5C25}\x{5C27}\x{5C28}\x{5C2A}\x{5C2B}\x{5C2C}\x{5C2D}' .
+'\x{5C2E}\x{5C2F}\x{5C30}\x{5C31}\x{5C32}\x{5C33}\x{5C34}\x{5C35}\x{5C37}' .
+'\x{5C38}\x{5C39}\x{5C3A}\x{5C3B}\x{5C3C}\x{5C3D}\x{5C3E}\x{5C3F}\x{5C40}' .
+'\x{5C41}\x{5C42}\x{5C43}\x{5C44}\x{5C45}\x{5C46}\x{5C47}\x{5C48}\x{5C49}' .
+'\x{5C4A}\x{5C4B}\x{5C4C}\x{5C4D}\x{5C4E}\x{5C4F}\x{5C50}\x{5C51}\x{5C52}' .
+'\x{5C53}\x{5C54}\x{5C55}\x{5C56}\x{5C57}\x{5C58}\x{5C59}\x{5C5B}\x{5C5C}' .
+'\x{5C5D}\x{5C5E}\x{5C5F}\x{5C60}\x{5C61}\x{5C62}\x{5C63}\x{5C64}\x{5C65}' .
+'\x{5C66}\x{5C67}\x{5C68}\x{5C69}\x{5C6A}\x{5C6B}\x{5C6C}\x{5C6D}\x{5C6E}' .
+'\x{5C6F}\x{5C70}\x{5C71}\x{5C72}\x{5C73}\x{5C74}\x{5C75}\x{5C76}\x{5C77}' .
+'\x{5C78}\x{5C79}\x{5C7A}\x{5C7B}\x{5C7C}\x{5C7D}\x{5C7E}\x{5C7F}\x{5C80}' .
+'\x{5C81}\x{5C82}\x{5C83}\x{5C84}\x{5C86}\x{5C87}\x{5C88}\x{5C89}\x{5C8A}' .
+'\x{5C8B}\x{5C8C}\x{5C8D}\x{5C8E}\x{5C8F}\x{5C90}\x{5C91}\x{5C92}\x{5C93}' .
+'\x{5C94}\x{5C95}\x{5C96}\x{5C97}\x{5C98}\x{5C99}\x{5C9A}\x{5C9B}\x{5C9C}' .
+'\x{5C9D}\x{5C9E}\x{5C9F}\x{5CA0}\x{5CA1}\x{5CA2}\x{5CA3}\x{5CA4}\x{5CA5}' .
+'\x{5CA6}\x{5CA7}\x{5CA8}\x{5CA9}\x{5CAA}\x{5CAB}\x{5CAC}\x{5CAD}\x{5CAE}' .
+'\x{5CAF}\x{5CB0}\x{5CB1}\x{5CB2}\x{5CB3}\x{5CB5}\x{5CB6}\x{5CB7}\x{5CB8}' .
+'\x{5CBA}\x{5CBB}\x{5CBC}\x{5CBD}\x{5CBE}\x{5CBF}\x{5CC1}\x{5CC2}\x{5CC3}' .
+'\x{5CC4}\x{5CC5}\x{5CC6}\x{5CC7}\x{5CC8}\x{5CC9}\x{5CCA}\x{5CCB}\x{5CCC}' .
+'\x{5CCD}\x{5CCE}\x{5CCF}\x{5CD0}\x{5CD1}\x{5CD2}\x{5CD3}\x{5CD4}\x{5CD6}' .
+'\x{5CD7}\x{5CD8}\x{5CD9}\x{5CDA}\x{5CDB}\x{5CDC}\x{5CDE}\x{5CDF}\x{5CE0}' .
+'\x{5CE1}\x{5CE2}\x{5CE3}\x{5CE4}\x{5CE5}\x{5CE6}\x{5CE7}\x{5CE8}\x{5CE9}' .
+'\x{5CEA}\x{5CEB}\x{5CEC}\x{5CED}\x{5CEE}\x{5CEF}\x{5CF0}\x{5CF1}\x{5CF2}' .
+'\x{5CF3}\x{5CF4}\x{5CF6}\x{5CF7}\x{5CF8}\x{5CF9}\x{5CFA}\x{5CFB}\x{5CFC}' .
+'\x{5CFD}\x{5CFE}\x{5CFF}\x{5D00}\x{5D01}\x{5D02}\x{5D03}\x{5D04}\x{5D05}' .
+'\x{5D06}\x{5D07}\x{5D08}\x{5D09}\x{5D0A}\x{5D0B}\x{5D0C}\x{5D0D}\x{5D0E}' .
+'\x{5D0F}\x{5D10}\x{5D11}\x{5D12}\x{5D13}\x{5D14}\x{5D15}\x{5D16}\x{5D17}' .
+'\x{5D18}\x{5D19}\x{5D1A}\x{5D1B}\x{5D1C}\x{5D1D}\x{5D1E}\x{5D1F}\x{5D20}' .
+'\x{5D21}\x{5D22}\x{5D23}\x{5D24}\x{5D25}\x{5D26}\x{5D27}\x{5D28}\x{5D29}' .
+'\x{5D2A}\x{5D2C}\x{5D2D}\x{5D2E}\x{5D30}\x{5D31}\x{5D32}\x{5D33}\x{5D34}' .
+'\x{5D35}\x{5D36}\x{5D37}\x{5D38}\x{5D39}\x{5D3A}\x{5D3C}\x{5D3D}\x{5D3E}' .
+'\x{5D3F}\x{5D40}\x{5D41}\x{5D42}\x{5D43}\x{5D44}\x{5D45}\x{5D46}\x{5D47}' .
+'\x{5D48}\x{5D49}\x{5D4A}\x{5D4B}\x{5D4C}\x{5D4D}\x{5D4E}\x{5D4F}\x{5D50}' .
+'\x{5D51}\x{5D52}\x{5D54}\x{5D55}\x{5D56}\x{5D58}\x{5D59}\x{5D5A}\x{5D5B}' .
+'\x{5D5D}\x{5D5E}\x{5D5F}\x{5D61}\x{5D62}\x{5D63}\x{5D64}\x{5D65}\x{5D66}' .
+'\x{5D67}\x{5D68}\x{5D69}\x{5D6A}\x{5D6B}\x{5D6C}\x{5D6D}\x{5D6E}\x{5D6F}' .
+'\x{5D70}\x{5D71}\x{5D72}\x{5D73}\x{5D74}\x{5D75}\x{5D76}\x{5D77}\x{5D78}' .
+'\x{5D79}\x{5D7A}\x{5D7B}\x{5D7C}\x{5D7D}\x{5D7E}\x{5D7F}\x{5D80}\x{5D81}' .
+'\x{5D82}\x{5D84}\x{5D85}\x{5D86}\x{5D87}\x{5D88}\x{5D89}\x{5D8A}\x{5D8B}' .
+'\x{5D8C}\x{5D8D}\x{5D8E}\x{5D8F}\x{5D90}\x{5D91}\x{5D92}\x{5D93}\x{5D94}' .
+'\x{5D95}\x{5D97}\x{5D98}\x{5D99}\x{5D9A}\x{5D9B}\x{5D9C}\x{5D9D}\x{5D9E}' .
+'\x{5D9F}\x{5DA0}\x{5DA1}\x{5DA2}\x{5DA5}\x{5DA6}\x{5DA7}\x{5DA8}\x{5DA9}' .
+'\x{5DAA}\x{5DAC}\x{5DAD}\x{5DAE}\x{5DAF}\x{5DB0}\x{5DB1}\x{5DB2}\x{5DB4}' .
+'\x{5DB5}\x{5DB6}\x{5DB7}\x{5DB8}\x{5DBA}\x{5DBB}\x{5DBC}\x{5DBD}\x{5DBE}' .
+'\x{5DBF}\x{5DC0}\x{5DC1}\x{5DC2}\x{5DC3}\x{5DC5}\x{5DC6}\x{5DC7}\x{5DC8}' .
+'\x{5DC9}\x{5DCA}\x{5DCB}\x{5DCC}\x{5DCD}\x{5DCE}\x{5DCF}\x{5DD0}\x{5DD1}' .
+'\x{5DD2}\x{5DD3}\x{5DD4}\x{5DD5}\x{5DD6}\x{5DD8}\x{5DD9}\x{5DDB}\x{5DDD}' .
+'\x{5DDE}\x{5DDF}\x{5DE0}\x{5DE1}\x{5DE2}\x{5DE3}\x{5DE4}\x{5DE5}\x{5DE6}' .
+'\x{5DE7}\x{5DE8}\x{5DE9}\x{5DEA}\x{5DEB}\x{5DEC}\x{5DED}\x{5DEE}\x{5DEF}' .
+'\x{5DF0}\x{5DF1}\x{5DF2}\x{5DF3}\x{5DF4}\x{5DF5}\x{5DF7}\x{5DF8}\x{5DF9}' .
+'\x{5DFA}\x{5DFB}\x{5DFC}\x{5DFD}\x{5DFE}\x{5DFF}\x{5E00}\x{5E01}\x{5E02}' .
+'\x{5E03}\x{5E04}\x{5E05}\x{5E06}\x{5E07}\x{5E08}\x{5E09}\x{5E0A}\x{5E0B}' .
+'\x{5E0C}\x{5E0D}\x{5E0E}\x{5E0F}\x{5E10}\x{5E11}\x{5E13}\x{5E14}\x{5E15}' .
+'\x{5E16}\x{5E17}\x{5E18}\x{5E19}\x{5E1A}\x{5E1B}\x{5E1C}\x{5E1D}\x{5E1E}' .
+'\x{5E1F}\x{5E20}\x{5E21}\x{5E22}\x{5E23}\x{5E24}\x{5E25}\x{5E26}\x{5E27}' .
+'\x{5E28}\x{5E29}\x{5E2A}\x{5E2B}\x{5E2C}\x{5E2D}\x{5E2E}\x{5E2F}\x{5E30}' .
+'\x{5E31}\x{5E32}\x{5E33}\x{5E34}\x{5E35}\x{5E36}\x{5E37}\x{5E38}\x{5E39}' .
+'\x{5E3A}\x{5E3B}\x{5E3C}\x{5E3D}\x{5E3E}\x{5E40}\x{5E41}\x{5E42}\x{5E43}' .
+'\x{5E44}\x{5E45}\x{5E46}\x{5E47}\x{5E49}\x{5E4A}\x{5E4B}\x{5E4C}\x{5E4D}' .
+'\x{5E4E}\x{5E4F}\x{5E50}\x{5E52}\x{5E53}\x{5E54}\x{5E55}\x{5E56}\x{5E57}' .
+'\x{5E58}\x{5E59}\x{5E5A}\x{5E5B}\x{5E5C}\x{5E5D}\x{5E5E}\x{5E5F}\x{5E60}' .
+'\x{5E61}\x{5E62}\x{5E63}\x{5E64}\x{5E65}\x{5E66}\x{5E67}\x{5E68}\x{5E69}' .
+'\x{5E6A}\x{5E6B}\x{5E6C}\x{5E6D}\x{5E6E}\x{5E6F}\x{5E70}\x{5E71}\x{5E72}' .
+'\x{5E73}\x{5E74}\x{5E75}\x{5E76}\x{5E77}\x{5E78}\x{5E79}\x{5E7A}\x{5E7B}' .
+'\x{5E7C}\x{5E7D}\x{5E7E}\x{5E7F}\x{5E80}\x{5E81}\x{5E82}\x{5E83}\x{5E84}' .
+'\x{5E85}\x{5E86}\x{5E87}\x{5E88}\x{5E89}\x{5E8A}\x{5E8B}\x{5E8C}\x{5E8D}' .
+'\x{5E8E}\x{5E8F}\x{5E90}\x{5E91}\x{5E93}\x{5E94}\x{5E95}\x{5E96}\x{5E97}' .
+'\x{5E98}\x{5E99}\x{5E9A}\x{5E9B}\x{5E9C}\x{5E9D}\x{5E9E}\x{5E9F}\x{5EA0}' .
+'\x{5EA1}\x{5EA2}\x{5EA3}\x{5EA4}\x{5EA5}\x{5EA6}\x{5EA7}\x{5EA8}\x{5EA9}' .
+'\x{5EAA}\x{5EAB}\x{5EAC}\x{5EAD}\x{5EAE}\x{5EAF}\x{5EB0}\x{5EB1}\x{5EB2}' .
+'\x{5EB3}\x{5EB4}\x{5EB5}\x{5EB6}\x{5EB7}\x{5EB8}\x{5EB9}\x{5EBB}\x{5EBC}' .
+'\x{5EBD}\x{5EBE}\x{5EBF}\x{5EC1}\x{5EC2}\x{5EC3}\x{5EC4}\x{5EC5}\x{5EC6}' .
+'\x{5EC7}\x{5EC8}\x{5EC9}\x{5ECA}\x{5ECB}\x{5ECC}\x{5ECD}\x{5ECE}\x{5ECF}' .
+'\x{5ED0}\x{5ED1}\x{5ED2}\x{5ED3}\x{5ED4}\x{5ED5}\x{5ED6}\x{5ED7}\x{5ED8}' .
+'\x{5ED9}\x{5EDA}\x{5EDB}\x{5EDC}\x{5EDD}\x{5EDE}\x{5EDF}\x{5EE0}\x{5EE1}' .
+'\x{5EE2}\x{5EE3}\x{5EE4}\x{5EE5}\x{5EE6}\x{5EE7}\x{5EE8}\x{5EE9}\x{5EEA}' .
+'\x{5EEC}\x{5EED}\x{5EEE}\x{5EEF}\x{5EF0}\x{5EF1}\x{5EF2}\x{5EF3}\x{5EF4}' .
+'\x{5EF5}\x{5EF6}\x{5EF7}\x{5EF8}\x{5EFA}\x{5EFB}\x{5EFC}\x{5EFD}\x{5EFE}' .
+'\x{5EFF}\x{5F00}\x{5F01}\x{5F02}\x{5F03}\x{5F04}\x{5F05}\x{5F06}\x{5F07}' .
+'\x{5F08}\x{5F0A}\x{5F0B}\x{5F0C}\x{5F0D}\x{5F0F}\x{5F11}\x{5F12}\x{5F13}' .
+'\x{5F14}\x{5F15}\x{5F16}\x{5F17}\x{5F18}\x{5F19}\x{5F1A}\x{5F1B}\x{5F1C}' .
+'\x{5F1D}\x{5F1E}\x{5F1F}\x{5F20}\x{5F21}\x{5F22}\x{5F23}\x{5F24}\x{5F25}' .
+'\x{5F26}\x{5F27}\x{5F28}\x{5F29}\x{5F2A}\x{5F2B}\x{5F2C}\x{5F2D}\x{5F2E}' .
+'\x{5F2F}\x{5F30}\x{5F31}\x{5F32}\x{5F33}\x{5F34}\x{5F35}\x{5F36}\x{5F37}' .
+'\x{5F38}\x{5F39}\x{5F3A}\x{5F3C}\x{5F3E}\x{5F3F}\x{5F40}\x{5F41}\x{5F42}' .
+'\x{5F43}\x{5F44}\x{5F45}\x{5F46}\x{5F47}\x{5F48}\x{5F49}\x{5F4A}\x{5F4B}' .
+'\x{5F4C}\x{5F4D}\x{5F4E}\x{5F4F}\x{5F50}\x{5F51}\x{5F52}\x{5F53}\x{5F54}' .
+'\x{5F55}\x{5F56}\x{5F57}\x{5F58}\x{5F59}\x{5F5A}\x{5F5B}\x{5F5C}\x{5F5D}' .
+'\x{5F5E}\x{5F5F}\x{5F60}\x{5F61}\x{5F62}\x{5F63}\x{5F64}\x{5F65}\x{5F66}' .
+'\x{5F67}\x{5F68}\x{5F69}\x{5F6A}\x{5F6B}\x{5F6C}\x{5F6D}\x{5F6E}\x{5F6F}' .
+'\x{5F70}\x{5F71}\x{5F72}\x{5F73}\x{5F74}\x{5F75}\x{5F76}\x{5F77}\x{5F78}' .
+'\x{5F79}\x{5F7A}\x{5F7B}\x{5F7C}\x{5F7D}\x{5F7E}\x{5F7F}\x{5F80}\x{5F81}' .
+'\x{5F82}\x{5F83}\x{5F84}\x{5F85}\x{5F86}\x{5F87}\x{5F88}\x{5F89}\x{5F8A}' .
+'\x{5F8B}\x{5F8C}\x{5F8D}\x{5F8E}\x{5F90}\x{5F91}\x{5F92}\x{5F93}\x{5F94}' .
+'\x{5F95}\x{5F96}\x{5F97}\x{5F98}\x{5F99}\x{5F9B}\x{5F9C}\x{5F9D}\x{5F9E}' .
+'\x{5F9F}\x{5FA0}\x{5FA1}\x{5FA2}\x{5FA5}\x{5FA6}\x{5FA7}\x{5FA8}\x{5FA9}' .
+'\x{5FAA}\x{5FAB}\x{5FAC}\x{5FAD}\x{5FAE}\x{5FAF}\x{5FB1}\x{5FB2}\x{5FB3}' .
+'\x{5FB4}\x{5FB5}\x{5FB6}\x{5FB7}\x{5FB8}\x{5FB9}\x{5FBA}\x{5FBB}\x{5FBC}' .
+'\x{5FBD}\x{5FBE}\x{5FBF}\x{5FC0}\x{5FC1}\x{5FC3}\x{5FC4}\x{5FC5}\x{5FC6}' .
+'\x{5FC7}\x{5FC8}\x{5FC9}\x{5FCA}\x{5FCB}\x{5FCC}\x{5FCD}\x{5FCF}\x{5FD0}' .
+'\x{5FD1}\x{5FD2}\x{5FD3}\x{5FD4}\x{5FD5}\x{5FD6}\x{5FD7}\x{5FD8}\x{5FD9}' .
+'\x{5FDA}\x{5FDC}\x{5FDD}\x{5FDE}\x{5FE0}\x{5FE1}\x{5FE3}\x{5FE4}\x{5FE5}' .
+'\x{5FE6}\x{5FE7}\x{5FE8}\x{5FE9}\x{5FEA}\x{5FEB}\x{5FED}\x{5FEE}\x{5FEF}' .
+'\x{5FF0}\x{5FF1}\x{5FF2}\x{5FF3}\x{5FF4}\x{5FF5}\x{5FF6}\x{5FF7}\x{5FF8}' .
+'\x{5FF9}\x{5FFA}\x{5FFB}\x{5FFD}\x{5FFE}\x{5FFF}\x{6000}\x{6001}\x{6002}' .
+'\x{6003}\x{6004}\x{6005}\x{6006}\x{6007}\x{6008}\x{6009}\x{600A}\x{600B}' .
+'\x{600C}\x{600D}\x{600E}\x{600F}\x{6010}\x{6011}\x{6012}\x{6013}\x{6014}' .
+'\x{6015}\x{6016}\x{6017}\x{6018}\x{6019}\x{601A}\x{601B}\x{601C}\x{601D}' .
+'\x{601E}\x{601F}\x{6020}\x{6021}\x{6022}\x{6024}\x{6025}\x{6026}\x{6027}' .
+'\x{6028}\x{6029}\x{602A}\x{602B}\x{602C}\x{602D}\x{602E}\x{602F}\x{6030}' .
+'\x{6031}\x{6032}\x{6033}\x{6034}\x{6035}\x{6036}\x{6037}\x{6038}\x{6039}' .
+'\x{603A}\x{603B}\x{603C}\x{603D}\x{603E}\x{603F}\x{6040}\x{6041}\x{6042}' .
+'\x{6043}\x{6044}\x{6045}\x{6046}\x{6047}\x{6048}\x{6049}\x{604A}\x{604B}' .
+'\x{604C}\x{604D}\x{604E}\x{604F}\x{6050}\x{6051}\x{6052}\x{6053}\x{6054}' .
+'\x{6055}\x{6057}\x{6058}\x{6059}\x{605A}\x{605B}\x{605C}\x{605D}\x{605E}' .
+'\x{605F}\x{6062}\x{6063}\x{6064}\x{6065}\x{6066}\x{6067}\x{6068}\x{6069}' .
+'\x{606A}\x{606B}\x{606C}\x{606D}\x{606E}\x{606F}\x{6070}\x{6072}\x{6073}' .
+'\x{6075}\x{6076}\x{6077}\x{6078}\x{6079}\x{607A}\x{607B}\x{607C}\x{607D}' .
+'\x{607E}\x{607F}\x{6080}\x{6081}\x{6082}\x{6083}\x{6084}\x{6085}\x{6086}' .
+'\x{6087}\x{6088}\x{6089}\x{608A}\x{608B}\x{608C}\x{608D}\x{608E}\x{608F}' .
+'\x{6090}\x{6092}\x{6094}\x{6095}\x{6096}\x{6097}\x{6098}\x{6099}\x{609A}' .
+'\x{609B}\x{609C}\x{609D}\x{609E}\x{609F}\x{60A0}\x{60A1}\x{60A2}\x{60A3}' .
+'\x{60A4}\x{60A6}\x{60A7}\x{60A8}\x{60AA}\x{60AB}\x{60AC}\x{60AD}\x{60AE}' .
+'\x{60AF}\x{60B0}\x{60B1}\x{60B2}\x{60B3}\x{60B4}\x{60B5}\x{60B6}\x{60B7}' .
+'\x{60B8}\x{60B9}\x{60BA}\x{60BB}\x{60BC}\x{60BD}\x{60BE}\x{60BF}\x{60C0}' .
+'\x{60C1}\x{60C2}\x{60C3}\x{60C4}\x{60C5}\x{60C6}\x{60C7}\x{60C8}\x{60C9}' .
+'\x{60CA}\x{60CB}\x{60CC}\x{60CD}\x{60CE}\x{60CF}\x{60D0}\x{60D1}\x{60D3}' .
+'\x{60D4}\x{60D5}\x{60D7}\x{60D8}\x{60D9}\x{60DA}\x{60DB}\x{60DC}\x{60DD}' .
+'\x{60DF}\x{60E0}\x{60E1}\x{60E2}\x{60E4}\x{60E6}\x{60E7}\x{60E8}\x{60E9}' .
+'\x{60EA}\x{60EB}\x{60EC}\x{60ED}\x{60EE}\x{60EF}\x{60F0}\x{60F1}\x{60F2}' .
+'\x{60F3}\x{60F4}\x{60F5}\x{60F6}\x{60F7}\x{60F8}\x{60F9}\x{60FA}\x{60FB}' .
+'\x{60FC}\x{60FE}\x{60FF}\x{6100}\x{6101}\x{6103}\x{6104}\x{6105}\x{6106}' .
+'\x{6108}\x{6109}\x{610A}\x{610B}\x{610C}\x{610D}\x{610E}\x{610F}\x{6110}' .
+'\x{6112}\x{6113}\x{6114}\x{6115}\x{6116}\x{6117}\x{6118}\x{6119}\x{611A}' .
+'\x{611B}\x{611C}\x{611D}\x{611F}\x{6120}\x{6122}\x{6123}\x{6124}\x{6125}' .
+'\x{6126}\x{6127}\x{6128}\x{6129}\x{612A}\x{612B}\x{612C}\x{612D}\x{612E}' .
+'\x{612F}\x{6130}\x{6132}\x{6134}\x{6136}\x{6137}\x{613A}\x{613B}\x{613C}' .
+'\x{613D}\x{613E}\x{613F}\x{6140}\x{6141}\x{6142}\x{6143}\x{6144}\x{6145}' .
+'\x{6146}\x{6147}\x{6148}\x{6149}\x{614A}\x{614B}\x{614C}\x{614D}\x{614E}' .
+'\x{614F}\x{6150}\x{6151}\x{6152}\x{6153}\x{6154}\x{6155}\x{6156}\x{6157}' .
+'\x{6158}\x{6159}\x{615A}\x{615B}\x{615C}\x{615D}\x{615E}\x{615F}\x{6161}' .
+'\x{6162}\x{6163}\x{6164}\x{6165}\x{6166}\x{6167}\x{6168}\x{6169}\x{616A}' .
+'\x{616B}\x{616C}\x{616D}\x{616E}\x{6170}\x{6171}\x{6172}\x{6173}\x{6174}' .
+'\x{6175}\x{6176}\x{6177}\x{6178}\x{6179}\x{617A}\x{617C}\x{617E}\x{6180}' .
+'\x{6181}\x{6182}\x{6183}\x{6184}\x{6185}\x{6187}\x{6188}\x{6189}\x{618A}' .
+'\x{618B}\x{618C}\x{618D}\x{618E}\x{618F}\x{6190}\x{6191}\x{6192}\x{6193}' .
+'\x{6194}\x{6195}\x{6196}\x{6198}\x{6199}\x{619A}\x{619B}\x{619D}\x{619E}' .
+'\x{619F}\x{61A0}\x{61A1}\x{61A2}\x{61A3}\x{61A4}\x{61A5}\x{61A6}\x{61A7}' .
+'\x{61A8}\x{61A9}\x{61AA}\x{61AB}\x{61AC}\x{61AD}\x{61AE}\x{61AF}\x{61B0}' .
+'\x{61B1}\x{61B2}\x{61B3}\x{61B4}\x{61B5}\x{61B6}\x{61B7}\x{61B8}\x{61BA}' .
+'\x{61BC}\x{61BD}\x{61BE}\x{61BF}\x{61C0}\x{61C1}\x{61C2}\x{61C3}\x{61C4}' .
+'\x{61C5}\x{61C6}\x{61C7}\x{61C8}\x{61C9}\x{61CA}\x{61CB}\x{61CC}\x{61CD}' .
+'\x{61CE}\x{61CF}\x{61D0}\x{61D1}\x{61D2}\x{61D4}\x{61D6}\x{61D7}\x{61D8}' .
+'\x{61D9}\x{61DA}\x{61DB}\x{61DC}\x{61DD}\x{61DE}\x{61DF}\x{61E0}\x{61E1}' .
+'\x{61E2}\x{61E3}\x{61E4}\x{61E5}\x{61E6}\x{61E7}\x{61E8}\x{61E9}\x{61EA}' .
+'\x{61EB}\x{61ED}\x{61EE}\x{61F0}\x{61F1}\x{61F2}\x{61F3}\x{61F5}\x{61F6}' .
+'\x{61F7}\x{61F8}\x{61F9}\x{61FA}\x{61FB}\x{61FC}\x{61FD}\x{61FE}\x{61FF}' .
+'\x{6200}\x{6201}\x{6202}\x{6203}\x{6204}\x{6206}\x{6207}\x{6208}\x{6209}' .
+'\x{620A}\x{620B}\x{620C}\x{620D}\x{620E}\x{620F}\x{6210}\x{6211}\x{6212}' .
+'\x{6213}\x{6214}\x{6215}\x{6216}\x{6217}\x{6218}\x{6219}\x{621A}\x{621B}' .
+'\x{621C}\x{621D}\x{621E}\x{621F}\x{6220}\x{6221}\x{6222}\x{6223}\x{6224}' .
+'\x{6225}\x{6226}\x{6227}\x{6228}\x{6229}\x{622A}\x{622B}\x{622C}\x{622D}' .
+'\x{622E}\x{622F}\x{6230}\x{6231}\x{6232}\x{6233}\x{6234}\x{6236}\x{6237}' .
+'\x{6238}\x{623A}\x{623B}\x{623C}\x{623D}\x{623E}\x{623F}\x{6240}\x{6241}' .
+'\x{6242}\x{6243}\x{6244}\x{6245}\x{6246}\x{6247}\x{6248}\x{6249}\x{624A}' .
+'\x{624B}\x{624C}\x{624D}\x{624E}\x{624F}\x{6250}\x{6251}\x{6252}\x{6253}' .
+'\x{6254}\x{6255}\x{6256}\x{6258}\x{6259}\x{625A}\x{625B}\x{625C}\x{625D}' .
+'\x{625E}\x{625F}\x{6260}\x{6261}\x{6262}\x{6263}\x{6264}\x{6265}\x{6266}' .
+'\x{6267}\x{6268}\x{6269}\x{626A}\x{626B}\x{626C}\x{626D}\x{626E}\x{626F}' .
+'\x{6270}\x{6271}\x{6272}\x{6273}\x{6274}\x{6275}\x{6276}\x{6277}\x{6278}' .
+'\x{6279}\x{627A}\x{627B}\x{627C}\x{627D}\x{627E}\x{627F}\x{6280}\x{6281}' .
+'\x{6283}\x{6284}\x{6285}\x{6286}\x{6287}\x{6288}\x{6289}\x{628A}\x{628B}' .
+'\x{628C}\x{628E}\x{628F}\x{6290}\x{6291}\x{6292}\x{6293}\x{6294}\x{6295}' .
+'\x{6296}\x{6297}\x{6298}\x{6299}\x{629A}\x{629B}\x{629C}\x{629E}\x{629F}' .
+'\x{62A0}\x{62A1}\x{62A2}\x{62A3}\x{62A4}\x{62A5}\x{62A7}\x{62A8}\x{62A9}' .
+'\x{62AA}\x{62AB}\x{62AC}\x{62AD}\x{62AE}\x{62AF}\x{62B0}\x{62B1}\x{62B2}' .
+'\x{62B3}\x{62B4}\x{62B5}\x{62B6}\x{62B7}\x{62B8}\x{62B9}\x{62BA}\x{62BB}' .
+'\x{62BC}\x{62BD}\x{62BE}\x{62BF}\x{62C0}\x{62C1}\x{62C2}\x{62C3}\x{62C4}' .
+'\x{62C5}\x{62C6}\x{62C7}\x{62C8}\x{62C9}\x{62CA}\x{62CB}\x{62CC}\x{62CD}' .
+'\x{62CE}\x{62CF}\x{62D0}\x{62D1}\x{62D2}\x{62D3}\x{62D4}\x{62D5}\x{62D6}' .
+'\x{62D7}\x{62D8}\x{62D9}\x{62DA}\x{62DB}\x{62DC}\x{62DD}\x{62DF}\x{62E0}' .
+'\x{62E1}\x{62E2}\x{62E3}\x{62E4}\x{62E5}\x{62E6}\x{62E7}\x{62E8}\x{62E9}' .
+'\x{62EB}\x{62EC}\x{62ED}\x{62EE}\x{62EF}\x{62F0}\x{62F1}\x{62F2}\x{62F3}' .
+'\x{62F4}\x{62F5}\x{62F6}\x{62F7}\x{62F8}\x{62F9}\x{62FA}\x{62FB}\x{62FC}' .
+'\x{62FD}\x{62FE}\x{62FF}\x{6300}\x{6301}\x{6302}\x{6303}\x{6304}\x{6305}' .
+'\x{6306}\x{6307}\x{6308}\x{6309}\x{630B}\x{630C}\x{630D}\x{630E}\x{630F}' .
+'\x{6310}\x{6311}\x{6312}\x{6313}\x{6314}\x{6315}\x{6316}\x{6318}\x{6319}' .
+'\x{631A}\x{631B}\x{631C}\x{631D}\x{631E}\x{631F}\x{6320}\x{6321}\x{6322}' .
+'\x{6323}\x{6324}\x{6325}\x{6326}\x{6327}\x{6328}\x{6329}\x{632A}\x{632B}' .
+'\x{632C}\x{632D}\x{632E}\x{632F}\x{6330}\x{6332}\x{6333}\x{6334}\x{6336}' .
+'\x{6338}\x{6339}\x{633A}\x{633B}\x{633C}\x{633D}\x{633E}\x{6340}\x{6341}' .
+'\x{6342}\x{6343}\x{6344}\x{6345}\x{6346}\x{6347}\x{6348}\x{6349}\x{634A}' .
+'\x{634B}\x{634C}\x{634D}\x{634E}\x{634F}\x{6350}\x{6351}\x{6352}\x{6353}' .
+'\x{6354}\x{6355}\x{6356}\x{6357}\x{6358}\x{6359}\x{635A}\x{635C}\x{635D}' .
+'\x{635E}\x{635F}\x{6360}\x{6361}\x{6362}\x{6363}\x{6364}\x{6365}\x{6366}' .
+'\x{6367}\x{6368}\x{6369}\x{636A}\x{636B}\x{636C}\x{636D}\x{636E}\x{636F}' .
+'\x{6370}\x{6371}\x{6372}\x{6373}\x{6374}\x{6375}\x{6376}\x{6377}\x{6378}' .
+'\x{6379}\x{637A}\x{637B}\x{637C}\x{637D}\x{637E}\x{6380}\x{6381}\x{6382}' .
+'\x{6383}\x{6384}\x{6385}\x{6386}\x{6387}\x{6388}\x{6389}\x{638A}\x{638C}' .
+'\x{638D}\x{638E}\x{638F}\x{6390}\x{6391}\x{6392}\x{6394}\x{6395}\x{6396}' .
+'\x{6397}\x{6398}\x{6399}\x{639A}\x{639B}\x{639C}\x{639D}\x{639E}\x{639F}' .
+'\x{63A0}\x{63A1}\x{63A2}\x{63A3}\x{63A4}\x{63A5}\x{63A6}\x{63A7}\x{63A8}' .
+'\x{63A9}\x{63AA}\x{63AB}\x{63AC}\x{63AD}\x{63AE}\x{63AF}\x{63B0}\x{63B1}' .
+'\x{63B2}\x{63B3}\x{63B4}\x{63B5}\x{63B6}\x{63B7}\x{63B8}\x{63B9}\x{63BA}' .
+'\x{63BC}\x{63BD}\x{63BE}\x{63BF}\x{63C0}\x{63C1}\x{63C2}\x{63C3}\x{63C4}' .
+'\x{63C5}\x{63C6}\x{63C7}\x{63C8}\x{63C9}\x{63CA}\x{63CB}\x{63CC}\x{63CD}' .
+'\x{63CE}\x{63CF}\x{63D0}\x{63D2}\x{63D3}\x{63D4}\x{63D5}\x{63D6}\x{63D7}' .
+'\x{63D8}\x{63D9}\x{63DA}\x{63DB}\x{63DC}\x{63DD}\x{63DE}\x{63DF}\x{63E0}' .
+'\x{63E1}\x{63E2}\x{63E3}\x{63E4}\x{63E5}\x{63E6}\x{63E7}\x{63E8}\x{63E9}' .
+'\x{63EA}\x{63EB}\x{63EC}\x{63ED}\x{63EE}\x{63EF}\x{63F0}\x{63F1}\x{63F2}' .
+'\x{63F3}\x{63F4}\x{63F5}\x{63F6}\x{63F7}\x{63F8}\x{63F9}\x{63FA}\x{63FB}' .
+'\x{63FC}\x{63FD}\x{63FE}\x{63FF}\x{6400}\x{6401}\x{6402}\x{6403}\x{6404}' .
+'\x{6405}\x{6406}\x{6408}\x{6409}\x{640A}\x{640B}\x{640C}\x{640D}\x{640E}' .
+'\x{640F}\x{6410}\x{6411}\x{6412}\x{6413}\x{6414}\x{6415}\x{6416}\x{6417}' .
+'\x{6418}\x{6419}\x{641A}\x{641B}\x{641C}\x{641D}\x{641E}\x{641F}\x{6420}' .
+'\x{6421}\x{6422}\x{6423}\x{6424}\x{6425}\x{6426}\x{6427}\x{6428}\x{6429}' .
+'\x{642A}\x{642B}\x{642C}\x{642D}\x{642E}\x{642F}\x{6430}\x{6431}\x{6432}' .
+'\x{6433}\x{6434}\x{6435}\x{6436}\x{6437}\x{6438}\x{6439}\x{643A}\x{643D}' .
+'\x{643E}\x{643F}\x{6440}\x{6441}\x{6443}\x{6444}\x{6445}\x{6446}\x{6447}' .
+'\x{6448}\x{644A}\x{644B}\x{644C}\x{644D}\x{644E}\x{644F}\x{6450}\x{6451}' .
+'\x{6452}\x{6453}\x{6454}\x{6455}\x{6456}\x{6457}\x{6458}\x{6459}\x{645B}' .
+'\x{645C}\x{645D}\x{645E}\x{645F}\x{6460}\x{6461}\x{6462}\x{6463}\x{6464}' .
+'\x{6465}\x{6466}\x{6467}\x{6468}\x{6469}\x{646A}\x{646B}\x{646C}\x{646D}' .
+'\x{646E}\x{646F}\x{6470}\x{6471}\x{6472}\x{6473}\x{6474}\x{6475}\x{6476}' .
+'\x{6477}\x{6478}\x{6479}\x{647A}\x{647B}\x{647C}\x{647D}\x{647F}\x{6480}' .
+'\x{6481}\x{6482}\x{6483}\x{6484}\x{6485}\x{6487}\x{6488}\x{6489}\x{648A}' .
+'\x{648B}\x{648C}\x{648D}\x{648E}\x{648F}\x{6490}\x{6491}\x{6492}\x{6493}' .
+'\x{6494}\x{6495}\x{6496}\x{6497}\x{6498}\x{6499}\x{649A}\x{649B}\x{649C}' .
+'\x{649D}\x{649E}\x{649F}\x{64A0}\x{64A2}\x{64A3}\x{64A4}\x{64A5}\x{64A6}' .
+'\x{64A7}\x{64A8}\x{64A9}\x{64AA}\x{64AB}\x{64AC}\x{64AD}\x{64AE}\x{64B0}' .
+'\x{64B1}\x{64B2}\x{64B3}\x{64B4}\x{64B5}\x{64B7}\x{64B8}\x{64B9}\x{64BA}' .
+'\x{64BB}\x{64BC}\x{64BD}\x{64BE}\x{64BF}\x{64C0}\x{64C1}\x{64C2}\x{64C3}' .
+'\x{64C4}\x{64C5}\x{64C6}\x{64C7}\x{64C9}\x{64CA}\x{64CB}\x{64CC}\x{64CD}' .
+'\x{64CE}\x{64CF}\x{64D0}\x{64D1}\x{64D2}\x{64D3}\x{64D4}\x{64D6}\x{64D7}' .
+'\x{64D8}\x{64D9}\x{64DA}\x{64DB}\x{64DC}\x{64DD}\x{64DE}\x{64DF}\x{64E0}' .
+'\x{64E2}\x{64E3}\x{64E4}\x{64E6}\x{64E7}\x{64E8}\x{64E9}\x{64EA}\x{64EB}' .
+'\x{64EC}\x{64ED}\x{64EF}\x{64F0}\x{64F1}\x{64F2}\x{64F3}\x{64F4}\x{64F6}' .
+'\x{64F7}\x{64F8}\x{64FA}\x{64FB}\x{64FC}\x{64FD}\x{64FE}\x{64FF}\x{6500}' .
+'\x{6501}\x{6503}\x{6504}\x{6505}\x{6506}\x{6507}\x{6508}\x{6509}\x{650B}' .
+'\x{650C}\x{650D}\x{650E}\x{650F}\x{6510}\x{6511}\x{6512}\x{6513}\x{6514}' .
+'\x{6515}\x{6516}\x{6517}\x{6518}\x{6519}\x{651A}\x{651B}\x{651C}\x{651D}' .
+'\x{651E}\x{6520}\x{6521}\x{6522}\x{6523}\x{6524}\x{6525}\x{6526}\x{6527}' .
+'\x{6529}\x{652A}\x{652B}\x{652C}\x{652D}\x{652E}\x{652F}\x{6530}\x{6531}' .
+'\x{6532}\x{6533}\x{6534}\x{6535}\x{6536}\x{6537}\x{6538}\x{6539}\x{653A}' .
+'\x{653B}\x{653C}\x{653D}\x{653E}\x{653F}\x{6541}\x{6543}\x{6544}\x{6545}' .
+'\x{6546}\x{6547}\x{6548}\x{6549}\x{654A}\x{654B}\x{654C}\x{654D}\x{654E}' .
+'\x{654F}\x{6550}\x{6551}\x{6552}\x{6553}\x{6554}\x{6555}\x{6556}\x{6557}' .
+'\x{6558}\x{6559}\x{655B}\x{655C}\x{655D}\x{655E}\x{6560}\x{6561}\x{6562}' .
+'\x{6563}\x{6564}\x{6565}\x{6566}\x{6567}\x{6568}\x{6569}\x{656A}\x{656B}' .
+'\x{656C}\x{656E}\x{656F}\x{6570}\x{6571}\x{6572}\x{6573}\x{6574}\x{6575}' .
+'\x{6576}\x{6577}\x{6578}\x{6579}\x{657A}\x{657B}\x{657C}\x{657E}\x{657F}' .
+'\x{6580}\x{6581}\x{6582}\x{6583}\x{6584}\x{6585}\x{6586}\x{6587}\x{6588}' .
+'\x{6589}\x{658B}\x{658C}\x{658D}\x{658E}\x{658F}\x{6590}\x{6591}\x{6592}' .
+'\x{6593}\x{6594}\x{6595}\x{6596}\x{6597}\x{6598}\x{6599}\x{659B}\x{659C}' .
+'\x{659D}\x{659E}\x{659F}\x{65A0}\x{65A1}\x{65A2}\x{65A3}\x{65A4}\x{65A5}' .
+'\x{65A6}\x{65A7}\x{65A8}\x{65A9}\x{65AA}\x{65AB}\x{65AC}\x{65AD}\x{65AE}' .
+'\x{65AF}\x{65B0}\x{65B1}\x{65B2}\x{65B3}\x{65B4}\x{65B6}\x{65B7}\x{65B8}' .
+'\x{65B9}\x{65BA}\x{65BB}\x{65BC}\x{65BD}\x{65BF}\x{65C0}\x{65C1}\x{65C2}' .
+'\x{65C3}\x{65C4}\x{65C5}\x{65C6}\x{65C7}\x{65CA}\x{65CB}\x{65CC}\x{65CD}' .
+'\x{65CE}\x{65CF}\x{65D0}\x{65D2}\x{65D3}\x{65D4}\x{65D5}\x{65D6}\x{65D7}' .
+'\x{65DA}\x{65DB}\x{65DD}\x{65DE}\x{65DF}\x{65E0}\x{65E1}\x{65E2}\x{65E3}' .
+'\x{65E5}\x{65E6}\x{65E7}\x{65E8}\x{65E9}\x{65EB}\x{65EC}\x{65ED}\x{65EE}' .
+'\x{65EF}\x{65F0}\x{65F1}\x{65F2}\x{65F3}\x{65F4}\x{65F5}\x{65F6}\x{65F7}' .
+'\x{65F8}\x{65FA}\x{65FB}\x{65FC}\x{65FD}\x{6600}\x{6601}\x{6602}\x{6603}' .
+'\x{6604}\x{6605}\x{6606}\x{6607}\x{6608}\x{6609}\x{660A}\x{660B}\x{660C}' .
+'\x{660D}\x{660E}\x{660F}\x{6610}\x{6611}\x{6612}\x{6613}\x{6614}\x{6615}' .
+'\x{6616}\x{6618}\x{6619}\x{661A}\x{661B}\x{661C}\x{661D}\x{661F}\x{6620}' .
+'\x{6621}\x{6622}\x{6623}\x{6624}\x{6625}\x{6626}\x{6627}\x{6628}\x{6629}' .
+'\x{662A}\x{662B}\x{662D}\x{662E}\x{662F}\x{6630}\x{6631}\x{6632}\x{6633}' .
+'\x{6634}\x{6635}\x{6636}\x{6639}\x{663A}\x{663C}\x{663D}\x{663E}\x{6640}' .
+'\x{6641}\x{6642}\x{6643}\x{6644}\x{6645}\x{6646}\x{6647}\x{6649}\x{664A}' .
+'\x{664B}\x{664C}\x{664E}\x{664F}\x{6650}\x{6651}\x{6652}\x{6653}\x{6654}' .
+'\x{6655}\x{6656}\x{6657}\x{6658}\x{6659}\x{665A}\x{665B}\x{665C}\x{665D}' .
+'\x{665E}\x{665F}\x{6661}\x{6662}\x{6664}\x{6665}\x{6666}\x{6668}\x{6669}' .
+'\x{666A}\x{666B}\x{666C}\x{666D}\x{666E}\x{666F}\x{6670}\x{6671}\x{6672}' .
+'\x{6673}\x{6674}\x{6675}\x{6676}\x{6677}\x{6678}\x{6679}\x{667A}\x{667B}' .
+'\x{667C}\x{667D}\x{667E}\x{667F}\x{6680}\x{6681}\x{6682}\x{6683}\x{6684}' .
+'\x{6685}\x{6686}\x{6687}\x{6688}\x{6689}\x{668A}\x{668B}\x{668C}\x{668D}' .
+'\x{668E}\x{668F}\x{6690}\x{6691}\x{6693}\x{6694}\x{6695}\x{6696}\x{6697}' .
+'\x{6698}\x{6699}\x{669A}\x{669B}\x{669D}\x{669F}\x{66A0}\x{66A1}\x{66A2}' .
+'\x{66A3}\x{66A4}\x{66A5}\x{66A6}\x{66A7}\x{66A8}\x{66A9}\x{66AA}\x{66AB}' .
+'\x{66AE}\x{66AF}\x{66B0}\x{66B1}\x{66B2}\x{66B3}\x{66B4}\x{66B5}\x{66B6}' .
+'\x{66B7}\x{66B8}\x{66B9}\x{66BA}\x{66BB}\x{66BC}\x{66BD}\x{66BE}\x{66BF}' .
+'\x{66C0}\x{66C1}\x{66C2}\x{66C3}\x{66C4}\x{66C5}\x{66C6}\x{66C7}\x{66C8}' .
+'\x{66C9}\x{66CA}\x{66CB}\x{66CC}\x{66CD}\x{66CE}\x{66CF}\x{66D1}\x{66D2}' .
+'\x{66D4}\x{66D5}\x{66D6}\x{66D8}\x{66D9}\x{66DA}\x{66DB}\x{66DC}\x{66DD}' .
+'\x{66DE}\x{66E0}\x{66E1}\x{66E2}\x{66E3}\x{66E4}\x{66E5}\x{66E6}\x{66E7}' .
+'\x{66E8}\x{66E9}\x{66EA}\x{66EB}\x{66EC}\x{66ED}\x{66EE}\x{66F0}\x{66F1}' .
+'\x{66F2}\x{66F3}\x{66F4}\x{66F5}\x{66F6}\x{66F7}\x{66F8}\x{66F9}\x{66FA}' .
+'\x{66FB}\x{66FC}\x{66FE}\x{66FF}\x{6700}\x{6701}\x{6703}\x{6704}\x{6705}' .
+'\x{6706}\x{6708}\x{6709}\x{670A}\x{670B}\x{670C}\x{670D}\x{670E}\x{670F}' .
+'\x{6710}\x{6711}\x{6712}\x{6713}\x{6714}\x{6715}\x{6716}\x{6717}\x{6718}' .
+'\x{671A}\x{671B}\x{671C}\x{671D}\x{671E}\x{671F}\x{6720}\x{6721}\x{6722}' .
+'\x{6723}\x{6725}\x{6726}\x{6727}\x{6728}\x{672A}\x{672B}\x{672C}\x{672D}' .
+'\x{672E}\x{672F}\x{6730}\x{6731}\x{6732}\x{6733}\x{6734}\x{6735}\x{6736}' .
+'\x{6737}\x{6738}\x{6739}\x{673A}\x{673B}\x{673C}\x{673D}\x{673E}\x{673F}' .
+'\x{6740}\x{6741}\x{6742}\x{6743}\x{6744}\x{6745}\x{6746}\x{6747}\x{6748}' .
+'\x{6749}\x{674A}\x{674B}\x{674C}\x{674D}\x{674E}\x{674F}\x{6750}\x{6751}' .
+'\x{6752}\x{6753}\x{6754}\x{6755}\x{6756}\x{6757}\x{6758}\x{6759}\x{675A}' .
+'\x{675B}\x{675C}\x{675D}\x{675E}\x{675F}\x{6760}\x{6761}\x{6762}\x{6763}' .
+'\x{6764}\x{6765}\x{6766}\x{6768}\x{6769}\x{676A}\x{676B}\x{676C}\x{676D}' .
+'\x{676E}\x{676F}\x{6770}\x{6771}\x{6772}\x{6773}\x{6774}\x{6775}\x{6776}' .
+'\x{6777}\x{6778}\x{6779}\x{677A}\x{677B}\x{677C}\x{677D}\x{677E}\x{677F}' .
+'\x{6780}\x{6781}\x{6782}\x{6783}\x{6784}\x{6785}\x{6786}\x{6787}\x{6789}' .
+'\x{678A}\x{678B}\x{678C}\x{678D}\x{678E}\x{678F}\x{6790}\x{6791}\x{6792}' .
+'\x{6793}\x{6794}\x{6795}\x{6797}\x{6798}\x{6799}\x{679A}\x{679B}\x{679C}' .
+'\x{679D}\x{679E}\x{679F}\x{67A0}\x{67A1}\x{67A2}\x{67A3}\x{67A4}\x{67A5}' .
+'\x{67A6}\x{67A7}\x{67A8}\x{67AA}\x{67AB}\x{67AC}\x{67AD}\x{67AE}\x{67AF}' .
+'\x{67B0}\x{67B1}\x{67B2}\x{67B3}\x{67B4}\x{67B5}\x{67B6}\x{67B7}\x{67B8}' .
+'\x{67B9}\x{67BA}\x{67BB}\x{67BC}\x{67BE}\x{67C0}\x{67C1}\x{67C2}\x{67C3}' .
+'\x{67C4}\x{67C5}\x{67C6}\x{67C7}\x{67C8}\x{67C9}\x{67CA}\x{67CB}\x{67CC}' .
+'\x{67CD}\x{67CE}\x{67CF}\x{67D0}\x{67D1}\x{67D2}\x{67D3}\x{67D4}\x{67D6}' .
+'\x{67D8}\x{67D9}\x{67DA}\x{67DB}\x{67DC}\x{67DD}\x{67DE}\x{67DF}\x{67E0}' .
+'\x{67E1}\x{67E2}\x{67E3}\x{67E4}\x{67E5}\x{67E6}\x{67E7}\x{67E8}\x{67E9}' .
+'\x{67EA}\x{67EB}\x{67EC}\x{67ED}\x{67EE}\x{67EF}\x{67F0}\x{67F1}\x{67F2}' .
+'\x{67F3}\x{67F4}\x{67F5}\x{67F6}\x{67F7}\x{67F8}\x{67FA}\x{67FB}\x{67FC}' .
+'\x{67FD}\x{67FE}\x{67FF}\x{6800}\x{6802}\x{6803}\x{6804}\x{6805}\x{6806}' .
+'\x{6807}\x{6808}\x{6809}\x{680A}\x{680B}\x{680C}\x{680D}\x{680E}\x{680F}' .
+'\x{6810}\x{6811}\x{6812}\x{6813}\x{6814}\x{6816}\x{6817}\x{6818}\x{6819}' .
+'\x{681A}\x{681B}\x{681C}\x{681D}\x{681F}\x{6820}\x{6821}\x{6822}\x{6823}' .
+'\x{6824}\x{6825}\x{6826}\x{6828}\x{6829}\x{682A}\x{682B}\x{682C}\x{682D}' .
+'\x{682E}\x{682F}\x{6831}\x{6832}\x{6833}\x{6834}\x{6835}\x{6836}\x{6837}' .
+'\x{6838}\x{6839}\x{683A}\x{683B}\x{683C}\x{683D}\x{683E}\x{683F}\x{6840}' .
+'\x{6841}\x{6842}\x{6843}\x{6844}\x{6845}\x{6846}\x{6847}\x{6848}\x{6849}' .
+'\x{684A}\x{684B}\x{684C}\x{684D}\x{684E}\x{684F}\x{6850}\x{6851}\x{6852}' .
+'\x{6853}\x{6854}\x{6855}\x{6856}\x{6857}\x{685B}\x{685D}\x{6860}\x{6861}' .
+'\x{6862}\x{6863}\x{6864}\x{6865}\x{6866}\x{6867}\x{6868}\x{6869}\x{686A}' .
+'\x{686B}\x{686C}\x{686D}\x{686E}\x{686F}\x{6870}\x{6871}\x{6872}\x{6873}' .
+'\x{6874}\x{6875}\x{6876}\x{6877}\x{6878}\x{6879}\x{687B}\x{687C}\x{687D}' .
+'\x{687E}\x{687F}\x{6880}\x{6881}\x{6882}\x{6883}\x{6884}\x{6885}\x{6886}' .
+'\x{6887}\x{6888}\x{6889}\x{688A}\x{688B}\x{688C}\x{688D}\x{688E}\x{688F}' .
+'\x{6890}\x{6891}\x{6892}\x{6893}\x{6894}\x{6896}\x{6897}\x{6898}\x{689A}' .
+'\x{689B}\x{689C}\x{689D}\x{689E}\x{689F}\x{68A0}\x{68A1}\x{68A2}\x{68A3}' .
+'\x{68A4}\x{68A6}\x{68A7}\x{68A8}\x{68A9}\x{68AA}\x{68AB}\x{68AC}\x{68AD}' .
+'\x{68AE}\x{68AF}\x{68B0}\x{68B1}\x{68B2}\x{68B3}\x{68B4}\x{68B5}\x{68B6}' .
+'\x{68B7}\x{68B9}\x{68BB}\x{68BC}\x{68BD}\x{68BE}\x{68BF}\x{68C0}\x{68C1}' .
+'\x{68C2}\x{68C4}\x{68C6}\x{68C7}\x{68C8}\x{68C9}\x{68CA}\x{68CB}\x{68CC}' .
+'\x{68CD}\x{68CE}\x{68CF}\x{68D0}\x{68D1}\x{68D2}\x{68D3}\x{68D4}\x{68D5}' .
+'\x{68D6}\x{68D7}\x{68D8}\x{68DA}\x{68DB}\x{68DC}\x{68DD}\x{68DE}\x{68DF}' .
+'\x{68E0}\x{68E1}\x{68E3}\x{68E4}\x{68E6}\x{68E7}\x{68E8}\x{68E9}\x{68EA}' .
+'\x{68EB}\x{68EC}\x{68ED}\x{68EE}\x{68EF}\x{68F0}\x{68F1}\x{68F2}\x{68F3}' .
+'\x{68F4}\x{68F5}\x{68F6}\x{68F7}\x{68F8}\x{68F9}\x{68FA}\x{68FB}\x{68FC}' .
+'\x{68FD}\x{68FE}\x{68FF}\x{6901}\x{6902}\x{6903}\x{6904}\x{6905}\x{6906}' .
+'\x{6907}\x{6908}\x{690A}\x{690B}\x{690C}\x{690D}\x{690E}\x{690F}\x{6910}' .
+'\x{6911}\x{6912}\x{6913}\x{6914}\x{6915}\x{6916}\x{6917}\x{6918}\x{6919}' .
+'\x{691A}\x{691B}\x{691C}\x{691D}\x{691E}\x{691F}\x{6920}\x{6921}\x{6922}' .
+'\x{6923}\x{6924}\x{6925}\x{6926}\x{6927}\x{6928}\x{6929}\x{692A}\x{692B}' .
+'\x{692C}\x{692D}\x{692E}\x{692F}\x{6930}\x{6931}\x{6932}\x{6933}\x{6934}' .
+'\x{6935}\x{6936}\x{6937}\x{6938}\x{6939}\x{693A}\x{693B}\x{693C}\x{693D}' .
+'\x{693F}\x{6940}\x{6941}\x{6942}\x{6943}\x{6944}\x{6945}\x{6946}\x{6947}' .
+'\x{6948}\x{6949}\x{694A}\x{694B}\x{694C}\x{694E}\x{694F}\x{6950}\x{6951}' .
+'\x{6952}\x{6953}\x{6954}\x{6955}\x{6956}\x{6957}\x{6958}\x{6959}\x{695A}' .
+'\x{695B}\x{695C}\x{695D}\x{695E}\x{695F}\x{6960}\x{6961}\x{6962}\x{6963}' .
+'\x{6964}\x{6965}\x{6966}\x{6967}\x{6968}\x{6969}\x{696A}\x{696B}\x{696C}' .
+'\x{696D}\x{696E}\x{696F}\x{6970}\x{6971}\x{6972}\x{6973}\x{6974}\x{6975}' .
+'\x{6976}\x{6977}\x{6978}\x{6979}\x{697A}\x{697B}\x{697C}\x{697D}\x{697E}' .
+'\x{697F}\x{6980}\x{6981}\x{6982}\x{6983}\x{6984}\x{6985}\x{6986}\x{6987}' .
+'\x{6988}\x{6989}\x{698A}\x{698B}\x{698C}\x{698D}\x{698E}\x{698F}\x{6990}' .
+'\x{6991}\x{6992}\x{6993}\x{6994}\x{6995}\x{6996}\x{6997}\x{6998}\x{6999}' .
+'\x{699A}\x{699B}\x{699C}\x{699D}\x{699E}\x{69A0}\x{69A1}\x{69A3}\x{69A4}' .
+'\x{69A5}\x{69A6}\x{69A7}\x{69A8}\x{69A9}\x{69AA}\x{69AB}\x{69AC}\x{69AD}' .
+'\x{69AE}\x{69AF}\x{69B0}\x{69B1}\x{69B2}\x{69B3}\x{69B4}\x{69B5}\x{69B6}' .
+'\x{69B7}\x{69B8}\x{69B9}\x{69BA}\x{69BB}\x{69BC}\x{69BD}\x{69BE}\x{69BF}' .
+'\x{69C1}\x{69C2}\x{69C3}\x{69C4}\x{69C5}\x{69C6}\x{69C7}\x{69C8}\x{69C9}' .
+'\x{69CA}\x{69CB}\x{69CC}\x{69CD}\x{69CE}\x{69CF}\x{69D0}\x{69D3}\x{69D4}' .
+'\x{69D8}\x{69D9}\x{69DA}\x{69DB}\x{69DC}\x{69DD}\x{69DE}\x{69DF}\x{69E0}' .
+'\x{69E1}\x{69E2}\x{69E3}\x{69E4}\x{69E5}\x{69E6}\x{69E7}\x{69E8}\x{69E9}' .
+'\x{69EA}\x{69EB}\x{69EC}\x{69ED}\x{69EE}\x{69EF}\x{69F0}\x{69F1}\x{69F2}' .
+'\x{69F3}\x{69F4}\x{69F5}\x{69F6}\x{69F7}\x{69F8}\x{69FA}\x{69FB}\x{69FC}' .
+'\x{69FD}\x{69FE}\x{69FF}\x{6A00}\x{6A01}\x{6A02}\x{6A04}\x{6A05}\x{6A06}' .
+'\x{6A07}\x{6A08}\x{6A09}\x{6A0A}\x{6A0B}\x{6A0D}\x{6A0E}\x{6A0F}\x{6A10}' .
+'\x{6A11}\x{6A12}\x{6A13}\x{6A14}\x{6A15}\x{6A16}\x{6A17}\x{6A18}\x{6A19}' .
+'\x{6A1A}\x{6A1B}\x{6A1D}\x{6A1E}\x{6A1F}\x{6A20}\x{6A21}\x{6A22}\x{6A23}' .
+'\x{6A25}\x{6A26}\x{6A27}\x{6A28}\x{6A29}\x{6A2A}\x{6A2B}\x{6A2C}\x{6A2D}' .
+'\x{6A2E}\x{6A2F}\x{6A30}\x{6A31}\x{6A32}\x{6A33}\x{6A34}\x{6A35}\x{6A36}' .
+'\x{6A38}\x{6A39}\x{6A3A}\x{6A3B}\x{6A3C}\x{6A3D}\x{6A3E}\x{6A3F}\x{6A40}' .
+'\x{6A41}\x{6A42}\x{6A43}\x{6A44}\x{6A45}\x{6A46}\x{6A47}\x{6A48}\x{6A49}' .
+'\x{6A4B}\x{6A4C}\x{6A4D}\x{6A4E}\x{6A4F}\x{6A50}\x{6A51}\x{6A52}\x{6A54}' .
+'\x{6A55}\x{6A56}\x{6A57}\x{6A58}\x{6A59}\x{6A5A}\x{6A5B}\x{6A5D}\x{6A5E}' .
+'\x{6A5F}\x{6A60}\x{6A61}\x{6A62}\x{6A63}\x{6A64}\x{6A65}\x{6A66}\x{6A67}' .
+'\x{6A68}\x{6A69}\x{6A6A}\x{6A6B}\x{6A6C}\x{6A6D}\x{6A6F}\x{6A71}\x{6A72}' .
+'\x{6A73}\x{6A74}\x{6A75}\x{6A76}\x{6A77}\x{6A78}\x{6A79}\x{6A7A}\x{6A7B}' .
+'\x{6A7C}\x{6A7D}\x{6A7E}\x{6A7F}\x{6A80}\x{6A81}\x{6A82}\x{6A83}\x{6A84}' .
+'\x{6A85}\x{6A87}\x{6A88}\x{6A89}\x{6A8B}\x{6A8C}\x{6A8D}\x{6A8E}\x{6A90}' .
+'\x{6A91}\x{6A92}\x{6A93}\x{6A94}\x{6A95}\x{6A96}\x{6A97}\x{6A98}\x{6A9A}' .
+'\x{6A9B}\x{6A9C}\x{6A9E}\x{6A9F}\x{6AA0}\x{6AA1}\x{6AA2}\x{6AA3}\x{6AA4}' .
+'\x{6AA5}\x{6AA6}\x{6AA7}\x{6AA8}\x{6AA9}\x{6AAB}\x{6AAC}\x{6AAD}\x{6AAE}' .
+'\x{6AAF}\x{6AB0}\x{6AB2}\x{6AB3}\x{6AB4}\x{6AB5}\x{6AB6}\x{6AB7}\x{6AB8}' .
+'\x{6AB9}\x{6ABA}\x{6ABB}\x{6ABC}\x{6ABD}\x{6ABF}\x{6AC1}\x{6AC2}\x{6AC3}' .
+'\x{6AC5}\x{6AC6}\x{6AC7}\x{6ACA}\x{6ACB}\x{6ACC}\x{6ACD}\x{6ACE}\x{6ACF}' .
+'\x{6AD0}\x{6AD1}\x{6AD2}\x{6AD3}\x{6AD4}\x{6AD5}\x{6AD6}\x{6AD7}\x{6AD9}' .
+'\x{6ADA}\x{6ADB}\x{6ADC}\x{6ADD}\x{6ADE}\x{6ADF}\x{6AE0}\x{6AE1}\x{6AE2}' .
+'\x{6AE3}\x{6AE4}\x{6AE5}\x{6AE6}\x{6AE7}\x{6AE8}\x{6AEA}\x{6AEB}\x{6AEC}' .
+'\x{6AED}\x{6AEE}\x{6AEF}\x{6AF0}\x{6AF1}\x{6AF2}\x{6AF3}\x{6AF4}\x{6AF5}' .
+'\x{6AF6}\x{6AF7}\x{6AF8}\x{6AF9}\x{6AFA}\x{6AFB}\x{6AFC}\x{6AFD}\x{6AFE}' .
+'\x{6AFF}\x{6B00}\x{6B01}\x{6B02}\x{6B03}\x{6B04}\x{6B05}\x{6B06}\x{6B07}' .
+'\x{6B08}\x{6B09}\x{6B0A}\x{6B0B}\x{6B0C}\x{6B0D}\x{6B0F}\x{6B10}\x{6B11}' .
+'\x{6B12}\x{6B13}\x{6B14}\x{6B15}\x{6B16}\x{6B17}\x{6B18}\x{6B19}\x{6B1A}' .
+'\x{6B1C}\x{6B1D}\x{6B1E}\x{6B1F}\x{6B20}\x{6B21}\x{6B22}\x{6B23}\x{6B24}' .
+'\x{6B25}\x{6B26}\x{6B27}\x{6B28}\x{6B29}\x{6B2A}\x{6B2B}\x{6B2C}\x{6B2D}' .
+'\x{6B2F}\x{6B30}\x{6B31}\x{6B32}\x{6B33}\x{6B34}\x{6B36}\x{6B37}\x{6B38}' .
+'\x{6B39}\x{6B3A}\x{6B3B}\x{6B3C}\x{6B3D}\x{6B3E}\x{6B3F}\x{6B41}\x{6B42}' .
+'\x{6B43}\x{6B44}\x{6B45}\x{6B46}\x{6B47}\x{6B48}\x{6B49}\x{6B4A}\x{6B4B}' .
+'\x{6B4C}\x{6B4D}\x{6B4E}\x{6B4F}\x{6B50}\x{6B51}\x{6B52}\x{6B53}\x{6B54}' .
+'\x{6B55}\x{6B56}\x{6B59}\x{6B5A}\x{6B5B}\x{6B5C}\x{6B5E}\x{6B5F}\x{6B60}' .
+'\x{6B61}\x{6B62}\x{6B63}\x{6B64}\x{6B65}\x{6B66}\x{6B67}\x{6B69}\x{6B6A}' .
+'\x{6B6B}\x{6B6D}\x{6B6F}\x{6B70}\x{6B72}\x{6B73}\x{6B74}\x{6B76}\x{6B77}' .
+'\x{6B78}\x{6B79}\x{6B7A}\x{6B7B}\x{6B7C}\x{6B7E}\x{6B7F}\x{6B80}\x{6B81}' .
+'\x{6B82}\x{6B83}\x{6B84}\x{6B85}\x{6B86}\x{6B87}\x{6B88}\x{6B89}\x{6B8A}' .
+'\x{6B8B}\x{6B8C}\x{6B8D}\x{6B8E}\x{6B8F}\x{6B90}\x{6B91}\x{6B92}\x{6B93}' .
+'\x{6B94}\x{6B95}\x{6B96}\x{6B97}\x{6B98}\x{6B99}\x{6B9A}\x{6B9B}\x{6B9C}' .
+'\x{6B9D}\x{6B9E}\x{6B9F}\x{6BA0}\x{6BA1}\x{6BA2}\x{6BA3}\x{6BA4}\x{6BA5}' .
+'\x{6BA6}\x{6BA7}\x{6BA8}\x{6BA9}\x{6BAA}\x{6BAB}\x{6BAC}\x{6BAD}\x{6BAE}' .
+'\x{6BAF}\x{6BB0}\x{6BB2}\x{6BB3}\x{6BB4}\x{6BB5}\x{6BB6}\x{6BB7}\x{6BB9}' .
+'\x{6BBA}\x{6BBB}\x{6BBC}\x{6BBD}\x{6BBE}\x{6BBF}\x{6BC0}\x{6BC1}\x{6BC2}' .
+'\x{6BC3}\x{6BC4}\x{6BC5}\x{6BC6}\x{6BC7}\x{6BC8}\x{6BC9}\x{6BCA}\x{6BCB}' .
+'\x{6BCC}\x{6BCD}\x{6BCE}\x{6BCF}\x{6BD0}\x{6BD1}\x{6BD2}\x{6BD3}\x{6BD4}' .
+'\x{6BD5}\x{6BD6}\x{6BD7}\x{6BD8}\x{6BD9}\x{6BDA}\x{6BDB}\x{6BDC}\x{6BDD}' .
+'\x{6BDE}\x{6BDF}\x{6BE0}\x{6BE1}\x{6BE2}\x{6BE3}\x{6BE4}\x{6BE5}\x{6BE6}' .
+'\x{6BE7}\x{6BE8}\x{6BEA}\x{6BEB}\x{6BEC}\x{6BED}\x{6BEE}\x{6BEF}\x{6BF0}' .
+'\x{6BF2}\x{6BF3}\x{6BF5}\x{6BF6}\x{6BF7}\x{6BF8}\x{6BF9}\x{6BFB}\x{6BFC}' .
+'\x{6BFD}\x{6BFE}\x{6BFF}\x{6C00}\x{6C01}\x{6C02}\x{6C03}\x{6C04}\x{6C05}' .
+'\x{6C06}\x{6C07}\x{6C08}\x{6C09}\x{6C0B}\x{6C0C}\x{6C0D}\x{6C0E}\x{6C0F}' .
+'\x{6C10}\x{6C11}\x{6C12}\x{6C13}\x{6C14}\x{6C15}\x{6C16}\x{6C18}\x{6C19}' .
+'\x{6C1A}\x{6C1B}\x{6C1D}\x{6C1E}\x{6C1F}\x{6C20}\x{6C21}\x{6C22}\x{6C23}' .
+'\x{6C24}\x{6C25}\x{6C26}\x{6C27}\x{6C28}\x{6C29}\x{6C2A}\x{6C2B}\x{6C2C}' .
+'\x{6C2E}\x{6C2F}\x{6C30}\x{6C31}\x{6C32}\x{6C33}\x{6C34}\x{6C35}\x{6C36}' .
+'\x{6C37}\x{6C38}\x{6C3A}\x{6C3B}\x{6C3D}\x{6C3E}\x{6C3F}\x{6C40}\x{6C41}' .
+'\x{6C42}\x{6C43}\x{6C44}\x{6C46}\x{6C47}\x{6C48}\x{6C49}\x{6C4A}\x{6C4B}' .
+'\x{6C4C}\x{6C4D}\x{6C4E}\x{6C4F}\x{6C50}\x{6C51}\x{6C52}\x{6C53}\x{6C54}' .
+'\x{6C55}\x{6C56}\x{6C57}\x{6C58}\x{6C59}\x{6C5A}\x{6C5B}\x{6C5C}\x{6C5D}' .
+'\x{6C5E}\x{6C5F}\x{6C60}\x{6C61}\x{6C62}\x{6C63}\x{6C64}\x{6C65}\x{6C66}' .
+'\x{6C67}\x{6C68}\x{6C69}\x{6C6A}\x{6C6B}\x{6C6D}\x{6C6F}\x{6C70}\x{6C71}' .
+'\x{6C72}\x{6C73}\x{6C74}\x{6C75}\x{6C76}\x{6C77}\x{6C78}\x{6C79}\x{6C7A}' .
+'\x{6C7B}\x{6C7C}\x{6C7D}\x{6C7E}\x{6C7F}\x{6C80}\x{6C81}\x{6C82}\x{6C83}' .
+'\x{6C84}\x{6C85}\x{6C86}\x{6C87}\x{6C88}\x{6C89}\x{6C8A}\x{6C8B}\x{6C8C}' .
+'\x{6C8D}\x{6C8E}\x{6C8F}\x{6C90}\x{6C91}\x{6C92}\x{6C93}\x{6C94}\x{6C95}' .
+'\x{6C96}\x{6C97}\x{6C98}\x{6C99}\x{6C9A}\x{6C9B}\x{6C9C}\x{6C9D}\x{6C9E}' .
+'\x{6C9F}\x{6CA1}\x{6CA2}\x{6CA3}\x{6CA4}\x{6CA5}\x{6CA6}\x{6CA7}\x{6CA8}' .
+'\x{6CA9}\x{6CAA}\x{6CAB}\x{6CAC}\x{6CAD}\x{6CAE}\x{6CAF}\x{6CB0}\x{6CB1}' .
+'\x{6CB2}\x{6CB3}\x{6CB4}\x{6CB5}\x{6CB6}\x{6CB7}\x{6CB8}\x{6CB9}\x{6CBA}' .
+'\x{6CBB}\x{6CBC}\x{6CBD}\x{6CBE}\x{6CBF}\x{6CC0}\x{6CC1}\x{6CC2}\x{6CC3}' .
+'\x{6CC4}\x{6CC5}\x{6CC6}\x{6CC7}\x{6CC8}\x{6CC9}\x{6CCA}\x{6CCB}\x{6CCC}' .
+'\x{6CCD}\x{6CCE}\x{6CCF}\x{6CD0}\x{6CD1}\x{6CD2}\x{6CD3}\x{6CD4}\x{6CD5}' .
+'\x{6CD6}\x{6CD7}\x{6CD9}\x{6CDA}\x{6CDB}\x{6CDC}\x{6CDD}\x{6CDE}\x{6CDF}' .
+'\x{6CE0}\x{6CE1}\x{6CE2}\x{6CE3}\x{6CE4}\x{6CE5}\x{6CE6}\x{6CE7}\x{6CE8}' .
+'\x{6CE9}\x{6CEA}\x{6CEB}\x{6CEC}\x{6CED}\x{6CEE}\x{6CEF}\x{6CF0}\x{6CF1}' .
+'\x{6CF2}\x{6CF3}\x{6CF5}\x{6CF6}\x{6CF7}\x{6CF8}\x{6CF9}\x{6CFA}\x{6CFB}' .
+'\x{6CFC}\x{6CFD}\x{6CFE}\x{6CFF}\x{6D00}\x{6D01}\x{6D03}\x{6D04}\x{6D05}' .
+'\x{6D06}\x{6D07}\x{6D08}\x{6D09}\x{6D0A}\x{6D0B}\x{6D0C}\x{6D0D}\x{6D0E}' .
+'\x{6D0F}\x{6D10}\x{6D11}\x{6D12}\x{6D13}\x{6D14}\x{6D15}\x{6D16}\x{6D17}' .
+'\x{6D18}\x{6D19}\x{6D1A}\x{6D1B}\x{6D1D}\x{6D1E}\x{6D1F}\x{6D20}\x{6D21}' .
+'\x{6D22}\x{6D23}\x{6D25}\x{6D26}\x{6D27}\x{6D28}\x{6D29}\x{6D2A}\x{6D2B}' .
+'\x{6D2C}\x{6D2D}\x{6D2E}\x{6D2F}\x{6D30}\x{6D31}\x{6D32}\x{6D33}\x{6D34}' .
+'\x{6D35}\x{6D36}\x{6D37}\x{6D38}\x{6D39}\x{6D3A}\x{6D3B}\x{6D3C}\x{6D3D}' .
+'\x{6D3E}\x{6D3F}\x{6D40}\x{6D41}\x{6D42}\x{6D43}\x{6D44}\x{6D45}\x{6D46}' .
+'\x{6D47}\x{6D48}\x{6D49}\x{6D4A}\x{6D4B}\x{6D4C}\x{6D4D}\x{6D4E}\x{6D4F}' .
+'\x{6D50}\x{6D51}\x{6D52}\x{6D53}\x{6D54}\x{6D55}\x{6D56}\x{6D57}\x{6D58}' .
+'\x{6D59}\x{6D5A}\x{6D5B}\x{6D5C}\x{6D5D}\x{6D5E}\x{6D5F}\x{6D60}\x{6D61}' .
+'\x{6D62}\x{6D63}\x{6D64}\x{6D65}\x{6D66}\x{6D67}\x{6D68}\x{6D69}\x{6D6A}' .
+'\x{6D6B}\x{6D6C}\x{6D6D}\x{6D6E}\x{6D6F}\x{6D70}\x{6D72}\x{6D73}\x{6D74}' .
+'\x{6D75}\x{6D76}\x{6D77}\x{6D78}\x{6D79}\x{6D7A}\x{6D7B}\x{6D7C}\x{6D7D}' .
+'\x{6D7E}\x{6D7F}\x{6D80}\x{6D82}\x{6D83}\x{6D84}\x{6D85}\x{6D86}\x{6D87}' .
+'\x{6D88}\x{6D89}\x{6D8A}\x{6D8B}\x{6D8C}\x{6D8D}\x{6D8E}\x{6D8F}\x{6D90}' .
+'\x{6D91}\x{6D92}\x{6D93}\x{6D94}\x{6D95}\x{6D97}\x{6D98}\x{6D99}\x{6D9A}' .
+'\x{6D9B}\x{6D9D}\x{6D9E}\x{6D9F}\x{6DA0}\x{6DA1}\x{6DA2}\x{6DA3}\x{6DA4}' .
+'\x{6DA5}\x{6DA6}\x{6DA7}\x{6DA8}\x{6DA9}\x{6DAA}\x{6DAB}\x{6DAC}\x{6DAD}' .
+'\x{6DAE}\x{6DAF}\x{6DB2}\x{6DB3}\x{6DB4}\x{6DB5}\x{6DB7}\x{6DB8}\x{6DB9}' .
+'\x{6DBA}\x{6DBB}\x{6DBC}\x{6DBD}\x{6DBE}\x{6DBF}\x{6DC0}\x{6DC1}\x{6DC2}' .
+'\x{6DC3}\x{6DC4}\x{6DC5}\x{6DC6}\x{6DC7}\x{6DC8}\x{6DC9}\x{6DCA}\x{6DCB}' .
+'\x{6DCC}\x{6DCD}\x{6DCE}\x{6DCF}\x{6DD0}\x{6DD1}\x{6DD2}\x{6DD3}\x{6DD4}' .
+'\x{6DD5}\x{6DD6}\x{6DD7}\x{6DD8}\x{6DD9}\x{6DDA}\x{6DDB}\x{6DDC}\x{6DDD}' .
+'\x{6DDE}\x{6DDF}\x{6DE0}\x{6DE1}\x{6DE2}\x{6DE3}\x{6DE4}\x{6DE5}\x{6DE6}' .
+'\x{6DE7}\x{6DE8}\x{6DE9}\x{6DEA}\x{6DEB}\x{6DEC}\x{6DED}\x{6DEE}\x{6DEF}' .
+'\x{6DF0}\x{6DF1}\x{6DF2}\x{6DF3}\x{6DF4}\x{6DF5}\x{6DF6}\x{6DF7}\x{6DF8}' .
+'\x{6DF9}\x{6DFA}\x{6DFB}\x{6DFC}\x{6DFD}\x{6E00}\x{6E03}\x{6E04}\x{6E05}' .
+'\x{6E07}\x{6E08}\x{6E09}\x{6E0A}\x{6E0B}\x{6E0C}\x{6E0D}\x{6E0E}\x{6E0F}' .
+'\x{6E10}\x{6E11}\x{6E14}\x{6E15}\x{6E16}\x{6E17}\x{6E19}\x{6E1A}\x{6E1B}' .
+'\x{6E1C}\x{6E1D}\x{6E1E}\x{6E1F}\x{6E20}\x{6E21}\x{6E22}\x{6E23}\x{6E24}' .
+'\x{6E25}\x{6E26}\x{6E27}\x{6E28}\x{6E29}\x{6E2B}\x{6E2C}\x{6E2D}\x{6E2E}' .
+'\x{6E2F}\x{6E30}\x{6E31}\x{6E32}\x{6E33}\x{6E34}\x{6E35}\x{6E36}\x{6E37}' .
+'\x{6E38}\x{6E39}\x{6E3A}\x{6E3B}\x{6E3C}\x{6E3D}\x{6E3E}\x{6E3F}\x{6E40}' .
+'\x{6E41}\x{6E42}\x{6E43}\x{6E44}\x{6E45}\x{6E46}\x{6E47}\x{6E48}\x{6E49}' .
+'\x{6E4A}\x{6E4B}\x{6E4D}\x{6E4E}\x{6E4F}\x{6E50}\x{6E51}\x{6E52}\x{6E53}' .
+'\x{6E54}\x{6E55}\x{6E56}\x{6E57}\x{6E58}\x{6E59}\x{6E5A}\x{6E5B}\x{6E5C}' .
+'\x{6E5D}\x{6E5E}\x{6E5F}\x{6E60}\x{6E61}\x{6E62}\x{6E63}\x{6E64}\x{6E65}' .
+'\x{6E66}\x{6E67}\x{6E68}\x{6E69}\x{6E6A}\x{6E6B}\x{6E6D}\x{6E6E}\x{6E6F}' .
+'\x{6E70}\x{6E71}\x{6E72}\x{6E73}\x{6E74}\x{6E75}\x{6E77}\x{6E78}\x{6E79}' .
+'\x{6E7E}\x{6E7F}\x{6E80}\x{6E81}\x{6E82}\x{6E83}\x{6E84}\x{6E85}\x{6E86}' .
+'\x{6E87}\x{6E88}\x{6E89}\x{6E8A}\x{6E8D}\x{6E8E}\x{6E8F}\x{6E90}\x{6E91}' .
+'\x{6E92}\x{6E93}\x{6E94}\x{6E96}\x{6E97}\x{6E98}\x{6E99}\x{6E9A}\x{6E9B}' .
+'\x{6E9C}\x{6E9D}\x{6E9E}\x{6E9F}\x{6EA0}\x{6EA1}\x{6EA2}\x{6EA3}\x{6EA4}' .
+'\x{6EA5}\x{6EA6}\x{6EA7}\x{6EA8}\x{6EA9}\x{6EAA}\x{6EAB}\x{6EAC}\x{6EAD}' .
+'\x{6EAE}\x{6EAF}\x{6EB0}\x{6EB1}\x{6EB2}\x{6EB3}\x{6EB4}\x{6EB5}\x{6EB6}' .
+'\x{6EB7}\x{6EB8}\x{6EB9}\x{6EBA}\x{6EBB}\x{6EBC}\x{6EBD}\x{6EBE}\x{6EBF}' .
+'\x{6EC0}\x{6EC1}\x{6EC2}\x{6EC3}\x{6EC4}\x{6EC5}\x{6EC6}\x{6EC7}\x{6EC8}' .
+'\x{6EC9}\x{6ECA}\x{6ECB}\x{6ECC}\x{6ECD}\x{6ECE}\x{6ECF}\x{6ED0}\x{6ED1}' .
+'\x{6ED2}\x{6ED3}\x{6ED4}\x{6ED5}\x{6ED6}\x{6ED7}\x{6ED8}\x{6ED9}\x{6EDA}' .
+'\x{6EDC}\x{6EDE}\x{6EDF}\x{6EE0}\x{6EE1}\x{6EE2}\x{6EE4}\x{6EE5}\x{6EE6}' .
+'\x{6EE7}\x{6EE8}\x{6EE9}\x{6EEA}\x{6EEB}\x{6EEC}\x{6EED}\x{6EEE}\x{6EEF}' .
+'\x{6EF0}\x{6EF1}\x{6EF2}\x{6EF3}\x{6EF4}\x{6EF5}\x{6EF6}\x{6EF7}\x{6EF8}' .
+'\x{6EF9}\x{6EFA}\x{6EFB}\x{6EFC}\x{6EFD}\x{6EFE}\x{6EFF}\x{6F00}\x{6F01}' .
+'\x{6F02}\x{6F03}\x{6F05}\x{6F06}\x{6F07}\x{6F08}\x{6F09}\x{6F0A}\x{6F0C}' .
+'\x{6F0D}\x{6F0E}\x{6F0F}\x{6F10}\x{6F11}\x{6F12}\x{6F13}\x{6F14}\x{6F15}' .
+'\x{6F16}\x{6F17}\x{6F18}\x{6F19}\x{6F1A}\x{6F1B}\x{6F1C}\x{6F1D}\x{6F1E}' .
+'\x{6F1F}\x{6F20}\x{6F21}\x{6F22}\x{6F23}\x{6F24}\x{6F25}\x{6F26}\x{6F27}' .
+'\x{6F28}\x{6F29}\x{6F2A}\x{6F2B}\x{6F2C}\x{6F2D}\x{6F2E}\x{6F2F}\x{6F30}' .
+'\x{6F31}\x{6F32}\x{6F33}\x{6F34}\x{6F35}\x{6F36}\x{6F37}\x{6F38}\x{6F39}' .
+'\x{6F3A}\x{6F3B}\x{6F3C}\x{6F3D}\x{6F3E}\x{6F3F}\x{6F40}\x{6F41}\x{6F43}' .
+'\x{6F44}\x{6F45}\x{6F46}\x{6F47}\x{6F49}\x{6F4B}\x{6F4C}\x{6F4D}\x{6F4E}' .
+'\x{6F4F}\x{6F50}\x{6F51}\x{6F52}\x{6F53}\x{6F54}\x{6F55}\x{6F56}\x{6F57}' .
+'\x{6F58}\x{6F59}\x{6F5A}\x{6F5B}\x{6F5C}\x{6F5D}\x{6F5E}\x{6F5F}\x{6F60}' .
+'\x{6F61}\x{6F62}\x{6F63}\x{6F64}\x{6F65}\x{6F66}\x{6F67}\x{6F68}\x{6F69}' .
+'\x{6F6A}\x{6F6B}\x{6F6C}\x{6F6D}\x{6F6E}\x{6F6F}\x{6F70}\x{6F71}\x{6F72}' .
+'\x{6F73}\x{6F74}\x{6F75}\x{6F76}\x{6F77}\x{6F78}\x{6F7A}\x{6F7B}\x{6F7C}' .
+'\x{6F7D}\x{6F7E}\x{6F7F}\x{6F80}\x{6F81}\x{6F82}\x{6F83}\x{6F84}\x{6F85}' .
+'\x{6F86}\x{6F87}\x{6F88}\x{6F89}\x{6F8A}\x{6F8B}\x{6F8C}\x{6F8D}\x{6F8E}' .
+'\x{6F8F}\x{6F90}\x{6F91}\x{6F92}\x{6F93}\x{6F94}\x{6F95}\x{6F96}\x{6F97}' .
+'\x{6F99}\x{6F9B}\x{6F9C}\x{6F9D}\x{6F9E}\x{6FA0}\x{6FA1}\x{6FA2}\x{6FA3}' .
+'\x{6FA4}\x{6FA5}\x{6FA6}\x{6FA7}\x{6FA8}\x{6FA9}\x{6FAA}\x{6FAB}\x{6FAC}' .
+'\x{6FAD}\x{6FAE}\x{6FAF}\x{6FB0}\x{6FB1}\x{6FB2}\x{6FB3}\x{6FB4}\x{6FB5}' .
+'\x{6FB6}\x{6FB8}\x{6FB9}\x{6FBA}\x{6FBB}\x{6FBC}\x{6FBD}\x{6FBE}\x{6FBF}' .
+'\x{6FC0}\x{6FC1}\x{6FC2}\x{6FC3}\x{6FC4}\x{6FC6}\x{6FC7}\x{6FC8}\x{6FC9}' .
+'\x{6FCA}\x{6FCB}\x{6FCC}\x{6FCD}\x{6FCE}\x{6FCF}\x{6FD1}\x{6FD2}\x{6FD4}' .
+'\x{6FD5}\x{6FD6}\x{6FD7}\x{6FD8}\x{6FD9}\x{6FDA}\x{6FDB}\x{6FDC}\x{6FDD}' .
+'\x{6FDE}\x{6FDF}\x{6FE0}\x{6FE1}\x{6FE2}\x{6FE3}\x{6FE4}\x{6FE5}\x{6FE6}' .
+'\x{6FE7}\x{6FE8}\x{6FE9}\x{6FEA}\x{6FEB}\x{6FEC}\x{6FED}\x{6FEE}\x{6FEF}' .
+'\x{6FF0}\x{6FF1}\x{6FF2}\x{6FF3}\x{6FF4}\x{6FF6}\x{6FF7}\x{6FF8}\x{6FF9}' .
+'\x{6FFA}\x{6FFB}\x{6FFC}\x{6FFE}\x{6FFF}\x{7000}\x{7001}\x{7002}\x{7003}' .
+'\x{7004}\x{7005}\x{7006}\x{7007}\x{7008}\x{7009}\x{700A}\x{700B}\x{700C}' .
+'\x{700D}\x{700E}\x{700F}\x{7011}\x{7012}\x{7014}\x{7015}\x{7016}\x{7017}' .
+'\x{7018}\x{7019}\x{701A}\x{701B}\x{701C}\x{701D}\x{701F}\x{7020}\x{7021}' .
+'\x{7022}\x{7023}\x{7024}\x{7025}\x{7026}\x{7027}\x{7028}\x{7029}\x{702A}' .
+'\x{702B}\x{702C}\x{702D}\x{702E}\x{702F}\x{7030}\x{7031}\x{7032}\x{7033}' .
+'\x{7034}\x{7035}\x{7036}\x{7037}\x{7038}\x{7039}\x{703A}\x{703B}\x{703C}' .
+'\x{703D}\x{703E}\x{703F}\x{7040}\x{7041}\x{7042}\x{7043}\x{7044}\x{7045}' .
+'\x{7046}\x{7048}\x{7049}\x{704A}\x{704C}\x{704D}\x{704F}\x{7050}\x{7051}' .
+'\x{7052}\x{7053}\x{7054}\x{7055}\x{7056}\x{7057}\x{7058}\x{7059}\x{705A}' .
+'\x{705B}\x{705C}\x{705D}\x{705E}\x{705F}\x{7060}\x{7061}\x{7062}\x{7063}' .
+'\x{7064}\x{7065}\x{7066}\x{7067}\x{7068}\x{7069}\x{706A}\x{706B}\x{706C}' .
+'\x{706D}\x{706E}\x{706F}\x{7070}\x{7071}\x{7074}\x{7075}\x{7076}\x{7077}' .
+'\x{7078}\x{7079}\x{707A}\x{707C}\x{707D}\x{707E}\x{707F}\x{7080}\x{7082}' .
+'\x{7083}\x{7084}\x{7085}\x{7086}\x{7087}\x{7088}\x{7089}\x{708A}\x{708B}' .
+'\x{708C}\x{708E}\x{708F}\x{7090}\x{7091}\x{7092}\x{7093}\x{7094}\x{7095}' .
+'\x{7096}\x{7098}\x{7099}\x{709A}\x{709C}\x{709D}\x{709E}\x{709F}\x{70A0}' .
+'\x{70A1}\x{70A2}\x{70A3}\x{70A4}\x{70A5}\x{70A6}\x{70A7}\x{70A8}\x{70A9}' .
+'\x{70AB}\x{70AC}\x{70AD}\x{70AE}\x{70AF}\x{70B0}\x{70B1}\x{70B3}\x{70B4}' .
+'\x{70B5}\x{70B7}\x{70B8}\x{70B9}\x{70BA}\x{70BB}\x{70BC}\x{70BD}\x{70BE}' .
+'\x{70BF}\x{70C0}\x{70C1}\x{70C2}\x{70C3}\x{70C4}\x{70C5}\x{70C6}\x{70C7}' .
+'\x{70C8}\x{70C9}\x{70CA}\x{70CB}\x{70CC}\x{70CD}\x{70CE}\x{70CF}\x{70D0}' .
+'\x{70D1}\x{70D2}\x{70D3}\x{70D4}\x{70D6}\x{70D7}\x{70D8}\x{70D9}\x{70DA}' .
+'\x{70DB}\x{70DC}\x{70DD}\x{70DE}\x{70DF}\x{70E0}\x{70E1}\x{70E2}\x{70E3}' .
+'\x{70E4}\x{70E5}\x{70E6}\x{70E7}\x{70E8}\x{70E9}\x{70EA}\x{70EB}\x{70EC}' .
+'\x{70ED}\x{70EE}\x{70EF}\x{70F0}\x{70F1}\x{70F2}\x{70F3}\x{70F4}\x{70F5}' .
+'\x{70F6}\x{70F7}\x{70F8}\x{70F9}\x{70FA}\x{70FB}\x{70FC}\x{70FD}\x{70FF}' .
+'\x{7100}\x{7101}\x{7102}\x{7103}\x{7104}\x{7105}\x{7106}\x{7107}\x{7109}' .
+'\x{710A}\x{710B}\x{710C}\x{710D}\x{710E}\x{710F}\x{7110}\x{7111}\x{7112}' .
+'\x{7113}\x{7115}\x{7116}\x{7117}\x{7118}\x{7119}\x{711A}\x{711B}\x{711C}' .
+'\x{711D}\x{711E}\x{711F}\x{7120}\x{7121}\x{7122}\x{7123}\x{7125}\x{7126}' .
+'\x{7127}\x{7128}\x{7129}\x{712A}\x{712B}\x{712C}\x{712D}\x{712E}\x{712F}' .
+'\x{7130}\x{7131}\x{7132}\x{7135}\x{7136}\x{7137}\x{7138}\x{7139}\x{713A}' .
+'\x{713B}\x{713D}\x{713E}\x{713F}\x{7140}\x{7141}\x{7142}\x{7143}\x{7144}' .
+'\x{7145}\x{7146}\x{7147}\x{7148}\x{7149}\x{714A}\x{714B}\x{714C}\x{714D}' .
+'\x{714E}\x{714F}\x{7150}\x{7151}\x{7152}\x{7153}\x{7154}\x{7156}\x{7158}' .
+'\x{7159}\x{715A}\x{715B}\x{715C}\x{715D}\x{715E}\x{715F}\x{7160}\x{7161}' .
+'\x{7162}\x{7163}\x{7164}\x{7165}\x{7166}\x{7167}\x{7168}\x{7169}\x{716A}' .
+'\x{716C}\x{716E}\x{716F}\x{7170}\x{7171}\x{7172}\x{7173}\x{7174}\x{7175}' .
+'\x{7176}\x{7177}\x{7178}\x{7179}\x{717A}\x{717B}\x{717C}\x{717D}\x{717E}' .
+'\x{717F}\x{7180}\x{7181}\x{7182}\x{7183}\x{7184}\x{7185}\x{7186}\x{7187}' .
+'\x{7188}\x{7189}\x{718A}\x{718B}\x{718C}\x{718E}\x{718F}\x{7190}\x{7191}' .
+'\x{7192}\x{7193}\x{7194}\x{7195}\x{7197}\x{7198}\x{7199}\x{719A}\x{719B}' .
+'\x{719C}\x{719D}\x{719E}\x{719F}\x{71A0}\x{71A1}\x{71A2}\x{71A3}\x{71A4}' .
+'\x{71A5}\x{71A7}\x{71A8}\x{71A9}\x{71AA}\x{71AC}\x{71AD}\x{71AE}\x{71AF}' .
+'\x{71B0}\x{71B1}\x{71B2}\x{71B3}\x{71B4}\x{71B5}\x{71B7}\x{71B8}\x{71B9}' .
+'\x{71BA}\x{71BB}\x{71BC}\x{71BD}\x{71BE}\x{71BF}\x{71C0}\x{71C1}\x{71C2}' .
+'\x{71C3}\x{71C4}\x{71C5}\x{71C6}\x{71C7}\x{71C8}\x{71C9}\x{71CA}\x{71CB}' .
+'\x{71CD}\x{71CE}\x{71CF}\x{71D0}\x{71D1}\x{71D2}\x{71D4}\x{71D5}\x{71D6}' .
+'\x{71D7}\x{71D8}\x{71D9}\x{71DA}\x{71DB}\x{71DC}\x{71DD}\x{71DE}\x{71DF}' .
+'\x{71E0}\x{71E1}\x{71E2}\x{71E3}\x{71E4}\x{71E5}\x{71E6}\x{71E7}\x{71E8}' .
+'\x{71E9}\x{71EA}\x{71EB}\x{71EC}\x{71ED}\x{71EE}\x{71EF}\x{71F0}\x{71F1}' .
+'\x{71F2}\x{71F4}\x{71F5}\x{71F6}\x{71F7}\x{71F8}\x{71F9}\x{71FB}\x{71FC}' .
+'\x{71FD}\x{71FE}\x{71FF}\x{7201}\x{7202}\x{7203}\x{7204}\x{7205}\x{7206}' .
+'\x{7207}\x{7208}\x{7209}\x{720A}\x{720C}\x{720D}\x{720E}\x{720F}\x{7210}' .
+'\x{7212}\x{7213}\x{7214}\x{7216}\x{7218}\x{7219}\x{721A}\x{721B}\x{721C}' .
+'\x{721D}\x{721E}\x{721F}\x{7221}\x{7222}\x{7223}\x{7226}\x{7227}\x{7228}' .
+'\x{7229}\x{722A}\x{722B}\x{722C}\x{722D}\x{722E}\x{7230}\x{7231}\x{7232}' .
+'\x{7233}\x{7235}\x{7236}\x{7237}\x{7238}\x{7239}\x{723A}\x{723B}\x{723C}' .
+'\x{723D}\x{723E}\x{723F}\x{7240}\x{7241}\x{7242}\x{7243}\x{7244}\x{7246}' .
+'\x{7247}\x{7248}\x{7249}\x{724A}\x{724B}\x{724C}\x{724D}\x{724F}\x{7251}' .
+'\x{7252}\x{7253}\x{7254}\x{7256}\x{7257}\x{7258}\x{7259}\x{725A}\x{725B}' .
+'\x{725C}\x{725D}\x{725E}\x{725F}\x{7260}\x{7261}\x{7262}\x{7263}\x{7264}' .
+'\x{7265}\x{7266}\x{7267}\x{7268}\x{7269}\x{726A}\x{726B}\x{726C}\x{726D}' .
+'\x{726E}\x{726F}\x{7270}\x{7271}\x{7272}\x{7273}\x{7274}\x{7275}\x{7276}' .
+'\x{7277}\x{7278}\x{7279}\x{727A}\x{727B}\x{727C}\x{727D}\x{727E}\x{727F}' .
+'\x{7280}\x{7281}\x{7282}\x{7283}\x{7284}\x{7285}\x{7286}\x{7287}\x{7288}' .
+'\x{7289}\x{728A}\x{728B}\x{728C}\x{728D}\x{728E}\x{728F}\x{7290}\x{7291}' .
+'\x{7292}\x{7293}\x{7294}\x{7295}\x{7296}\x{7297}\x{7298}\x{7299}\x{729A}' .
+'\x{729B}\x{729C}\x{729D}\x{729E}\x{729F}\x{72A1}\x{72A2}\x{72A3}\x{72A4}' .
+'\x{72A5}\x{72A6}\x{72A7}\x{72A8}\x{72A9}\x{72AA}\x{72AC}\x{72AD}\x{72AE}' .
+'\x{72AF}\x{72B0}\x{72B1}\x{72B2}\x{72B3}\x{72B4}\x{72B5}\x{72B6}\x{72B7}' .
+'\x{72B8}\x{72B9}\x{72BA}\x{72BB}\x{72BC}\x{72BD}\x{72BF}\x{72C0}\x{72C1}' .
+'\x{72C2}\x{72C3}\x{72C4}\x{72C5}\x{72C6}\x{72C7}\x{72C8}\x{72C9}\x{72CA}' .
+'\x{72CB}\x{72CC}\x{72CD}\x{72CE}\x{72CF}\x{72D0}\x{72D1}\x{72D2}\x{72D3}' .
+'\x{72D4}\x{72D5}\x{72D6}\x{72D7}\x{72D8}\x{72D9}\x{72DA}\x{72DB}\x{72DC}' .
+'\x{72DD}\x{72DE}\x{72DF}\x{72E0}\x{72E1}\x{72E2}\x{72E3}\x{72E4}\x{72E5}' .
+'\x{72E6}\x{72E7}\x{72E8}\x{72E9}\x{72EA}\x{72EB}\x{72EC}\x{72ED}\x{72EE}' .
+'\x{72EF}\x{72F0}\x{72F1}\x{72F2}\x{72F3}\x{72F4}\x{72F5}\x{72F6}\x{72F7}' .
+'\x{72F8}\x{72F9}\x{72FA}\x{72FB}\x{72FC}\x{72FD}\x{72FE}\x{72FF}\x{7300}' .
+'\x{7301}\x{7303}\x{7304}\x{7305}\x{7306}\x{7307}\x{7308}\x{7309}\x{730A}' .
+'\x{730B}\x{730C}\x{730D}\x{730E}\x{730F}\x{7311}\x{7312}\x{7313}\x{7314}' .
+'\x{7315}\x{7316}\x{7317}\x{7318}\x{7319}\x{731A}\x{731B}\x{731C}\x{731D}' .
+'\x{731E}\x{7320}\x{7321}\x{7322}\x{7323}\x{7324}\x{7325}\x{7326}\x{7327}' .
+'\x{7329}\x{732A}\x{732B}\x{732C}\x{732D}\x{732E}\x{7330}\x{7331}\x{7332}' .
+'\x{7333}\x{7334}\x{7335}\x{7336}\x{7337}\x{7338}\x{7339}\x{733A}\x{733B}' .
+'\x{733C}\x{733D}\x{733E}\x{733F}\x{7340}\x{7341}\x{7342}\x{7343}\x{7344}' .
+'\x{7345}\x{7346}\x{7347}\x{7348}\x{7349}\x{734A}\x{734B}\x{734C}\x{734D}' .
+'\x{734E}\x{7350}\x{7351}\x{7352}\x{7354}\x{7355}\x{7356}\x{7357}\x{7358}' .
+'\x{7359}\x{735A}\x{735B}\x{735C}\x{735D}\x{735E}\x{735F}\x{7360}\x{7361}' .
+'\x{7362}\x{7364}\x{7365}\x{7366}\x{7367}\x{7368}\x{7369}\x{736A}\x{736B}' .
+'\x{736C}\x{736D}\x{736E}\x{736F}\x{7370}\x{7371}\x{7372}\x{7373}\x{7374}' .
+'\x{7375}\x{7376}\x{7377}\x{7378}\x{7379}\x{737A}\x{737B}\x{737C}\x{737D}' .
+'\x{737E}\x{737F}\x{7380}\x{7381}\x{7382}\x{7383}\x{7384}\x{7385}\x{7386}' .
+'\x{7387}\x{7388}\x{7389}\x{738A}\x{738B}\x{738C}\x{738D}\x{738E}\x{738F}' .
+'\x{7390}\x{7391}\x{7392}\x{7393}\x{7394}\x{7395}\x{7396}\x{7397}\x{7398}' .
+'\x{7399}\x{739A}\x{739B}\x{739D}\x{739E}\x{739F}\x{73A0}\x{73A1}\x{73A2}' .
+'\x{73A3}\x{73A4}\x{73A5}\x{73A6}\x{73A7}\x{73A8}\x{73A9}\x{73AA}\x{73AB}' .
+'\x{73AC}\x{73AD}\x{73AE}\x{73AF}\x{73B0}\x{73B1}\x{73B2}\x{73B3}\x{73B4}' .
+'\x{73B5}\x{73B6}\x{73B7}\x{73B8}\x{73B9}\x{73BA}\x{73BB}\x{73BC}\x{73BD}' .
+'\x{73BE}\x{73BF}\x{73C0}\x{73C2}\x{73C3}\x{73C4}\x{73C5}\x{73C6}\x{73C7}' .
+'\x{73C8}\x{73C9}\x{73CA}\x{73CB}\x{73CC}\x{73CD}\x{73CE}\x{73CF}\x{73D0}' .
+'\x{73D1}\x{73D2}\x{73D3}\x{73D4}\x{73D5}\x{73D6}\x{73D7}\x{73D8}\x{73D9}' .
+'\x{73DA}\x{73DB}\x{73DC}\x{73DD}\x{73DE}\x{73DF}\x{73E0}\x{73E2}\x{73E3}' .
+'\x{73E5}\x{73E6}\x{73E7}\x{73E8}\x{73E9}\x{73EA}\x{73EB}\x{73EC}\x{73ED}' .
+'\x{73EE}\x{73EF}\x{73F0}\x{73F1}\x{73F2}\x{73F4}\x{73F5}\x{73F6}\x{73F7}' .
+'\x{73F8}\x{73F9}\x{73FA}\x{73FC}\x{73FD}\x{73FE}\x{73FF}\x{7400}\x{7401}' .
+'\x{7402}\x{7403}\x{7404}\x{7405}\x{7406}\x{7407}\x{7408}\x{7409}\x{740A}' .
+'\x{740B}\x{740C}\x{740D}\x{740E}\x{740F}\x{7410}\x{7411}\x{7412}\x{7413}' .
+'\x{7414}\x{7415}\x{7416}\x{7417}\x{7419}\x{741A}\x{741B}\x{741C}\x{741D}' .
+'\x{741E}\x{741F}\x{7420}\x{7421}\x{7422}\x{7423}\x{7424}\x{7425}\x{7426}' .
+'\x{7427}\x{7428}\x{7429}\x{742A}\x{742B}\x{742C}\x{742D}\x{742E}\x{742F}' .
+'\x{7430}\x{7431}\x{7432}\x{7433}\x{7434}\x{7435}\x{7436}\x{7437}\x{7438}' .
+'\x{743A}\x{743B}\x{743C}\x{743D}\x{743F}\x{7440}\x{7441}\x{7442}\x{7443}' .
+'\x{7444}\x{7445}\x{7446}\x{7448}\x{744A}\x{744B}\x{744C}\x{744D}\x{744E}' .
+'\x{744F}\x{7450}\x{7451}\x{7452}\x{7453}\x{7454}\x{7455}\x{7456}\x{7457}' .
+'\x{7459}\x{745A}\x{745B}\x{745C}\x{745D}\x{745E}\x{745F}\x{7461}\x{7462}' .
+'\x{7463}\x{7464}\x{7465}\x{7466}\x{7467}\x{7468}\x{7469}\x{746A}\x{746B}' .
+'\x{746C}\x{746D}\x{746E}\x{746F}\x{7470}\x{7471}\x{7472}\x{7473}\x{7474}' .
+'\x{7475}\x{7476}\x{7477}\x{7478}\x{7479}\x{747A}\x{747C}\x{747D}\x{747E}' .
+'\x{747F}\x{7480}\x{7481}\x{7482}\x{7483}\x{7485}\x{7486}\x{7487}\x{7488}' .
+'\x{7489}\x{748A}\x{748B}\x{748C}\x{748D}\x{748E}\x{748F}\x{7490}\x{7491}' .
+'\x{7492}\x{7493}\x{7494}\x{7495}\x{7497}\x{7498}\x{7499}\x{749A}\x{749B}' .
+'\x{749C}\x{749E}\x{749F}\x{74A0}\x{74A1}\x{74A3}\x{74A4}\x{74A5}\x{74A6}' .
+'\x{74A7}\x{74A8}\x{74A9}\x{74AA}\x{74AB}\x{74AC}\x{74AD}\x{74AE}\x{74AF}' .
+'\x{74B0}\x{74B1}\x{74B2}\x{74B3}\x{74B4}\x{74B5}\x{74B6}\x{74B7}\x{74B8}' .
+'\x{74B9}\x{74BA}\x{74BB}\x{74BC}\x{74BD}\x{74BE}\x{74BF}\x{74C0}\x{74C1}' .
+'\x{74C2}\x{74C3}\x{74C4}\x{74C5}\x{74C6}\x{74CA}\x{74CB}\x{74CD}\x{74CE}' .
+'\x{74CF}\x{74D0}\x{74D1}\x{74D2}\x{74D3}\x{74D4}\x{74D5}\x{74D6}\x{74D7}' .
+'\x{74D8}\x{74D9}\x{74DA}\x{74DB}\x{74DC}\x{74DD}\x{74DE}\x{74DF}\x{74E0}' .
+'\x{74E1}\x{74E2}\x{74E3}\x{74E4}\x{74E5}\x{74E6}\x{74E7}\x{74E8}\x{74E9}' .
+'\x{74EA}\x{74EC}\x{74ED}\x{74EE}\x{74EF}\x{74F0}\x{74F1}\x{74F2}\x{74F3}' .
+'\x{74F4}\x{74F5}\x{74F6}\x{74F7}\x{74F8}\x{74F9}\x{74FA}\x{74FB}\x{74FC}' .
+'\x{74FD}\x{74FE}\x{74FF}\x{7500}\x{7501}\x{7502}\x{7503}\x{7504}\x{7505}' .
+'\x{7506}\x{7507}\x{7508}\x{7509}\x{750A}\x{750B}\x{750C}\x{750D}\x{750F}' .
+'\x{7510}\x{7511}\x{7512}\x{7513}\x{7514}\x{7515}\x{7516}\x{7517}\x{7518}' .
+'\x{7519}\x{751A}\x{751B}\x{751C}\x{751D}\x{751E}\x{751F}\x{7521}\x{7522}' .
+'\x{7523}\x{7524}\x{7525}\x{7526}\x{7527}\x{7528}\x{7529}\x{752A}\x{752B}' .
+'\x{752C}\x{752D}\x{752E}\x{752F}\x{7530}\x{7531}\x{7532}\x{7533}\x{7535}' .
+'\x{7536}\x{7537}\x{7538}\x{7539}\x{753A}\x{753B}\x{753C}\x{753D}\x{753E}' .
+'\x{753F}\x{7540}\x{7542}\x{7543}\x{7544}\x{7545}\x{7546}\x{7547}\x{7548}' .
+'\x{7549}\x{754B}\x{754C}\x{754D}\x{754E}\x{754F}\x{7550}\x{7551}\x{7553}' .
+'\x{7554}\x{7556}\x{7557}\x{7558}\x{7559}\x{755A}\x{755B}\x{755C}\x{755D}' .
+'\x{755F}\x{7560}\x{7562}\x{7563}\x{7564}\x{7565}\x{7566}\x{7567}\x{7568}' .
+'\x{7569}\x{756A}\x{756B}\x{756C}\x{756D}\x{756E}\x{756F}\x{7570}\x{7572}' .
+'\x{7574}\x{7575}\x{7576}\x{7577}\x{7578}\x{7579}\x{757C}\x{757D}\x{757E}' .
+'\x{757F}\x{7580}\x{7581}\x{7582}\x{7583}\x{7584}\x{7586}\x{7587}\x{7588}' .
+'\x{7589}\x{758A}\x{758B}\x{758C}\x{758D}\x{758F}\x{7590}\x{7591}\x{7592}' .
+'\x{7593}\x{7594}\x{7595}\x{7596}\x{7597}\x{7598}\x{7599}\x{759A}\x{759B}' .
+'\x{759C}\x{759D}\x{759E}\x{759F}\x{75A0}\x{75A1}\x{75A2}\x{75A3}\x{75A4}' .
+'\x{75A5}\x{75A6}\x{75A7}\x{75A8}\x{75AA}\x{75AB}\x{75AC}\x{75AD}\x{75AE}' .
+'\x{75AF}\x{75B0}\x{75B1}\x{75B2}\x{75B3}\x{75B4}\x{75B5}\x{75B6}\x{75B8}' .
+'\x{75B9}\x{75BA}\x{75BB}\x{75BC}\x{75BD}\x{75BE}\x{75BF}\x{75C0}\x{75C1}' .
+'\x{75C2}\x{75C3}\x{75C4}\x{75C5}\x{75C6}\x{75C7}\x{75C8}\x{75C9}\x{75CA}' .
+'\x{75CB}\x{75CC}\x{75CD}\x{75CE}\x{75CF}\x{75D0}\x{75D1}\x{75D2}\x{75D3}' .
+'\x{75D4}\x{75D5}\x{75D6}\x{75D7}\x{75D8}\x{75D9}\x{75DA}\x{75DB}\x{75DD}' .
+'\x{75DE}\x{75DF}\x{75E0}\x{75E1}\x{75E2}\x{75E3}\x{75E4}\x{75E5}\x{75E6}' .
+'\x{75E7}\x{75E8}\x{75EA}\x{75EB}\x{75EC}\x{75ED}\x{75EF}\x{75F0}\x{75F1}' .
+'\x{75F2}\x{75F3}\x{75F4}\x{75F5}\x{75F6}\x{75F7}\x{75F8}\x{75F9}\x{75FA}' .
+'\x{75FB}\x{75FC}\x{75FD}\x{75FE}\x{75FF}\x{7600}\x{7601}\x{7602}\x{7603}' .
+'\x{7604}\x{7605}\x{7606}\x{7607}\x{7608}\x{7609}\x{760A}\x{760B}\x{760C}' .
+'\x{760D}\x{760E}\x{760F}\x{7610}\x{7611}\x{7612}\x{7613}\x{7614}\x{7615}' .
+'\x{7616}\x{7617}\x{7618}\x{7619}\x{761A}\x{761B}\x{761C}\x{761D}\x{761E}' .
+'\x{761F}\x{7620}\x{7621}\x{7622}\x{7623}\x{7624}\x{7625}\x{7626}\x{7627}' .
+'\x{7628}\x{7629}\x{762A}\x{762B}\x{762D}\x{762E}\x{762F}\x{7630}\x{7631}' .
+'\x{7632}\x{7633}\x{7634}\x{7635}\x{7636}\x{7637}\x{7638}\x{7639}\x{763A}' .
+'\x{763B}\x{763C}\x{763D}\x{763E}\x{763F}\x{7640}\x{7641}\x{7642}\x{7643}' .
+'\x{7646}\x{7647}\x{7648}\x{7649}\x{764A}\x{764B}\x{764C}\x{764D}\x{764F}' .
+'\x{7650}\x{7652}\x{7653}\x{7654}\x{7656}\x{7657}\x{7658}\x{7659}\x{765A}' .
+'\x{765B}\x{765C}\x{765D}\x{765E}\x{765F}\x{7660}\x{7661}\x{7662}\x{7663}' .
+'\x{7664}\x{7665}\x{7666}\x{7667}\x{7668}\x{7669}\x{766A}\x{766B}\x{766C}' .
+'\x{766D}\x{766E}\x{766F}\x{7670}\x{7671}\x{7672}\x{7674}\x{7675}\x{7676}' .
+'\x{7677}\x{7678}\x{7679}\x{767B}\x{767C}\x{767D}\x{767E}\x{767F}\x{7680}' .
+'\x{7681}\x{7682}\x{7683}\x{7684}\x{7685}\x{7686}\x{7687}\x{7688}\x{7689}' .
+'\x{768A}\x{768B}\x{768C}\x{768E}\x{768F}\x{7690}\x{7691}\x{7692}\x{7693}' .
+'\x{7694}\x{7695}\x{7696}\x{7697}\x{7698}\x{7699}\x{769A}\x{769B}\x{769C}' .
+'\x{769D}\x{769E}\x{769F}\x{76A0}\x{76A3}\x{76A4}\x{76A6}\x{76A7}\x{76A9}' .
+'\x{76AA}\x{76AB}\x{76AC}\x{76AD}\x{76AE}\x{76AF}\x{76B0}\x{76B1}\x{76B2}' .
+'\x{76B4}\x{76B5}\x{76B7}\x{76B8}\x{76BA}\x{76BB}\x{76BC}\x{76BD}\x{76BE}' .
+'\x{76BF}\x{76C0}\x{76C2}\x{76C3}\x{76C4}\x{76C5}\x{76C6}\x{76C7}\x{76C8}' .
+'\x{76C9}\x{76CA}\x{76CD}\x{76CE}\x{76CF}\x{76D0}\x{76D1}\x{76D2}\x{76D3}' .
+'\x{76D4}\x{76D5}\x{76D6}\x{76D7}\x{76D8}\x{76DA}\x{76DB}\x{76DC}\x{76DD}' .
+'\x{76DE}\x{76DF}\x{76E0}\x{76E1}\x{76E2}\x{76E3}\x{76E4}\x{76E5}\x{76E6}' .
+'\x{76E7}\x{76E8}\x{76E9}\x{76EA}\x{76EC}\x{76ED}\x{76EE}\x{76EF}\x{76F0}' .
+'\x{76F1}\x{76F2}\x{76F3}\x{76F4}\x{76F5}\x{76F6}\x{76F7}\x{76F8}\x{76F9}' .
+'\x{76FA}\x{76FB}\x{76FC}\x{76FD}\x{76FE}\x{76FF}\x{7701}\x{7703}\x{7704}' .
+'\x{7705}\x{7706}\x{7707}\x{7708}\x{7709}\x{770A}\x{770B}\x{770C}\x{770D}' .
+'\x{770F}\x{7710}\x{7711}\x{7712}\x{7713}\x{7714}\x{7715}\x{7716}\x{7717}' .
+'\x{7718}\x{7719}\x{771A}\x{771B}\x{771C}\x{771D}\x{771E}\x{771F}\x{7720}' .
+'\x{7722}\x{7723}\x{7725}\x{7726}\x{7727}\x{7728}\x{7729}\x{772A}\x{772C}' .
+'\x{772D}\x{772E}\x{772F}\x{7730}\x{7731}\x{7732}\x{7733}\x{7734}\x{7735}' .
+'\x{7736}\x{7737}\x{7738}\x{7739}\x{773A}\x{773B}\x{773C}\x{773D}\x{773E}' .
+'\x{7740}\x{7741}\x{7743}\x{7744}\x{7745}\x{7746}\x{7747}\x{7748}\x{7749}' .
+'\x{774A}\x{774B}\x{774C}\x{774D}\x{774E}\x{774F}\x{7750}\x{7751}\x{7752}' .
+'\x{7753}\x{7754}\x{7755}\x{7756}\x{7757}\x{7758}\x{7759}\x{775A}\x{775B}' .
+'\x{775C}\x{775D}\x{775E}\x{775F}\x{7760}\x{7761}\x{7762}\x{7763}\x{7765}' .
+'\x{7766}\x{7767}\x{7768}\x{7769}\x{776A}\x{776B}\x{776C}\x{776D}\x{776E}' .
+'\x{776F}\x{7770}\x{7771}\x{7772}\x{7773}\x{7774}\x{7775}\x{7776}\x{7777}' .
+'\x{7778}\x{7779}\x{777A}\x{777B}\x{777C}\x{777D}\x{777E}\x{777F}\x{7780}' .
+'\x{7781}\x{7782}\x{7783}\x{7784}\x{7785}\x{7786}\x{7787}\x{7788}\x{7789}' .
+'\x{778A}\x{778B}\x{778C}\x{778D}\x{778E}\x{778F}\x{7790}\x{7791}\x{7792}' .
+'\x{7793}\x{7794}\x{7795}\x{7797}\x{7798}\x{7799}\x{779A}\x{779B}\x{779C}' .
+'\x{779D}\x{779E}\x{779F}\x{77A0}\x{77A1}\x{77A2}\x{77A3}\x{77A5}\x{77A6}' .
+'\x{77A7}\x{77A8}\x{77A9}\x{77AA}\x{77AB}\x{77AC}\x{77AD}\x{77AE}\x{77AF}' .
+'\x{77B0}\x{77B1}\x{77B2}\x{77B3}\x{77B4}\x{77B5}\x{77B6}\x{77B7}\x{77B8}' .
+'\x{77B9}\x{77BA}\x{77BB}\x{77BC}\x{77BD}\x{77BF}\x{77C0}\x{77C2}\x{77C3}' .
+'\x{77C4}\x{77C5}\x{77C6}\x{77C7}\x{77C8}\x{77C9}\x{77CA}\x{77CB}\x{77CC}' .
+'\x{77CD}\x{77CE}\x{77CF}\x{77D0}\x{77D1}\x{77D3}\x{77D4}\x{77D5}\x{77D6}' .
+'\x{77D7}\x{77D8}\x{77D9}\x{77DA}\x{77DB}\x{77DC}\x{77DE}\x{77DF}\x{77E0}' .
+'\x{77E1}\x{77E2}\x{77E3}\x{77E5}\x{77E7}\x{77E8}\x{77E9}\x{77EA}\x{77EB}' .
+'\x{77EC}\x{77ED}\x{77EE}\x{77EF}\x{77F0}\x{77F1}\x{77F2}\x{77F3}\x{77F6}' .
+'\x{77F7}\x{77F8}\x{77F9}\x{77FA}\x{77FB}\x{77FC}\x{77FD}\x{77FE}\x{77FF}' .
+'\x{7800}\x{7801}\x{7802}\x{7803}\x{7804}\x{7805}\x{7806}\x{7808}\x{7809}' .
+'\x{780A}\x{780B}\x{780C}\x{780D}\x{780E}\x{780F}\x{7810}\x{7811}\x{7812}' .
+'\x{7813}\x{7814}\x{7815}\x{7816}\x{7817}\x{7818}\x{7819}\x{781A}\x{781B}' .
+'\x{781C}\x{781D}\x{781E}\x{781F}\x{7820}\x{7821}\x{7822}\x{7823}\x{7825}' .
+'\x{7826}\x{7827}\x{7828}\x{7829}\x{782A}\x{782B}\x{782C}\x{782D}\x{782E}' .
+'\x{782F}\x{7830}\x{7831}\x{7832}\x{7833}\x{7834}\x{7835}\x{7837}\x{7838}' .
+'\x{7839}\x{783A}\x{783B}\x{783C}\x{783D}\x{783E}\x{7840}\x{7841}\x{7843}' .
+'\x{7844}\x{7845}\x{7847}\x{7848}\x{7849}\x{784A}\x{784C}\x{784D}\x{784E}' .
+'\x{7850}\x{7851}\x{7852}\x{7853}\x{7854}\x{7855}\x{7856}\x{7857}\x{7858}' .
+'\x{7859}\x{785A}\x{785B}\x{785C}\x{785D}\x{785E}\x{785F}\x{7860}\x{7861}' .
+'\x{7862}\x{7863}\x{7864}\x{7865}\x{7866}\x{7867}\x{7868}\x{7869}\x{786A}' .
+'\x{786B}\x{786C}\x{786D}\x{786E}\x{786F}\x{7870}\x{7871}\x{7872}\x{7873}' .
+'\x{7874}\x{7875}\x{7877}\x{7878}\x{7879}\x{787A}\x{787B}\x{787C}\x{787D}' .
+'\x{787E}\x{787F}\x{7880}\x{7881}\x{7882}\x{7883}\x{7884}\x{7885}\x{7886}' .
+'\x{7887}\x{7889}\x{788A}\x{788B}\x{788C}\x{788D}\x{788E}\x{788F}\x{7890}' .
+'\x{7891}\x{7892}\x{7893}\x{7894}\x{7895}\x{7896}\x{7897}\x{7898}\x{7899}' .
+'\x{789A}\x{789B}\x{789C}\x{789D}\x{789E}\x{789F}\x{78A0}\x{78A1}\x{78A2}' .
+'\x{78A3}\x{78A4}\x{78A5}\x{78A6}\x{78A7}\x{78A8}\x{78A9}\x{78AA}\x{78AB}' .
+'\x{78AC}\x{78AD}\x{78AE}\x{78AF}\x{78B0}\x{78B1}\x{78B2}\x{78B3}\x{78B4}' .
+'\x{78B5}\x{78B6}\x{78B7}\x{78B8}\x{78B9}\x{78BA}\x{78BB}\x{78BC}\x{78BD}' .
+'\x{78BE}\x{78BF}\x{78C0}\x{78C1}\x{78C3}\x{78C4}\x{78C5}\x{78C6}\x{78C8}' .
+'\x{78C9}\x{78CA}\x{78CB}\x{78CC}\x{78CD}\x{78CE}\x{78CF}\x{78D0}\x{78D1}' .
+'\x{78D3}\x{78D4}\x{78D5}\x{78D6}\x{78D7}\x{78D8}\x{78D9}\x{78DA}\x{78DB}' .
+'\x{78DC}\x{78DD}\x{78DE}\x{78DF}\x{78E0}\x{78E1}\x{78E2}\x{78E3}\x{78E4}' .
+'\x{78E5}\x{78E6}\x{78E7}\x{78E8}\x{78E9}\x{78EA}\x{78EB}\x{78EC}\x{78ED}' .
+'\x{78EE}\x{78EF}\x{78F1}\x{78F2}\x{78F3}\x{78F4}\x{78F5}\x{78F6}\x{78F7}' .
+'\x{78F9}\x{78FA}\x{78FB}\x{78FC}\x{78FD}\x{78FE}\x{78FF}\x{7901}\x{7902}' .
+'\x{7903}\x{7904}\x{7905}\x{7906}\x{7907}\x{7909}\x{790A}\x{790B}\x{790C}' .
+'\x{790E}\x{790F}\x{7910}\x{7911}\x{7912}\x{7913}\x{7914}\x{7916}\x{7917}' .
+'\x{7918}\x{7919}\x{791A}\x{791B}\x{791C}\x{791D}\x{791E}\x{7921}\x{7922}' .
+'\x{7923}\x{7924}\x{7925}\x{7926}\x{7927}\x{7928}\x{7929}\x{792A}\x{792B}' .
+'\x{792C}\x{792D}\x{792E}\x{792F}\x{7930}\x{7931}\x{7933}\x{7934}\x{7935}' .
+'\x{7937}\x{7938}\x{7939}\x{793A}\x{793B}\x{793C}\x{793D}\x{793E}\x{793F}' .
+'\x{7940}\x{7941}\x{7942}\x{7943}\x{7944}\x{7945}\x{7946}\x{7947}\x{7948}' .
+'\x{7949}\x{794A}\x{794B}\x{794C}\x{794D}\x{794E}\x{794F}\x{7950}\x{7951}' .
+'\x{7952}\x{7953}\x{7954}\x{7955}\x{7956}\x{7957}\x{7958}\x{795A}\x{795B}' .
+'\x{795C}\x{795D}\x{795E}\x{795F}\x{7960}\x{7961}\x{7962}\x{7963}\x{7964}' .
+'\x{7965}\x{7966}\x{7967}\x{7968}\x{7969}\x{796A}\x{796B}\x{796D}\x{796F}' .
+'\x{7970}\x{7971}\x{7972}\x{7973}\x{7974}\x{7977}\x{7978}\x{7979}\x{797A}' .
+'\x{797B}\x{797C}\x{797D}\x{797E}\x{797F}\x{7980}\x{7981}\x{7982}\x{7983}' .
+'\x{7984}\x{7985}\x{7988}\x{7989}\x{798A}\x{798B}\x{798C}\x{798D}\x{798E}' .
+'\x{798F}\x{7990}\x{7991}\x{7992}\x{7993}\x{7994}\x{7995}\x{7996}\x{7997}' .
+'\x{7998}\x{7999}\x{799A}\x{799B}\x{799C}\x{799F}\x{79A0}\x{79A1}\x{79A2}' .
+'\x{79A3}\x{79A4}\x{79A5}\x{79A6}\x{79A7}\x{79A8}\x{79AA}\x{79AB}\x{79AC}' .
+'\x{79AD}\x{79AE}\x{79AF}\x{79B0}\x{79B1}\x{79B2}\x{79B3}\x{79B4}\x{79B5}' .
+'\x{79B6}\x{79B7}\x{79B8}\x{79B9}\x{79BA}\x{79BB}\x{79BD}\x{79BE}\x{79BF}' .
+'\x{79C0}\x{79C1}\x{79C2}\x{79C3}\x{79C5}\x{79C6}\x{79C8}\x{79C9}\x{79CA}' .
+'\x{79CB}\x{79CD}\x{79CE}\x{79CF}\x{79D0}\x{79D1}\x{79D2}\x{79D3}\x{79D5}' .
+'\x{79D6}\x{79D8}\x{79D9}\x{79DA}\x{79DB}\x{79DC}\x{79DD}\x{79DE}\x{79DF}' .
+'\x{79E0}\x{79E1}\x{79E2}\x{79E3}\x{79E4}\x{79E5}\x{79E6}\x{79E7}\x{79E8}' .
+'\x{79E9}\x{79EA}\x{79EB}\x{79EC}\x{79ED}\x{79EE}\x{79EF}\x{79F0}\x{79F1}' .
+'\x{79F2}\x{79F3}\x{79F4}\x{79F5}\x{79F6}\x{79F7}\x{79F8}\x{79F9}\x{79FA}' .
+'\x{79FB}\x{79FC}\x{79FD}\x{79FE}\x{79FF}\x{7A00}\x{7A02}\x{7A03}\x{7A04}' .
+'\x{7A05}\x{7A06}\x{7A08}\x{7A0A}\x{7A0B}\x{7A0C}\x{7A0D}\x{7A0E}\x{7A0F}' .
+'\x{7A10}\x{7A11}\x{7A12}\x{7A13}\x{7A14}\x{7A15}\x{7A16}\x{7A17}\x{7A18}' .
+'\x{7A19}\x{7A1A}\x{7A1B}\x{7A1C}\x{7A1D}\x{7A1E}\x{7A1F}\x{7A20}\x{7A21}' .
+'\x{7A22}\x{7A23}\x{7A24}\x{7A25}\x{7A26}\x{7A27}\x{7A28}\x{7A29}\x{7A2A}' .
+'\x{7A2B}\x{7A2D}\x{7A2E}\x{7A2F}\x{7A30}\x{7A31}\x{7A32}\x{7A33}\x{7A34}' .
+'\x{7A35}\x{7A37}\x{7A39}\x{7A3B}\x{7A3C}\x{7A3D}\x{7A3E}\x{7A3F}\x{7A40}' .
+'\x{7A41}\x{7A42}\x{7A43}\x{7A44}\x{7A45}\x{7A46}\x{7A47}\x{7A48}\x{7A49}' .
+'\x{7A4A}\x{7A4B}\x{7A4C}\x{7A4D}\x{7A4E}\x{7A50}\x{7A51}\x{7A52}\x{7A53}' .
+'\x{7A54}\x{7A55}\x{7A56}\x{7A57}\x{7A58}\x{7A59}\x{7A5A}\x{7A5B}\x{7A5C}' .
+'\x{7A5D}\x{7A5E}\x{7A5F}\x{7A60}\x{7A61}\x{7A62}\x{7A65}\x{7A66}\x{7A67}' .
+'\x{7A68}\x{7A69}\x{7A6B}\x{7A6C}\x{7A6D}\x{7A6E}\x{7A70}\x{7A71}\x{7A72}' .
+'\x{7A73}\x{7A74}\x{7A75}\x{7A76}\x{7A77}\x{7A78}\x{7A79}\x{7A7A}\x{7A7B}' .
+'\x{7A7C}\x{7A7D}\x{7A7E}\x{7A7F}\x{7A80}\x{7A81}\x{7A83}\x{7A84}\x{7A85}' .
+'\x{7A86}\x{7A87}\x{7A88}\x{7A89}\x{7A8A}\x{7A8B}\x{7A8C}\x{7A8D}\x{7A8E}' .
+'\x{7A8F}\x{7A90}\x{7A91}\x{7A92}\x{7A93}\x{7A94}\x{7A95}\x{7A96}\x{7A97}' .
+'\x{7A98}\x{7A99}\x{7A9C}\x{7A9D}\x{7A9E}\x{7A9F}\x{7AA0}\x{7AA1}\x{7AA2}' .
+'\x{7AA3}\x{7AA4}\x{7AA5}\x{7AA6}\x{7AA7}\x{7AA8}\x{7AA9}\x{7AAA}\x{7AAB}' .
+'\x{7AAC}\x{7AAD}\x{7AAE}\x{7AAF}\x{7AB0}\x{7AB1}\x{7AB2}\x{7AB3}\x{7AB4}' .
+'\x{7AB5}\x{7AB6}\x{7AB7}\x{7AB8}\x{7ABA}\x{7ABE}\x{7ABF}\x{7AC0}\x{7AC1}' .
+'\x{7AC4}\x{7AC5}\x{7AC7}\x{7AC8}\x{7AC9}\x{7ACA}\x{7ACB}\x{7ACC}\x{7ACD}' .
+'\x{7ACE}\x{7ACF}\x{7AD0}\x{7AD1}\x{7AD2}\x{7AD3}\x{7AD4}\x{7AD5}\x{7AD6}' .
+'\x{7AD8}\x{7AD9}\x{7ADB}\x{7ADC}\x{7ADD}\x{7ADE}\x{7ADF}\x{7AE0}\x{7AE1}' .
+'\x{7AE2}\x{7AE3}\x{7AE4}\x{7AE5}\x{7AE6}\x{7AE7}\x{7AE8}\x{7AEA}\x{7AEB}' .
+'\x{7AEC}\x{7AED}\x{7AEE}\x{7AEF}\x{7AF0}\x{7AF1}\x{7AF2}\x{7AF3}\x{7AF4}' .
+'\x{7AF6}\x{7AF7}\x{7AF8}\x{7AF9}\x{7AFA}\x{7AFB}\x{7AFD}\x{7AFE}\x{7AFF}' .
+'\x{7B00}\x{7B01}\x{7B02}\x{7B03}\x{7B04}\x{7B05}\x{7B06}\x{7B08}\x{7B09}' .
+'\x{7B0A}\x{7B0B}\x{7B0C}\x{7B0D}\x{7B0E}\x{7B0F}\x{7B10}\x{7B11}\x{7B12}' .
+'\x{7B13}\x{7B14}\x{7B15}\x{7B16}\x{7B17}\x{7B18}\x{7B19}\x{7B1A}\x{7B1B}' .
+'\x{7B1C}\x{7B1D}\x{7B1E}\x{7B20}\x{7B21}\x{7B22}\x{7B23}\x{7B24}\x{7B25}' .
+'\x{7B26}\x{7B28}\x{7B2A}\x{7B2B}\x{7B2C}\x{7B2D}\x{7B2E}\x{7B2F}\x{7B30}' .
+'\x{7B31}\x{7B32}\x{7B33}\x{7B34}\x{7B35}\x{7B36}\x{7B37}\x{7B38}\x{7B39}' .
+'\x{7B3A}\x{7B3B}\x{7B3C}\x{7B3D}\x{7B3E}\x{7B3F}\x{7B40}\x{7B41}\x{7B43}' .
+'\x{7B44}\x{7B45}\x{7B46}\x{7B47}\x{7B48}\x{7B49}\x{7B4A}\x{7B4B}\x{7B4C}' .
+'\x{7B4D}\x{7B4E}\x{7B4F}\x{7B50}\x{7B51}\x{7B52}\x{7B54}\x{7B55}\x{7B56}' .
+'\x{7B57}\x{7B58}\x{7B59}\x{7B5A}\x{7B5B}\x{7B5C}\x{7B5D}\x{7B5E}\x{7B5F}' .
+'\x{7B60}\x{7B61}\x{7B62}\x{7B63}\x{7B64}\x{7B65}\x{7B66}\x{7B67}\x{7B68}' .
+'\x{7B69}\x{7B6A}\x{7B6B}\x{7B6C}\x{7B6D}\x{7B6E}\x{7B70}\x{7B71}\x{7B72}' .
+'\x{7B73}\x{7B74}\x{7B75}\x{7B76}\x{7B77}\x{7B78}\x{7B79}\x{7B7B}\x{7B7C}' .
+'\x{7B7D}\x{7B7E}\x{7B7F}\x{7B80}\x{7B81}\x{7B82}\x{7B83}\x{7B84}\x{7B85}' .
+'\x{7B87}\x{7B88}\x{7B89}\x{7B8A}\x{7B8B}\x{7B8C}\x{7B8D}\x{7B8E}\x{7B8F}' .
+'\x{7B90}\x{7B91}\x{7B93}\x{7B94}\x{7B95}\x{7B96}\x{7B97}\x{7B98}\x{7B99}' .
+'\x{7B9A}\x{7B9B}\x{7B9C}\x{7B9D}\x{7B9E}\x{7B9F}\x{7BA0}\x{7BA1}\x{7BA2}' .
+'\x{7BA4}\x{7BA6}\x{7BA7}\x{7BA8}\x{7BA9}\x{7BAA}\x{7BAB}\x{7BAC}\x{7BAD}' .
+'\x{7BAE}\x{7BAF}\x{7BB1}\x{7BB3}\x{7BB4}\x{7BB5}\x{7BB6}\x{7BB7}\x{7BB8}' .
+'\x{7BB9}\x{7BBA}\x{7BBB}\x{7BBC}\x{7BBD}\x{7BBE}\x{7BBF}\x{7BC0}\x{7BC1}' .
+'\x{7BC2}\x{7BC3}\x{7BC4}\x{7BC5}\x{7BC6}\x{7BC7}\x{7BC8}\x{7BC9}\x{7BCA}' .
+'\x{7BCB}\x{7BCC}\x{7BCD}\x{7BCE}\x{7BD0}\x{7BD1}\x{7BD2}\x{7BD3}\x{7BD4}' .
+'\x{7BD5}\x{7BD6}\x{7BD7}\x{7BD8}\x{7BD9}\x{7BDA}\x{7BDB}\x{7BDC}\x{7BDD}' .
+'\x{7BDE}\x{7BDF}\x{7BE0}\x{7BE1}\x{7BE2}\x{7BE3}\x{7BE4}\x{7BE5}\x{7BE6}' .
+'\x{7BE7}\x{7BE8}\x{7BE9}\x{7BEA}\x{7BEB}\x{7BEC}\x{7BED}\x{7BEE}\x{7BEF}' .
+'\x{7BF0}\x{7BF1}\x{7BF2}\x{7BF3}\x{7BF4}\x{7BF5}\x{7BF6}\x{7BF7}\x{7BF8}' .
+'\x{7BF9}\x{7BFB}\x{7BFC}\x{7BFD}\x{7BFE}\x{7BFF}\x{7C00}\x{7C01}\x{7C02}' .
+'\x{7C03}\x{7C04}\x{7C05}\x{7C06}\x{7C07}\x{7C08}\x{7C09}\x{7C0A}\x{7C0B}' .
+'\x{7C0C}\x{7C0D}\x{7C0E}\x{7C0F}\x{7C10}\x{7C11}\x{7C12}\x{7C13}\x{7C15}' .
+'\x{7C16}\x{7C17}\x{7C18}\x{7C19}\x{7C1A}\x{7C1C}\x{7C1D}\x{7C1E}\x{7C1F}' .
+'\x{7C20}\x{7C21}\x{7C22}\x{7C23}\x{7C24}\x{7C25}\x{7C26}\x{7C27}\x{7C28}' .
+'\x{7C29}\x{7C2A}\x{7C2B}\x{7C2C}\x{7C2D}\x{7C30}\x{7C31}\x{7C32}\x{7C33}' .
+'\x{7C34}\x{7C35}\x{7C36}\x{7C37}\x{7C38}\x{7C39}\x{7C3A}\x{7C3B}\x{7C3C}' .
+'\x{7C3D}\x{7C3E}\x{7C3F}\x{7C40}\x{7C41}\x{7C42}\x{7C43}\x{7C44}\x{7C45}' .
+'\x{7C46}\x{7C47}\x{7C48}\x{7C49}\x{7C4A}\x{7C4B}\x{7C4C}\x{7C4D}\x{7C4E}' .
+'\x{7C50}\x{7C51}\x{7C53}\x{7C54}\x{7C56}\x{7C57}\x{7C58}\x{7C59}\x{7C5A}' .
+'\x{7C5B}\x{7C5C}\x{7C5E}\x{7C5F}\x{7C60}\x{7C61}\x{7C62}\x{7C63}\x{7C64}' .
+'\x{7C65}\x{7C66}\x{7C67}\x{7C68}\x{7C69}\x{7C6A}\x{7C6B}\x{7C6C}\x{7C6D}' .
+'\x{7C6E}\x{7C6F}\x{7C70}\x{7C71}\x{7C72}\x{7C73}\x{7C74}\x{7C75}\x{7C77}' .
+'\x{7C78}\x{7C79}\x{7C7A}\x{7C7B}\x{7C7C}\x{7C7D}\x{7C7E}\x{7C7F}\x{7C80}' .
+'\x{7C81}\x{7C82}\x{7C84}\x{7C85}\x{7C86}\x{7C88}\x{7C89}\x{7C8A}\x{7C8B}' .
+'\x{7C8C}\x{7C8D}\x{7C8E}\x{7C8F}\x{7C90}\x{7C91}\x{7C92}\x{7C94}\x{7C95}' .
+'\x{7C96}\x{7C97}\x{7C98}\x{7C99}\x{7C9B}\x{7C9C}\x{7C9D}\x{7C9E}\x{7C9F}' .
+'\x{7CA0}\x{7CA1}\x{7CA2}\x{7CA3}\x{7CA4}\x{7CA5}\x{7CA6}\x{7CA7}\x{7CA8}' .
+'\x{7CA9}\x{7CAA}\x{7CAD}\x{7CAE}\x{7CAF}\x{7CB0}\x{7CB1}\x{7CB2}\x{7CB3}' .
+'\x{7CB4}\x{7CB5}\x{7CB6}\x{7CB7}\x{7CB8}\x{7CB9}\x{7CBA}\x{7CBB}\x{7CBC}' .
+'\x{7CBD}\x{7CBE}\x{7CBF}\x{7CC0}\x{7CC1}\x{7CC2}\x{7CC3}\x{7CC4}\x{7CC5}' .
+'\x{7CC6}\x{7CC7}\x{7CC8}\x{7CC9}\x{7CCA}\x{7CCB}\x{7CCC}\x{7CCD}\x{7CCE}' .
+'\x{7CCF}\x{7CD0}\x{7CD1}\x{7CD2}\x{7CD4}\x{7CD5}\x{7CD6}\x{7CD7}\x{7CD8}' .
+'\x{7CD9}\x{7CDC}\x{7CDD}\x{7CDE}\x{7CDF}\x{7CE0}\x{7CE2}\x{7CE4}\x{7CE7}' .
+'\x{7CE8}\x{7CE9}\x{7CEA}\x{7CEB}\x{7CEC}\x{7CED}\x{7CEE}\x{7CEF}\x{7CF0}' .
+'\x{7CF1}\x{7CF2}\x{7CF3}\x{7CF4}\x{7CF5}\x{7CF6}\x{7CF7}\x{7CF8}\x{7CF9}' .
+'\x{7CFA}\x{7CFB}\x{7CFD}\x{7CFE}\x{7D00}\x{7D01}\x{7D02}\x{7D03}\x{7D04}' .
+'\x{7D05}\x{7D06}\x{7D07}\x{7D08}\x{7D09}\x{7D0A}\x{7D0B}\x{7D0C}\x{7D0D}' .
+'\x{7D0E}\x{7D0F}\x{7D10}\x{7D11}\x{7D12}\x{7D13}\x{7D14}\x{7D15}\x{7D16}' .
+'\x{7D17}\x{7D18}\x{7D19}\x{7D1A}\x{7D1B}\x{7D1C}\x{7D1D}\x{7D1E}\x{7D1F}' .
+'\x{7D20}\x{7D21}\x{7D22}\x{7D24}\x{7D25}\x{7D26}\x{7D27}\x{7D28}\x{7D29}' .
+'\x{7D2B}\x{7D2C}\x{7D2E}\x{7D2F}\x{7D30}\x{7D31}\x{7D32}\x{7D33}\x{7D34}' .
+'\x{7D35}\x{7D36}\x{7D37}\x{7D38}\x{7D39}\x{7D3A}\x{7D3B}\x{7D3C}\x{7D3D}' .
+'\x{7D3E}\x{7D3F}\x{7D40}\x{7D41}\x{7D42}\x{7D43}\x{7D44}\x{7D45}\x{7D46}' .
+'\x{7D47}\x{7D49}\x{7D4A}\x{7D4B}\x{7D4C}\x{7D4E}\x{7D4F}\x{7D50}\x{7D51}' .
+'\x{7D52}\x{7D53}\x{7D54}\x{7D55}\x{7D56}\x{7D57}\x{7D58}\x{7D59}\x{7D5B}' .
+'\x{7D5C}\x{7D5D}\x{7D5E}\x{7D5F}\x{7D60}\x{7D61}\x{7D62}\x{7D63}\x{7D65}' .
+'\x{7D66}\x{7D67}\x{7D68}\x{7D69}\x{7D6A}\x{7D6B}\x{7D6C}\x{7D6D}\x{7D6E}' .
+'\x{7D6F}\x{7D70}\x{7D71}\x{7D72}\x{7D73}\x{7D74}\x{7D75}\x{7D76}\x{7D77}' .
+'\x{7D79}\x{7D7A}\x{7D7B}\x{7D7C}\x{7D7D}\x{7D7E}\x{7D7F}\x{7D80}\x{7D81}' .
+'\x{7D83}\x{7D84}\x{7D85}\x{7D86}\x{7D87}\x{7D88}\x{7D89}\x{7D8A}\x{7D8B}' .
+'\x{7D8C}\x{7D8D}\x{7D8E}\x{7D8F}\x{7D90}\x{7D91}\x{7D92}\x{7D93}\x{7D94}' .
+'\x{7D96}\x{7D97}\x{7D99}\x{7D9B}\x{7D9C}\x{7D9D}\x{7D9E}\x{7D9F}\x{7DA0}' .
+'\x{7DA1}\x{7DA2}\x{7DA3}\x{7DA5}\x{7DA6}\x{7DA7}\x{7DA9}\x{7DAA}\x{7DAB}' .
+'\x{7DAC}\x{7DAD}\x{7DAE}\x{7DAF}\x{7DB0}\x{7DB1}\x{7DB2}\x{7DB3}\x{7DB4}' .
+'\x{7DB5}\x{7DB6}\x{7DB7}\x{7DB8}\x{7DB9}\x{7DBA}\x{7DBB}\x{7DBC}\x{7DBD}' .
+'\x{7DBE}\x{7DBF}\x{7DC0}\x{7DC1}\x{7DC2}\x{7DC3}\x{7DC4}\x{7DC5}\x{7DC6}' .
+'\x{7DC7}\x{7DC8}\x{7DC9}\x{7DCA}\x{7DCB}\x{7DCC}\x{7DCE}\x{7DCF}\x{7DD0}' .
+'\x{7DD1}\x{7DD2}\x{7DD4}\x{7DD5}\x{7DD6}\x{7DD7}\x{7DD8}\x{7DD9}\x{7DDA}' .
+'\x{7DDB}\x{7DDD}\x{7DDE}\x{7DDF}\x{7DE0}\x{7DE1}\x{7DE2}\x{7DE3}\x{7DE6}' .
+'\x{7DE7}\x{7DE8}\x{7DE9}\x{7DEA}\x{7DEC}\x{7DED}\x{7DEE}\x{7DEF}\x{7DF0}' .
+'\x{7DF1}\x{7DF2}\x{7DF3}\x{7DF4}\x{7DF5}\x{7DF6}\x{7DF7}\x{7DF8}\x{7DF9}' .
+'\x{7DFA}\x{7DFB}\x{7DFC}\x{7E00}\x{7E01}\x{7E02}\x{7E03}\x{7E04}\x{7E05}' .
+'\x{7E06}\x{7E07}\x{7E08}\x{7E09}\x{7E0A}\x{7E0B}\x{7E0C}\x{7E0D}\x{7E0E}' .
+'\x{7E0F}\x{7E10}\x{7E11}\x{7E12}\x{7E13}\x{7E14}\x{7E15}\x{7E16}\x{7E17}' .
+'\x{7E19}\x{7E1A}\x{7E1B}\x{7E1C}\x{7E1D}\x{7E1E}\x{7E1F}\x{7E20}\x{7E21}' .
+'\x{7E22}\x{7E23}\x{7E24}\x{7E25}\x{7E26}\x{7E27}\x{7E28}\x{7E29}\x{7E2A}' .
+'\x{7E2B}\x{7E2C}\x{7E2D}\x{7E2E}\x{7E2F}\x{7E30}\x{7E31}\x{7E32}\x{7E33}' .
+'\x{7E34}\x{7E35}\x{7E36}\x{7E37}\x{7E38}\x{7E39}\x{7E3A}\x{7E3B}\x{7E3C}' .
+'\x{7E3D}\x{7E3E}\x{7E3F}\x{7E40}\x{7E41}\x{7E42}\x{7E43}\x{7E44}\x{7E45}' .
+'\x{7E46}\x{7E47}\x{7E48}\x{7E49}\x{7E4C}\x{7E4D}\x{7E4E}\x{7E4F}\x{7E50}' .
+'\x{7E51}\x{7E52}\x{7E53}\x{7E54}\x{7E55}\x{7E56}\x{7E57}\x{7E58}\x{7E59}' .
+'\x{7E5A}\x{7E5C}\x{7E5D}\x{7E5E}\x{7E5F}\x{7E60}\x{7E61}\x{7E62}\x{7E63}' .
+'\x{7E65}\x{7E66}\x{7E67}\x{7E68}\x{7E69}\x{7E6A}\x{7E6B}\x{7E6C}\x{7E6D}' .
+'\x{7E6E}\x{7E6F}\x{7E70}\x{7E71}\x{7E72}\x{7E73}\x{7E74}\x{7E75}\x{7E76}' .
+'\x{7E77}\x{7E78}\x{7E79}\x{7E7A}\x{7E7B}\x{7E7C}\x{7E7D}\x{7E7E}\x{7E7F}' .
+'\x{7E80}\x{7E81}\x{7E82}\x{7E83}\x{7E84}\x{7E85}\x{7E86}\x{7E87}\x{7E88}' .
+'\x{7E89}\x{7E8A}\x{7E8B}\x{7E8C}\x{7E8D}\x{7E8E}\x{7E8F}\x{7E90}\x{7E91}' .
+'\x{7E92}\x{7E93}\x{7E94}\x{7E95}\x{7E96}\x{7E97}\x{7E98}\x{7E99}\x{7E9A}' .
+'\x{7E9B}\x{7E9C}\x{7E9E}\x{7E9F}\x{7EA0}\x{7EA1}\x{7EA2}\x{7EA3}\x{7EA4}' .
+'\x{7EA5}\x{7EA6}\x{7EA7}\x{7EA8}\x{7EA9}\x{7EAA}\x{7EAB}\x{7EAC}\x{7EAD}' .
+'\x{7EAE}\x{7EAF}\x{7EB0}\x{7EB1}\x{7EB2}\x{7EB3}\x{7EB4}\x{7EB5}\x{7EB6}' .
+'\x{7EB7}\x{7EB8}\x{7EB9}\x{7EBA}\x{7EBB}\x{7EBC}\x{7EBD}\x{7EBE}\x{7EBF}' .
+'\x{7EC0}\x{7EC1}\x{7EC2}\x{7EC3}\x{7EC4}\x{7EC5}\x{7EC6}\x{7EC7}\x{7EC8}' .
+'\x{7EC9}\x{7ECA}\x{7ECB}\x{7ECC}\x{7ECD}\x{7ECE}\x{7ECF}\x{7ED0}\x{7ED1}' .
+'\x{7ED2}\x{7ED3}\x{7ED4}\x{7ED5}\x{7ED6}\x{7ED7}\x{7ED8}\x{7ED9}\x{7EDA}' .
+'\x{7EDB}\x{7EDC}\x{7EDD}\x{7EDE}\x{7EDF}\x{7EE0}\x{7EE1}\x{7EE2}\x{7EE3}' .
+'\x{7EE4}\x{7EE5}\x{7EE6}\x{7EE7}\x{7EE8}\x{7EE9}\x{7EEA}\x{7EEB}\x{7EEC}' .
+'\x{7EED}\x{7EEE}\x{7EEF}\x{7EF0}\x{7EF1}\x{7EF2}\x{7EF3}\x{7EF4}\x{7EF5}' .
+'\x{7EF6}\x{7EF7}\x{7EF8}\x{7EF9}\x{7EFA}\x{7EFB}\x{7EFC}\x{7EFD}\x{7EFE}' .
+'\x{7EFF}\x{7F00}\x{7F01}\x{7F02}\x{7F03}\x{7F04}\x{7F05}\x{7F06}\x{7F07}' .
+'\x{7F08}\x{7F09}\x{7F0A}\x{7F0B}\x{7F0C}\x{7F0D}\x{7F0E}\x{7F0F}\x{7F10}' .
+'\x{7F11}\x{7F12}\x{7F13}\x{7F14}\x{7F15}\x{7F16}\x{7F17}\x{7F18}\x{7F19}' .
+'\x{7F1A}\x{7F1B}\x{7F1C}\x{7F1D}\x{7F1E}\x{7F1F}\x{7F20}\x{7F21}\x{7F22}' .
+'\x{7F23}\x{7F24}\x{7F25}\x{7F26}\x{7F27}\x{7F28}\x{7F29}\x{7F2A}\x{7F2B}' .
+'\x{7F2C}\x{7F2D}\x{7F2E}\x{7F2F}\x{7F30}\x{7F31}\x{7F32}\x{7F33}\x{7F34}' .
+'\x{7F35}\x{7F36}\x{7F37}\x{7F38}\x{7F39}\x{7F3A}\x{7F3D}\x{7F3E}\x{7F3F}' .
+'\x{7F40}\x{7F42}\x{7F43}\x{7F44}\x{7F45}\x{7F47}\x{7F48}\x{7F49}\x{7F4A}' .
+'\x{7F4B}\x{7F4C}\x{7F4D}\x{7F4E}\x{7F4F}\x{7F50}\x{7F51}\x{7F52}\x{7F53}' .
+'\x{7F54}\x{7F55}\x{7F56}\x{7F57}\x{7F58}\x{7F5A}\x{7F5B}\x{7F5C}\x{7F5D}' .
+'\x{7F5E}\x{7F5F}\x{7F60}\x{7F61}\x{7F62}\x{7F63}\x{7F64}\x{7F65}\x{7F66}' .
+'\x{7F67}\x{7F68}\x{7F69}\x{7F6A}\x{7F6B}\x{7F6C}\x{7F6D}\x{7F6E}\x{7F6F}' .
+'\x{7F70}\x{7F71}\x{7F72}\x{7F73}\x{7F74}\x{7F75}\x{7F76}\x{7F77}\x{7F78}' .
+'\x{7F79}\x{7F7A}\x{7F7B}\x{7F7C}\x{7F7D}\x{7F7E}\x{7F7F}\x{7F80}\x{7F81}' .
+'\x{7F82}\x{7F83}\x{7F85}\x{7F86}\x{7F87}\x{7F88}\x{7F89}\x{7F8A}\x{7F8B}' .
+'\x{7F8C}\x{7F8D}\x{7F8E}\x{7F8F}\x{7F91}\x{7F92}\x{7F93}\x{7F94}\x{7F95}' .
+'\x{7F96}\x{7F98}\x{7F9A}\x{7F9B}\x{7F9C}\x{7F9D}\x{7F9E}\x{7F9F}\x{7FA0}' .
+'\x{7FA1}\x{7FA2}\x{7FA3}\x{7FA4}\x{7FA5}\x{7FA6}\x{7FA7}\x{7FA8}\x{7FA9}' .
+'\x{7FAA}\x{7FAB}\x{7FAC}\x{7FAD}\x{7FAE}\x{7FAF}\x{7FB0}\x{7FB1}\x{7FB2}' .
+'\x{7FB3}\x{7FB5}\x{7FB6}\x{7FB7}\x{7FB8}\x{7FB9}\x{7FBA}\x{7FBB}\x{7FBC}' .
+'\x{7FBD}\x{7FBE}\x{7FBF}\x{7FC0}\x{7FC1}\x{7FC2}\x{7FC3}\x{7FC4}\x{7FC5}' .
+'\x{7FC6}\x{7FC7}\x{7FC8}\x{7FC9}\x{7FCA}\x{7FCB}\x{7FCC}\x{7FCD}\x{7FCE}' .
+'\x{7FCF}\x{7FD0}\x{7FD1}\x{7FD2}\x{7FD3}\x{7FD4}\x{7FD5}\x{7FD7}\x{7FD8}' .
+'\x{7FD9}\x{7FDA}\x{7FDB}\x{7FDC}\x{7FDE}\x{7FDF}\x{7FE0}\x{7FE1}\x{7FE2}' .
+'\x{7FE3}\x{7FE5}\x{7FE6}\x{7FE7}\x{7FE8}\x{7FE9}\x{7FEA}\x{7FEB}\x{7FEC}' .
+'\x{7FED}\x{7FEE}\x{7FEF}\x{7FF0}\x{7FF1}\x{7FF2}\x{7FF3}\x{7FF4}\x{7FF5}' .
+'\x{7FF6}\x{7FF7}\x{7FF8}\x{7FF9}\x{7FFA}\x{7FFB}\x{7FFC}\x{7FFD}\x{7FFE}' .
+'\x{7FFF}\x{8000}\x{8001}\x{8002}\x{8003}\x{8004}\x{8005}\x{8006}\x{8007}' .
+'\x{8008}\x{8009}\x{800B}\x{800C}\x{800D}\x{800E}\x{800F}\x{8010}\x{8011}' .
+'\x{8012}\x{8013}\x{8014}\x{8015}\x{8016}\x{8017}\x{8018}\x{8019}\x{801A}' .
+'\x{801B}\x{801C}\x{801D}\x{801E}\x{801F}\x{8020}\x{8021}\x{8022}\x{8023}' .
+'\x{8024}\x{8025}\x{8026}\x{8027}\x{8028}\x{8029}\x{802A}\x{802B}\x{802C}' .
+'\x{802D}\x{802E}\x{8030}\x{8031}\x{8032}\x{8033}\x{8034}\x{8035}\x{8036}' .
+'\x{8037}\x{8038}\x{8039}\x{803A}\x{803B}\x{803D}\x{803E}\x{803F}\x{8041}' .
+'\x{8042}\x{8043}\x{8044}\x{8045}\x{8046}\x{8047}\x{8048}\x{8049}\x{804A}' .
+'\x{804B}\x{804C}\x{804D}\x{804E}\x{804F}\x{8050}\x{8051}\x{8052}\x{8053}' .
+'\x{8054}\x{8055}\x{8056}\x{8057}\x{8058}\x{8059}\x{805A}\x{805B}\x{805C}' .
+'\x{805D}\x{805E}\x{805F}\x{8060}\x{8061}\x{8062}\x{8063}\x{8064}\x{8065}' .
+'\x{8067}\x{8068}\x{8069}\x{806A}\x{806B}\x{806C}\x{806D}\x{806E}\x{806F}' .
+'\x{8070}\x{8071}\x{8072}\x{8073}\x{8074}\x{8075}\x{8076}\x{8077}\x{8078}' .
+'\x{8079}\x{807A}\x{807B}\x{807C}\x{807D}\x{807E}\x{807F}\x{8080}\x{8081}' .
+'\x{8082}\x{8083}\x{8084}\x{8085}\x{8086}\x{8087}\x{8089}\x{808A}\x{808B}' .
+'\x{808C}\x{808D}\x{808F}\x{8090}\x{8091}\x{8092}\x{8093}\x{8095}\x{8096}' .
+'\x{8097}\x{8098}\x{8099}\x{809A}\x{809B}\x{809C}\x{809D}\x{809E}\x{809F}' .
+'\x{80A0}\x{80A1}\x{80A2}\x{80A3}\x{80A4}\x{80A5}\x{80A9}\x{80AA}\x{80AB}' .
+'\x{80AD}\x{80AE}\x{80AF}\x{80B0}\x{80B1}\x{80B2}\x{80B4}\x{80B5}\x{80B6}' .
+'\x{80B7}\x{80B8}\x{80BA}\x{80BB}\x{80BC}\x{80BD}\x{80BE}\x{80BF}\x{80C0}' .
+'\x{80C1}\x{80C2}\x{80C3}\x{80C4}\x{80C5}\x{80C6}\x{80C7}\x{80C8}\x{80C9}' .
+'\x{80CA}\x{80CB}\x{80CC}\x{80CD}\x{80CE}\x{80CF}\x{80D0}\x{80D1}\x{80D2}' .
+'\x{80D3}\x{80D4}\x{80D5}\x{80D6}\x{80D7}\x{80D8}\x{80D9}\x{80DA}\x{80DB}' .
+'\x{80DC}\x{80DD}\x{80DE}\x{80E0}\x{80E1}\x{80E2}\x{80E3}\x{80E4}\x{80E5}' .
+'\x{80E6}\x{80E7}\x{80E8}\x{80E9}\x{80EA}\x{80EB}\x{80EC}\x{80ED}\x{80EE}' .
+'\x{80EF}\x{80F0}\x{80F1}\x{80F2}\x{80F3}\x{80F4}\x{80F5}\x{80F6}\x{80F7}' .
+'\x{80F8}\x{80F9}\x{80FA}\x{80FB}\x{80FC}\x{80FD}\x{80FE}\x{80FF}\x{8100}' .
+'\x{8101}\x{8102}\x{8105}\x{8106}\x{8107}\x{8108}\x{8109}\x{810A}\x{810B}' .
+'\x{810C}\x{810D}\x{810E}\x{810F}\x{8110}\x{8111}\x{8112}\x{8113}\x{8114}' .
+'\x{8115}\x{8116}\x{8118}\x{8119}\x{811A}\x{811B}\x{811C}\x{811D}\x{811E}' .
+'\x{811F}\x{8120}\x{8121}\x{8122}\x{8123}\x{8124}\x{8125}\x{8126}\x{8127}' .
+'\x{8128}\x{8129}\x{812A}\x{812B}\x{812C}\x{812D}\x{812E}\x{812F}\x{8130}' .
+'\x{8131}\x{8132}\x{8136}\x{8137}\x{8138}\x{8139}\x{813A}\x{813B}\x{813C}' .
+'\x{813D}\x{813E}\x{813F}\x{8140}\x{8141}\x{8142}\x{8143}\x{8144}\x{8145}' .
+'\x{8146}\x{8147}\x{8148}\x{8149}\x{814A}\x{814B}\x{814C}\x{814D}\x{814E}' .
+'\x{814F}\x{8150}\x{8151}\x{8152}\x{8153}\x{8154}\x{8155}\x{8156}\x{8157}' .
+'\x{8158}\x{8159}\x{815A}\x{815B}\x{815C}\x{815D}\x{815E}\x{8160}\x{8161}' .
+'\x{8162}\x{8163}\x{8164}\x{8165}\x{8166}\x{8167}\x{8168}\x{8169}\x{816A}' .
+'\x{816B}\x{816C}\x{816D}\x{816E}\x{816F}\x{8170}\x{8171}\x{8172}\x{8173}' .
+'\x{8174}\x{8175}\x{8176}\x{8177}\x{8178}\x{8179}\x{817A}\x{817B}\x{817C}' .
+'\x{817D}\x{817E}\x{817F}\x{8180}\x{8181}\x{8182}\x{8183}\x{8185}\x{8186}' .
+'\x{8187}\x{8188}\x{8189}\x{818A}\x{818B}\x{818C}\x{818D}\x{818E}\x{818F}' .
+'\x{8191}\x{8192}\x{8193}\x{8194}\x{8195}\x{8197}\x{8198}\x{8199}\x{819A}' .
+'\x{819B}\x{819C}\x{819D}\x{819E}\x{819F}\x{81A0}\x{81A1}\x{81A2}\x{81A3}' .
+'\x{81A4}\x{81A5}\x{81A6}\x{81A7}\x{81A8}\x{81A9}\x{81AA}\x{81AB}\x{81AC}' .
+'\x{81AD}\x{81AE}\x{81AF}\x{81B0}\x{81B1}\x{81B2}\x{81B3}\x{81B4}\x{81B5}' .
+'\x{81B6}\x{81B7}\x{81B8}\x{81B9}\x{81BA}\x{81BB}\x{81BC}\x{81BD}\x{81BE}' .
+'\x{81BF}\x{81C0}\x{81C1}\x{81C2}\x{81C3}\x{81C4}\x{81C5}\x{81C6}\x{81C7}' .
+'\x{81C8}\x{81C9}\x{81CA}\x{81CC}\x{81CD}\x{81CE}\x{81CF}\x{81D0}\x{81D1}' .
+'\x{81D2}\x{81D4}\x{81D5}\x{81D6}\x{81D7}\x{81D8}\x{81D9}\x{81DA}\x{81DB}' .
+'\x{81DC}\x{81DD}\x{81DE}\x{81DF}\x{81E0}\x{81E1}\x{81E2}\x{81E3}\x{81E5}' .
+'\x{81E6}\x{81E7}\x{81E8}\x{81E9}\x{81EA}\x{81EB}\x{81EC}\x{81ED}\x{81EE}' .
+'\x{81F1}\x{81F2}\x{81F3}\x{81F4}\x{81F5}\x{81F6}\x{81F7}\x{81F8}\x{81F9}' .
+'\x{81FA}\x{81FB}\x{81FC}\x{81FD}\x{81FE}\x{81FF}\x{8200}\x{8201}\x{8202}' .
+'\x{8203}\x{8204}\x{8205}\x{8206}\x{8207}\x{8208}\x{8209}\x{820A}\x{820B}' .
+'\x{820C}\x{820D}\x{820E}\x{820F}\x{8210}\x{8211}\x{8212}\x{8214}\x{8215}' .
+'\x{8216}\x{8218}\x{8219}\x{821A}\x{821B}\x{821C}\x{821D}\x{821E}\x{821F}' .
+'\x{8220}\x{8221}\x{8222}\x{8223}\x{8225}\x{8226}\x{8227}\x{8228}\x{8229}' .
+'\x{822A}\x{822B}\x{822C}\x{822D}\x{822F}\x{8230}\x{8231}\x{8232}\x{8233}' .
+'\x{8234}\x{8235}\x{8236}\x{8237}\x{8238}\x{8239}\x{823A}\x{823B}\x{823C}' .
+'\x{823D}\x{823E}\x{823F}\x{8240}\x{8242}\x{8243}\x{8244}\x{8245}\x{8246}' .
+'\x{8247}\x{8248}\x{8249}\x{824A}\x{824B}\x{824C}\x{824D}\x{824E}\x{824F}' .
+'\x{8250}\x{8251}\x{8252}\x{8253}\x{8254}\x{8255}\x{8256}\x{8257}\x{8258}' .
+'\x{8259}\x{825A}\x{825B}\x{825C}\x{825D}\x{825E}\x{825F}\x{8260}\x{8261}' .
+'\x{8263}\x{8264}\x{8266}\x{8267}\x{8268}\x{8269}\x{826A}\x{826B}\x{826C}' .
+'\x{826D}\x{826E}\x{826F}\x{8270}\x{8271}\x{8272}\x{8273}\x{8274}\x{8275}' .
+'\x{8276}\x{8277}\x{8278}\x{8279}\x{827A}\x{827B}\x{827C}\x{827D}\x{827E}' .
+'\x{827F}\x{8280}\x{8281}\x{8282}\x{8283}\x{8284}\x{8285}\x{8286}\x{8287}' .
+'\x{8288}\x{8289}\x{828A}\x{828B}\x{828D}\x{828E}\x{828F}\x{8290}\x{8291}' .
+'\x{8292}\x{8293}\x{8294}\x{8295}\x{8296}\x{8297}\x{8298}\x{8299}\x{829A}' .
+'\x{829B}\x{829C}\x{829D}\x{829E}\x{829F}\x{82A0}\x{82A1}\x{82A2}\x{82A3}' .
+'\x{82A4}\x{82A5}\x{82A6}\x{82A7}\x{82A8}\x{82A9}\x{82AA}\x{82AB}\x{82AC}' .
+'\x{82AD}\x{82AE}\x{82AF}\x{82B0}\x{82B1}\x{82B3}\x{82B4}\x{82B5}\x{82B6}' .
+'\x{82B7}\x{82B8}\x{82B9}\x{82BA}\x{82BB}\x{82BC}\x{82BD}\x{82BE}\x{82BF}' .
+'\x{82C0}\x{82C1}\x{82C2}\x{82C3}\x{82C4}\x{82C5}\x{82C6}\x{82C7}\x{82C8}' .
+'\x{82C9}\x{82CA}\x{82CB}\x{82CC}\x{82CD}\x{82CE}\x{82CF}\x{82D0}\x{82D1}' .
+'\x{82D2}\x{82D3}\x{82D4}\x{82D5}\x{82D6}\x{82D7}\x{82D8}\x{82D9}\x{82DA}' .
+'\x{82DB}\x{82DC}\x{82DD}\x{82DE}\x{82DF}\x{82E0}\x{82E1}\x{82E3}\x{82E4}' .
+'\x{82E5}\x{82E6}\x{82E7}\x{82E8}\x{82E9}\x{82EA}\x{82EB}\x{82EC}\x{82ED}' .
+'\x{82EE}\x{82EF}\x{82F0}\x{82F1}\x{82F2}\x{82F3}\x{82F4}\x{82F5}\x{82F6}' .
+'\x{82F7}\x{82F8}\x{82F9}\x{82FA}\x{82FB}\x{82FD}\x{82FE}\x{82FF}\x{8300}' .
+'\x{8301}\x{8302}\x{8303}\x{8304}\x{8305}\x{8306}\x{8307}\x{8308}\x{8309}' .
+'\x{830B}\x{830C}\x{830D}\x{830E}\x{830F}\x{8311}\x{8312}\x{8313}\x{8314}' .
+'\x{8315}\x{8316}\x{8317}\x{8318}\x{8319}\x{831A}\x{831B}\x{831C}\x{831D}' .
+'\x{831E}\x{831F}\x{8320}\x{8321}\x{8322}\x{8323}\x{8324}\x{8325}\x{8326}' .
+'\x{8327}\x{8328}\x{8329}\x{832A}\x{832B}\x{832C}\x{832D}\x{832E}\x{832F}' .
+'\x{8331}\x{8332}\x{8333}\x{8334}\x{8335}\x{8336}\x{8337}\x{8338}\x{8339}' .
+'\x{833A}\x{833B}\x{833C}\x{833D}\x{833E}\x{833F}\x{8340}\x{8341}\x{8342}' .
+'\x{8343}\x{8344}\x{8345}\x{8346}\x{8347}\x{8348}\x{8349}\x{834A}\x{834B}' .
+'\x{834C}\x{834D}\x{834E}\x{834F}\x{8350}\x{8351}\x{8352}\x{8353}\x{8354}' .
+'\x{8356}\x{8357}\x{8358}\x{8359}\x{835A}\x{835B}\x{835C}\x{835D}\x{835E}' .
+'\x{835F}\x{8360}\x{8361}\x{8362}\x{8363}\x{8364}\x{8365}\x{8366}\x{8367}' .
+'\x{8368}\x{8369}\x{836A}\x{836B}\x{836C}\x{836D}\x{836E}\x{836F}\x{8370}' .
+'\x{8371}\x{8372}\x{8373}\x{8374}\x{8375}\x{8376}\x{8377}\x{8378}\x{8379}' .
+'\x{837A}\x{837B}\x{837C}\x{837D}\x{837E}\x{837F}\x{8380}\x{8381}\x{8382}' .
+'\x{8383}\x{8384}\x{8385}\x{8386}\x{8387}\x{8388}\x{8389}\x{838A}\x{838B}' .
+'\x{838C}\x{838D}\x{838E}\x{838F}\x{8390}\x{8391}\x{8392}\x{8393}\x{8394}' .
+'\x{8395}\x{8396}\x{8397}\x{8398}\x{8399}\x{839A}\x{839B}\x{839C}\x{839D}' .
+'\x{839E}\x{83A0}\x{83A1}\x{83A2}\x{83A3}\x{83A4}\x{83A5}\x{83A6}\x{83A7}' .
+'\x{83A8}\x{83A9}\x{83AA}\x{83AB}\x{83AC}\x{83AD}\x{83AE}\x{83AF}\x{83B0}' .
+'\x{83B1}\x{83B2}\x{83B3}\x{83B4}\x{83B6}\x{83B7}\x{83B8}\x{83B9}\x{83BA}' .
+'\x{83BB}\x{83BC}\x{83BD}\x{83BF}\x{83C0}\x{83C1}\x{83C2}\x{83C3}\x{83C4}' .
+'\x{83C5}\x{83C6}\x{83C7}\x{83C8}\x{83C9}\x{83CA}\x{83CB}\x{83CC}\x{83CD}' .
+'\x{83CE}\x{83CF}\x{83D0}\x{83D1}\x{83D2}\x{83D3}\x{83D4}\x{83D5}\x{83D6}' .
+'\x{83D7}\x{83D8}\x{83D9}\x{83DA}\x{83DB}\x{83DC}\x{83DD}\x{83DE}\x{83DF}' .
+'\x{83E0}\x{83E1}\x{83E2}\x{83E3}\x{83E4}\x{83E5}\x{83E7}\x{83E8}\x{83E9}' .
+'\x{83EA}\x{83EB}\x{83EC}\x{83EE}\x{83EF}\x{83F0}\x{83F1}\x{83F2}\x{83F3}' .
+'\x{83F4}\x{83F5}\x{83F6}\x{83F7}\x{83F8}\x{83F9}\x{83FA}\x{83FB}\x{83FC}' .
+'\x{83FD}\x{83FE}\x{83FF}\x{8400}\x{8401}\x{8402}\x{8403}\x{8404}\x{8405}' .
+'\x{8406}\x{8407}\x{8408}\x{8409}\x{840A}\x{840B}\x{840C}\x{840D}\x{840E}' .
+'\x{840F}\x{8410}\x{8411}\x{8412}\x{8413}\x{8415}\x{8418}\x{8419}\x{841A}' .
+'\x{841B}\x{841C}\x{841D}\x{841E}\x{8421}\x{8422}\x{8423}\x{8424}\x{8425}' .
+'\x{8426}\x{8427}\x{8428}\x{8429}\x{842A}\x{842B}\x{842C}\x{842D}\x{842E}' .
+'\x{842F}\x{8430}\x{8431}\x{8432}\x{8433}\x{8434}\x{8435}\x{8436}\x{8437}' .
+'\x{8438}\x{8439}\x{843A}\x{843B}\x{843C}\x{843D}\x{843E}\x{843F}\x{8440}' .
+'\x{8441}\x{8442}\x{8443}\x{8444}\x{8445}\x{8446}\x{8447}\x{8448}\x{8449}' .
+'\x{844A}\x{844B}\x{844C}\x{844D}\x{844E}\x{844F}\x{8450}\x{8451}\x{8452}' .
+'\x{8453}\x{8454}\x{8455}\x{8456}\x{8457}\x{8459}\x{845A}\x{845B}\x{845C}' .
+'\x{845D}\x{845E}\x{845F}\x{8460}\x{8461}\x{8462}\x{8463}\x{8464}\x{8465}' .
+'\x{8466}\x{8467}\x{8468}\x{8469}\x{846A}\x{846B}\x{846C}\x{846D}\x{846E}' .
+'\x{846F}\x{8470}\x{8471}\x{8472}\x{8473}\x{8474}\x{8475}\x{8476}\x{8477}' .
+'\x{8478}\x{8479}\x{847A}\x{847B}\x{847C}\x{847D}\x{847E}\x{847F}\x{8480}' .
+'\x{8481}\x{8482}\x{8484}\x{8485}\x{8486}\x{8487}\x{8488}\x{8489}\x{848A}' .
+'\x{848B}\x{848C}\x{848D}\x{848E}\x{848F}\x{8490}\x{8491}\x{8492}\x{8493}' .
+'\x{8494}\x{8496}\x{8497}\x{8498}\x{8499}\x{849A}\x{849B}\x{849C}\x{849D}' .
+'\x{849E}\x{849F}\x{84A0}\x{84A1}\x{84A2}\x{84A3}\x{84A4}\x{84A5}\x{84A6}' .
+'\x{84A7}\x{84A8}\x{84A9}\x{84AA}\x{84AB}\x{84AC}\x{84AE}\x{84AF}\x{84B0}' .
+'\x{84B1}\x{84B2}\x{84B3}\x{84B4}\x{84B5}\x{84B6}\x{84B8}\x{84B9}\x{84BA}' .
+'\x{84BB}\x{84BC}\x{84BD}\x{84BE}\x{84BF}\x{84C0}\x{84C1}\x{84C2}\x{84C4}' .
+'\x{84C5}\x{84C6}\x{84C7}\x{84C8}\x{84C9}\x{84CA}\x{84CB}\x{84CC}\x{84CD}' .
+'\x{84CE}\x{84CF}\x{84D0}\x{84D1}\x{84D2}\x{84D3}\x{84D4}\x{84D5}\x{84D6}' .
+'\x{84D7}\x{84D8}\x{84D9}\x{84DB}\x{84DC}\x{84DD}\x{84DE}\x{84DF}\x{84E0}' .
+'\x{84E1}\x{84E2}\x{84E3}\x{84E4}\x{84E5}\x{84E6}\x{84E7}\x{84E8}\x{84E9}' .
+'\x{84EA}\x{84EB}\x{84EC}\x{84EE}\x{84EF}\x{84F0}\x{84F1}\x{84F2}\x{84F3}' .
+'\x{84F4}\x{84F5}\x{84F6}\x{84F7}\x{84F8}\x{84F9}\x{84FA}\x{84FB}\x{84FC}' .
+'\x{84FD}\x{84FE}\x{84FF}\x{8500}\x{8501}\x{8502}\x{8503}\x{8504}\x{8506}' .
+'\x{8507}\x{8508}\x{8509}\x{850A}\x{850B}\x{850C}\x{850D}\x{850E}\x{850F}' .
+'\x{8511}\x{8512}\x{8513}\x{8514}\x{8515}\x{8516}\x{8517}\x{8518}\x{8519}' .
+'\x{851A}\x{851B}\x{851C}\x{851D}\x{851E}\x{851F}\x{8520}\x{8521}\x{8522}' .
+'\x{8523}\x{8524}\x{8525}\x{8526}\x{8527}\x{8528}\x{8529}\x{852A}\x{852B}' .
+'\x{852C}\x{852D}\x{852E}\x{852F}\x{8530}\x{8531}\x{8534}\x{8535}\x{8536}' .
+'\x{8537}\x{8538}\x{8539}\x{853A}\x{853B}\x{853C}\x{853D}\x{853E}\x{853F}' .
+'\x{8540}\x{8541}\x{8542}\x{8543}\x{8544}\x{8545}\x{8546}\x{8547}\x{8548}' .
+'\x{8549}\x{854A}\x{854B}\x{854D}\x{854E}\x{854F}\x{8551}\x{8552}\x{8553}' .
+'\x{8554}\x{8555}\x{8556}\x{8557}\x{8558}\x{8559}\x{855A}\x{855B}\x{855C}' .
+'\x{855D}\x{855E}\x{855F}\x{8560}\x{8561}\x{8562}\x{8563}\x{8564}\x{8565}' .
+'\x{8566}\x{8567}\x{8568}\x{8569}\x{856A}\x{856B}\x{856C}\x{856D}\x{856E}' .
+'\x{856F}\x{8570}\x{8571}\x{8572}\x{8573}\x{8574}\x{8575}\x{8576}\x{8577}' .
+'\x{8578}\x{8579}\x{857A}\x{857B}\x{857C}\x{857D}\x{857E}\x{8580}\x{8581}' .
+'\x{8582}\x{8583}\x{8584}\x{8585}\x{8586}\x{8587}\x{8588}\x{8589}\x{858A}' .
+'\x{858B}\x{858C}\x{858D}\x{858E}\x{858F}\x{8590}\x{8591}\x{8592}\x{8594}' .
+'\x{8595}\x{8596}\x{8598}\x{8599}\x{859A}\x{859B}\x{859C}\x{859D}\x{859E}' .
+'\x{859F}\x{85A0}\x{85A1}\x{85A2}\x{85A3}\x{85A4}\x{85A5}\x{85A6}\x{85A7}' .
+'\x{85A8}\x{85A9}\x{85AA}\x{85AB}\x{85AC}\x{85AD}\x{85AE}\x{85AF}\x{85B0}' .
+'\x{85B1}\x{85B3}\x{85B4}\x{85B5}\x{85B6}\x{85B7}\x{85B8}\x{85B9}\x{85BA}' .
+'\x{85BC}\x{85BD}\x{85BE}\x{85BF}\x{85C0}\x{85C1}\x{85C2}\x{85C3}\x{85C4}' .
+'\x{85C5}\x{85C6}\x{85C7}\x{85C8}\x{85C9}\x{85CA}\x{85CB}\x{85CD}\x{85CE}' .
+'\x{85CF}\x{85D0}\x{85D1}\x{85D2}\x{85D3}\x{85D4}\x{85D5}\x{85D6}\x{85D7}' .
+'\x{85D8}\x{85D9}\x{85DA}\x{85DB}\x{85DC}\x{85DD}\x{85DE}\x{85DF}\x{85E0}' .
+'\x{85E1}\x{85E2}\x{85E3}\x{85E4}\x{85E5}\x{85E6}\x{85E7}\x{85E8}\x{85E9}' .
+'\x{85EA}\x{85EB}\x{85EC}\x{85ED}\x{85EF}\x{85F0}\x{85F1}\x{85F2}\x{85F4}' .
+'\x{85F5}\x{85F6}\x{85F7}\x{85F8}\x{85F9}\x{85FA}\x{85FB}\x{85FD}\x{85FE}' .
+'\x{85FF}\x{8600}\x{8601}\x{8602}\x{8604}\x{8605}\x{8606}\x{8607}\x{8608}' .
+'\x{8609}\x{860A}\x{860B}\x{860C}\x{860F}\x{8611}\x{8612}\x{8613}\x{8614}' .
+'\x{8616}\x{8617}\x{8618}\x{8619}\x{861A}\x{861B}\x{861C}\x{861E}\x{861F}' .
+'\x{8620}\x{8621}\x{8622}\x{8623}\x{8624}\x{8625}\x{8626}\x{8627}\x{8628}' .
+'\x{8629}\x{862A}\x{862B}\x{862C}\x{862D}\x{862E}\x{862F}\x{8630}\x{8631}' .
+'\x{8632}\x{8633}\x{8634}\x{8635}\x{8636}\x{8638}\x{8639}\x{863A}\x{863B}' .
+'\x{863C}\x{863D}\x{863E}\x{863F}\x{8640}\x{8641}\x{8642}\x{8643}\x{8644}' .
+'\x{8645}\x{8646}\x{8647}\x{8648}\x{8649}\x{864A}\x{864B}\x{864C}\x{864D}' .
+'\x{864E}\x{864F}\x{8650}\x{8651}\x{8652}\x{8653}\x{8654}\x{8655}\x{8656}' .
+'\x{8658}\x{8659}\x{865A}\x{865B}\x{865C}\x{865D}\x{865E}\x{865F}\x{8660}' .
+'\x{8661}\x{8662}\x{8663}\x{8664}\x{8665}\x{8666}\x{8667}\x{8668}\x{8669}' .
+'\x{866A}\x{866B}\x{866C}\x{866D}\x{866E}\x{866F}\x{8670}\x{8671}\x{8672}' .
+'\x{8673}\x{8674}\x{8676}\x{8677}\x{8678}\x{8679}\x{867A}\x{867B}\x{867C}' .
+'\x{867D}\x{867E}\x{867F}\x{8680}\x{8681}\x{8682}\x{8683}\x{8684}\x{8685}' .
+'\x{8686}\x{8687}\x{8688}\x{868A}\x{868B}\x{868C}\x{868D}\x{868E}\x{868F}' .
+'\x{8690}\x{8691}\x{8693}\x{8694}\x{8695}\x{8696}\x{8697}\x{8698}\x{8699}' .
+'\x{869A}\x{869B}\x{869C}\x{869D}\x{869E}\x{869F}\x{86A1}\x{86A2}\x{86A3}' .
+'\x{86A4}\x{86A5}\x{86A7}\x{86A8}\x{86A9}\x{86AA}\x{86AB}\x{86AC}\x{86AD}' .
+'\x{86AE}\x{86AF}\x{86B0}\x{86B1}\x{86B2}\x{86B3}\x{86B4}\x{86B5}\x{86B6}' .
+'\x{86B7}\x{86B8}\x{86B9}\x{86BA}\x{86BB}\x{86BC}\x{86BD}\x{86BE}\x{86BF}' .
+'\x{86C0}\x{86C1}\x{86C2}\x{86C3}\x{86C4}\x{86C5}\x{86C6}\x{86C7}\x{86C8}' .
+'\x{86C9}\x{86CA}\x{86CB}\x{86CC}\x{86CE}\x{86CF}\x{86D0}\x{86D1}\x{86D2}' .
+'\x{86D3}\x{86D4}\x{86D6}\x{86D7}\x{86D8}\x{86D9}\x{86DA}\x{86DB}\x{86DC}' .
+'\x{86DD}\x{86DE}\x{86DF}\x{86E1}\x{86E2}\x{86E3}\x{86E4}\x{86E5}\x{86E6}' .
+'\x{86E8}\x{86E9}\x{86EA}\x{86EB}\x{86EC}\x{86ED}\x{86EE}\x{86EF}\x{86F0}' .
+'\x{86F1}\x{86F2}\x{86F3}\x{86F4}\x{86F5}\x{86F6}\x{86F7}\x{86F8}\x{86F9}' .
+'\x{86FA}\x{86FB}\x{86FC}\x{86FE}\x{86FF}\x{8700}\x{8701}\x{8702}\x{8703}' .
+'\x{8704}\x{8705}\x{8706}\x{8707}\x{8708}\x{8709}\x{870A}\x{870B}\x{870C}' .
+'\x{870D}\x{870E}\x{870F}\x{8710}\x{8711}\x{8712}\x{8713}\x{8714}\x{8715}' .
+'\x{8716}\x{8717}\x{8718}\x{8719}\x{871A}\x{871B}\x{871C}\x{871E}\x{871F}' .
+'\x{8720}\x{8721}\x{8722}\x{8723}\x{8724}\x{8725}\x{8726}\x{8727}\x{8728}' .
+'\x{8729}\x{872A}\x{872B}\x{872C}\x{872D}\x{872E}\x{8730}\x{8731}\x{8732}' .
+'\x{8733}\x{8734}\x{8735}\x{8736}\x{8737}\x{8738}\x{8739}\x{873A}\x{873B}' .
+'\x{873C}\x{873E}\x{873F}\x{8740}\x{8741}\x{8742}\x{8743}\x{8744}\x{8746}' .
+'\x{8747}\x{8748}\x{8749}\x{874A}\x{874C}\x{874D}\x{874E}\x{874F}\x{8750}' .
+'\x{8751}\x{8752}\x{8753}\x{8754}\x{8755}\x{8756}\x{8757}\x{8758}\x{8759}' .
+'\x{875A}\x{875B}\x{875C}\x{875D}\x{875E}\x{875F}\x{8760}\x{8761}\x{8762}' .
+'\x{8763}\x{8764}\x{8765}\x{8766}\x{8767}\x{8768}\x{8769}\x{876A}\x{876B}' .
+'\x{876C}\x{876D}\x{876E}\x{876F}\x{8770}\x{8772}\x{8773}\x{8774}\x{8775}' .
+'\x{8776}\x{8777}\x{8778}\x{8779}\x{877A}\x{877B}\x{877C}\x{877D}\x{877E}' .
+'\x{8780}\x{8781}\x{8782}\x{8783}\x{8784}\x{8785}\x{8786}\x{8787}\x{8788}' .
+'\x{8789}\x{878A}\x{878B}\x{878C}\x{878D}\x{878F}\x{8790}\x{8791}\x{8792}' .
+'\x{8793}\x{8794}\x{8795}\x{8796}\x{8797}\x{8798}\x{879A}\x{879B}\x{879C}' .
+'\x{879D}\x{879E}\x{879F}\x{87A0}\x{87A1}\x{87A2}\x{87A3}\x{87A4}\x{87A5}' .
+'\x{87A6}\x{87A7}\x{87A8}\x{87A9}\x{87AA}\x{87AB}\x{87AC}\x{87AD}\x{87AE}' .
+'\x{87AF}\x{87B0}\x{87B1}\x{87B2}\x{87B3}\x{87B4}\x{87B5}\x{87B6}\x{87B7}' .
+'\x{87B8}\x{87B9}\x{87BA}\x{87BB}\x{87BC}\x{87BD}\x{87BE}\x{87BF}\x{87C0}' .
+'\x{87C1}\x{87C2}\x{87C3}\x{87C4}\x{87C5}\x{87C6}\x{87C7}\x{87C8}\x{87C9}' .
+'\x{87CA}\x{87CB}\x{87CC}\x{87CD}\x{87CE}\x{87CF}\x{87D0}\x{87D1}\x{87D2}' .
+'\x{87D3}\x{87D4}\x{87D5}\x{87D6}\x{87D7}\x{87D8}\x{87D9}\x{87DB}\x{87DC}' .
+'\x{87DD}\x{87DE}\x{87DF}\x{87E0}\x{87E1}\x{87E2}\x{87E3}\x{87E4}\x{87E5}' .
+'\x{87E6}\x{87E7}\x{87E8}\x{87E9}\x{87EA}\x{87EB}\x{87EC}\x{87ED}\x{87EE}' .
+'\x{87EF}\x{87F1}\x{87F2}\x{87F3}\x{87F4}\x{87F5}\x{87F6}\x{87F7}\x{87F8}' .
+'\x{87F9}\x{87FA}\x{87FB}\x{87FC}\x{87FD}\x{87FE}\x{87FF}\x{8800}\x{8801}' .
+'\x{8802}\x{8803}\x{8804}\x{8805}\x{8806}\x{8808}\x{8809}\x{880A}\x{880B}' .
+'\x{880C}\x{880D}\x{880E}\x{880F}\x{8810}\x{8811}\x{8813}\x{8814}\x{8815}' .
+'\x{8816}\x{8817}\x{8818}\x{8819}\x{881A}\x{881B}\x{881C}\x{881D}\x{881E}' .
+'\x{881F}\x{8820}\x{8821}\x{8822}\x{8823}\x{8824}\x{8825}\x{8826}\x{8827}' .
+'\x{8828}\x{8829}\x{882A}\x{882B}\x{882C}\x{882E}\x{882F}\x{8830}\x{8831}' .
+'\x{8832}\x{8833}\x{8834}\x{8835}\x{8836}\x{8837}\x{8838}\x{8839}\x{883B}' .
+'\x{883C}\x{883D}\x{883E}\x{883F}\x{8840}\x{8841}\x{8842}\x{8843}\x{8844}' .
+'\x{8845}\x{8846}\x{8848}\x{8849}\x{884A}\x{884B}\x{884C}\x{884D}\x{884E}' .
+'\x{884F}\x{8850}\x{8851}\x{8852}\x{8853}\x{8854}\x{8855}\x{8856}\x{8857}' .
+'\x{8859}\x{885A}\x{885B}\x{885D}\x{885E}\x{8860}\x{8861}\x{8862}\x{8863}' .
+'\x{8864}\x{8865}\x{8866}\x{8867}\x{8868}\x{8869}\x{886A}\x{886B}\x{886C}' .
+'\x{886D}\x{886E}\x{886F}\x{8870}\x{8871}\x{8872}\x{8873}\x{8874}\x{8875}' .
+'\x{8876}\x{8877}\x{8878}\x{8879}\x{887B}\x{887C}\x{887D}\x{887E}\x{887F}' .
+'\x{8880}\x{8881}\x{8882}\x{8883}\x{8884}\x{8885}\x{8886}\x{8887}\x{8888}' .
+'\x{8889}\x{888A}\x{888B}\x{888C}\x{888D}\x{888E}\x{888F}\x{8890}\x{8891}' .
+'\x{8892}\x{8893}\x{8894}\x{8895}\x{8896}\x{8897}\x{8898}\x{8899}\x{889A}' .
+'\x{889B}\x{889C}\x{889D}\x{889E}\x{889F}\x{88A0}\x{88A1}\x{88A2}\x{88A3}' .
+'\x{88A4}\x{88A5}\x{88A6}\x{88A7}\x{88A8}\x{88A9}\x{88AA}\x{88AB}\x{88AC}' .
+'\x{88AD}\x{88AE}\x{88AF}\x{88B0}\x{88B1}\x{88B2}\x{88B3}\x{88B4}\x{88B6}' .
+'\x{88B7}\x{88B8}\x{88B9}\x{88BA}\x{88BB}\x{88BC}\x{88BD}\x{88BE}\x{88BF}' .
+'\x{88C0}\x{88C1}\x{88C2}\x{88C3}\x{88C4}\x{88C5}\x{88C6}\x{88C7}\x{88C8}' .
+'\x{88C9}\x{88CA}\x{88CB}\x{88CC}\x{88CD}\x{88CE}\x{88CF}\x{88D0}\x{88D1}' .
+'\x{88D2}\x{88D3}\x{88D4}\x{88D5}\x{88D6}\x{88D7}\x{88D8}\x{88D9}\x{88DA}' .
+'\x{88DB}\x{88DC}\x{88DD}\x{88DE}\x{88DF}\x{88E0}\x{88E1}\x{88E2}\x{88E3}' .
+'\x{88E4}\x{88E5}\x{88E7}\x{88E8}\x{88EA}\x{88EB}\x{88EC}\x{88EE}\x{88EF}' .
+'\x{88F0}\x{88F1}\x{88F2}\x{88F3}\x{88F4}\x{88F5}\x{88F6}\x{88F7}\x{88F8}' .
+'\x{88F9}\x{88FA}\x{88FB}\x{88FC}\x{88FD}\x{88FE}\x{88FF}\x{8900}\x{8901}' .
+'\x{8902}\x{8904}\x{8905}\x{8906}\x{8907}\x{8908}\x{8909}\x{890A}\x{890B}' .
+'\x{890C}\x{890D}\x{890E}\x{8910}\x{8911}\x{8912}\x{8913}\x{8914}\x{8915}' .
+'\x{8916}\x{8917}\x{8918}\x{8919}\x{891A}\x{891B}\x{891C}\x{891D}\x{891E}' .
+'\x{891F}\x{8920}\x{8921}\x{8922}\x{8923}\x{8925}\x{8926}\x{8927}\x{8928}' .
+'\x{8929}\x{892A}\x{892B}\x{892C}\x{892D}\x{892E}\x{892F}\x{8930}\x{8931}' .
+'\x{8932}\x{8933}\x{8934}\x{8935}\x{8936}\x{8937}\x{8938}\x{8939}\x{893A}' .
+'\x{893B}\x{893C}\x{893D}\x{893E}\x{893F}\x{8940}\x{8941}\x{8942}\x{8943}' .
+'\x{8944}\x{8945}\x{8946}\x{8947}\x{8948}\x{8949}\x{894A}\x{894B}\x{894C}' .
+'\x{894E}\x{894F}\x{8950}\x{8951}\x{8952}\x{8953}\x{8954}\x{8955}\x{8956}' .
+'\x{8957}\x{8958}\x{8959}\x{895A}\x{895B}\x{895C}\x{895D}\x{895E}\x{895F}' .
+'\x{8960}\x{8961}\x{8962}\x{8963}\x{8964}\x{8966}\x{8967}\x{8968}\x{8969}' .
+'\x{896A}\x{896B}\x{896C}\x{896D}\x{896E}\x{896F}\x{8970}\x{8971}\x{8972}' .
+'\x{8973}\x{8974}\x{8976}\x{8977}\x{8978}\x{8979}\x{897A}\x{897B}\x{897C}' .
+'\x{897E}\x{897F}\x{8980}\x{8981}\x{8982}\x{8983}\x{8984}\x{8985}\x{8986}' .
+'\x{8987}\x{8988}\x{8989}\x{898A}\x{898B}\x{898C}\x{898E}\x{898F}\x{8991}' .
+'\x{8992}\x{8993}\x{8995}\x{8996}\x{8997}\x{8998}\x{899A}\x{899B}\x{899C}' .
+'\x{899D}\x{899E}\x{899F}\x{89A0}\x{89A1}\x{89A2}\x{89A3}\x{89A4}\x{89A5}' .
+'\x{89A6}\x{89A7}\x{89A8}\x{89AA}\x{89AB}\x{89AC}\x{89AD}\x{89AE}\x{89AF}' .
+'\x{89B1}\x{89B2}\x{89B3}\x{89B5}\x{89B6}\x{89B7}\x{89B8}\x{89B9}\x{89BA}' .
+'\x{89BD}\x{89BE}\x{89BF}\x{89C0}\x{89C1}\x{89C2}\x{89C3}\x{89C4}\x{89C5}' .
+'\x{89C6}\x{89C7}\x{89C8}\x{89C9}\x{89CA}\x{89CB}\x{89CC}\x{89CD}\x{89CE}' .
+'\x{89CF}\x{89D0}\x{89D1}\x{89D2}\x{89D3}\x{89D4}\x{89D5}\x{89D6}\x{89D7}' .
+'\x{89D8}\x{89D9}\x{89DA}\x{89DB}\x{89DC}\x{89DD}\x{89DE}\x{89DF}\x{89E0}' .
+'\x{89E1}\x{89E2}\x{89E3}\x{89E4}\x{89E5}\x{89E6}\x{89E7}\x{89E8}\x{89E9}' .
+'\x{89EA}\x{89EB}\x{89EC}\x{89ED}\x{89EF}\x{89F0}\x{89F1}\x{89F2}\x{89F3}' .
+'\x{89F4}\x{89F6}\x{89F7}\x{89F8}\x{89FA}\x{89FB}\x{89FC}\x{89FE}\x{89FF}' .
+'\x{8A00}\x{8A01}\x{8A02}\x{8A03}\x{8A04}\x{8A07}\x{8A08}\x{8A09}\x{8A0A}' .
+'\x{8A0B}\x{8A0C}\x{8A0D}\x{8A0E}\x{8A0F}\x{8A10}\x{8A11}\x{8A12}\x{8A13}' .
+'\x{8A15}\x{8A16}\x{8A17}\x{8A18}\x{8A1A}\x{8A1B}\x{8A1C}\x{8A1D}\x{8A1E}' .
+'\x{8A1F}\x{8A22}\x{8A23}\x{8A24}\x{8A25}\x{8A26}\x{8A27}\x{8A28}\x{8A29}' .
+'\x{8A2A}\x{8A2C}\x{8A2D}\x{8A2E}\x{8A2F}\x{8A30}\x{8A31}\x{8A32}\x{8A34}' .
+'\x{8A35}\x{8A36}\x{8A37}\x{8A38}\x{8A39}\x{8A3A}\x{8A3B}\x{8A3C}\x{8A3E}' .
+'\x{8A3F}\x{8A40}\x{8A41}\x{8A42}\x{8A43}\x{8A44}\x{8A45}\x{8A46}\x{8A47}' .
+'\x{8A48}\x{8A49}\x{8A4A}\x{8A4C}\x{8A4D}\x{8A4E}\x{8A4F}\x{8A50}\x{8A51}' .
+'\x{8A52}\x{8A53}\x{8A54}\x{8A55}\x{8A56}\x{8A57}\x{8A58}\x{8A59}\x{8A5A}' .
+'\x{8A5B}\x{8A5C}\x{8A5D}\x{8A5E}\x{8A5F}\x{8A60}\x{8A61}\x{8A62}\x{8A63}' .
+'\x{8A65}\x{8A66}\x{8A67}\x{8A68}\x{8A69}\x{8A6A}\x{8A6B}\x{8A6C}\x{8A6D}' .
+'\x{8A6E}\x{8A6F}\x{8A70}\x{8A71}\x{8A72}\x{8A73}\x{8A74}\x{8A75}\x{8A76}' .
+'\x{8A77}\x{8A79}\x{8A7A}\x{8A7B}\x{8A7C}\x{8A7E}\x{8A7F}\x{8A80}\x{8A81}' .
+'\x{8A82}\x{8A83}\x{8A84}\x{8A85}\x{8A86}\x{8A87}\x{8A89}\x{8A8A}\x{8A8B}' .
+'\x{8A8C}\x{8A8D}\x{8A8E}\x{8A8F}\x{8A90}\x{8A91}\x{8A92}\x{8A93}\x{8A94}' .
+'\x{8A95}\x{8A96}\x{8A97}\x{8A98}\x{8A99}\x{8A9A}\x{8A9B}\x{8A9C}\x{8A9D}' .
+'\x{8A9E}\x{8AA0}\x{8AA1}\x{8AA2}\x{8AA3}\x{8AA4}\x{8AA5}\x{8AA6}\x{8AA7}' .
+'\x{8AA8}\x{8AA9}\x{8AAA}\x{8AAB}\x{8AAC}\x{8AAE}\x{8AB0}\x{8AB1}\x{8AB2}' .
+'\x{8AB3}\x{8AB4}\x{8AB5}\x{8AB6}\x{8AB8}\x{8AB9}\x{8ABA}\x{8ABB}\x{8ABC}' .
+'\x{8ABD}\x{8ABE}\x{8ABF}\x{8AC0}\x{8AC1}\x{8AC2}\x{8AC3}\x{8AC4}\x{8AC5}' .
+'\x{8AC6}\x{8AC7}\x{8AC8}\x{8AC9}\x{8ACA}\x{8ACB}\x{8ACC}\x{8ACD}\x{8ACE}' .
+'\x{8ACF}\x{8AD1}\x{8AD2}\x{8AD3}\x{8AD4}\x{8AD5}\x{8AD6}\x{8AD7}\x{8AD8}' .
+'\x{8AD9}\x{8ADA}\x{8ADB}\x{8ADC}\x{8ADD}\x{8ADE}\x{8ADF}\x{8AE0}\x{8AE1}' .
+'\x{8AE2}\x{8AE3}\x{8AE4}\x{8AE5}\x{8AE6}\x{8AE7}\x{8AE8}\x{8AE9}\x{8AEA}' .
+'\x{8AEB}\x{8AED}\x{8AEE}\x{8AEF}\x{8AF0}\x{8AF1}\x{8AF2}\x{8AF3}\x{8AF4}' .
+'\x{8AF5}\x{8AF6}\x{8AF7}\x{8AF8}\x{8AF9}\x{8AFA}\x{8AFB}\x{8AFC}\x{8AFD}' .
+'\x{8AFE}\x{8AFF}\x{8B00}\x{8B01}\x{8B02}\x{8B03}\x{8B04}\x{8B05}\x{8B06}' .
+'\x{8B07}\x{8B08}\x{8B09}\x{8B0A}\x{8B0B}\x{8B0D}\x{8B0E}\x{8B0F}\x{8B10}' .
+'\x{8B11}\x{8B12}\x{8B13}\x{8B14}\x{8B15}\x{8B16}\x{8B17}\x{8B18}\x{8B19}' .
+'\x{8B1A}\x{8B1B}\x{8B1C}\x{8B1D}\x{8B1E}\x{8B1F}\x{8B20}\x{8B21}\x{8B22}' .
+'\x{8B23}\x{8B24}\x{8B25}\x{8B26}\x{8B27}\x{8B28}\x{8B2A}\x{8B2B}\x{8B2C}' .
+'\x{8B2D}\x{8B2E}\x{8B2F}\x{8B30}\x{8B31}\x{8B33}\x{8B34}\x{8B35}\x{8B36}' .
+'\x{8B37}\x{8B39}\x{8B3A}\x{8B3B}\x{8B3C}\x{8B3D}\x{8B3E}\x{8B40}\x{8B41}' .
+'\x{8B42}\x{8B43}\x{8B44}\x{8B45}\x{8B46}\x{8B47}\x{8B48}\x{8B49}\x{8B4A}' .
+'\x{8B4B}\x{8B4C}\x{8B4D}\x{8B4E}\x{8B4F}\x{8B50}\x{8B51}\x{8B52}\x{8B53}' .
+'\x{8B54}\x{8B55}\x{8B56}\x{8B57}\x{8B58}\x{8B59}\x{8B5A}\x{8B5B}\x{8B5C}' .
+'\x{8B5D}\x{8B5E}\x{8B5F}\x{8B60}\x{8B63}\x{8B64}\x{8B65}\x{8B66}\x{8B67}' .
+'\x{8B68}\x{8B6A}\x{8B6B}\x{8B6C}\x{8B6D}\x{8B6E}\x{8B6F}\x{8B70}\x{8B71}' .
+'\x{8B73}\x{8B74}\x{8B76}\x{8B77}\x{8B78}\x{8B79}\x{8B7A}\x{8B7B}\x{8B7D}' .
+'\x{8B7E}\x{8B7F}\x{8B80}\x{8B82}\x{8B83}\x{8B84}\x{8B85}\x{8B86}\x{8B88}' .
+'\x{8B89}\x{8B8A}\x{8B8B}\x{8B8C}\x{8B8E}\x{8B90}\x{8B91}\x{8B92}\x{8B93}' .
+'\x{8B94}\x{8B95}\x{8B96}\x{8B97}\x{8B98}\x{8B99}\x{8B9A}\x{8B9C}\x{8B9D}' .
+'\x{8B9E}\x{8B9F}\x{8BA0}\x{8BA1}\x{8BA2}\x{8BA3}\x{8BA4}\x{8BA5}\x{8BA6}' .
+'\x{8BA7}\x{8BA8}\x{8BA9}\x{8BAA}\x{8BAB}\x{8BAC}\x{8BAD}\x{8BAE}\x{8BAF}' .
+'\x{8BB0}\x{8BB1}\x{8BB2}\x{8BB3}\x{8BB4}\x{8BB5}\x{8BB6}\x{8BB7}\x{8BB8}' .
+'\x{8BB9}\x{8BBA}\x{8BBB}\x{8BBC}\x{8BBD}\x{8BBE}\x{8BBF}\x{8BC0}\x{8BC1}' .
+'\x{8BC2}\x{8BC3}\x{8BC4}\x{8BC5}\x{8BC6}\x{8BC7}\x{8BC8}\x{8BC9}\x{8BCA}' .
+'\x{8BCB}\x{8BCC}\x{8BCD}\x{8BCE}\x{8BCF}\x{8BD0}\x{8BD1}\x{8BD2}\x{8BD3}' .
+'\x{8BD4}\x{8BD5}\x{8BD6}\x{8BD7}\x{8BD8}\x{8BD9}\x{8BDA}\x{8BDB}\x{8BDC}' .
+'\x{8BDD}\x{8BDE}\x{8BDF}\x{8BE0}\x{8BE1}\x{8BE2}\x{8BE3}\x{8BE4}\x{8BE5}' .
+'\x{8BE6}\x{8BE7}\x{8BE8}\x{8BE9}\x{8BEA}\x{8BEB}\x{8BEC}\x{8BED}\x{8BEE}' .
+'\x{8BEF}\x{8BF0}\x{8BF1}\x{8BF2}\x{8BF3}\x{8BF4}\x{8BF5}\x{8BF6}\x{8BF7}' .
+'\x{8BF8}\x{8BF9}\x{8BFA}\x{8BFB}\x{8BFC}\x{8BFD}\x{8BFE}\x{8BFF}\x{8C00}' .
+'\x{8C01}\x{8C02}\x{8C03}\x{8C04}\x{8C05}\x{8C06}\x{8C07}\x{8C08}\x{8C09}' .
+'\x{8C0A}\x{8C0B}\x{8C0C}\x{8C0D}\x{8C0E}\x{8C0F}\x{8C10}\x{8C11}\x{8C12}' .
+'\x{8C13}\x{8C14}\x{8C15}\x{8C16}\x{8C17}\x{8C18}\x{8C19}\x{8C1A}\x{8C1B}' .
+'\x{8C1C}\x{8C1D}\x{8C1E}\x{8C1F}\x{8C20}\x{8C21}\x{8C22}\x{8C23}\x{8C24}' .
+'\x{8C25}\x{8C26}\x{8C27}\x{8C28}\x{8C29}\x{8C2A}\x{8C2B}\x{8C2C}\x{8C2D}' .
+'\x{8C2E}\x{8C2F}\x{8C30}\x{8C31}\x{8C32}\x{8C33}\x{8C34}\x{8C35}\x{8C36}' .
+'\x{8C37}\x{8C39}\x{8C3A}\x{8C3B}\x{8C3C}\x{8C3D}\x{8C3E}\x{8C3F}\x{8C41}' .
+'\x{8C42}\x{8C43}\x{8C45}\x{8C46}\x{8C47}\x{8C48}\x{8C49}\x{8C4A}\x{8C4B}' .
+'\x{8C4C}\x{8C4D}\x{8C4E}\x{8C4F}\x{8C50}\x{8C54}\x{8C55}\x{8C56}\x{8C57}' .
+'\x{8C59}\x{8C5A}\x{8C5B}\x{8C5C}\x{8C5D}\x{8C5E}\x{8C5F}\x{8C60}\x{8C61}' .
+'\x{8C62}\x{8C63}\x{8C64}\x{8C65}\x{8C66}\x{8C67}\x{8C68}\x{8C69}\x{8C6A}' .
+'\x{8C6B}\x{8C6C}\x{8C6D}\x{8C6E}\x{8C6F}\x{8C70}\x{8C71}\x{8C72}\x{8C73}' .
+'\x{8C75}\x{8C76}\x{8C77}\x{8C78}\x{8C79}\x{8C7A}\x{8C7B}\x{8C7D}\x{8C7E}' .
+'\x{8C80}\x{8C81}\x{8C82}\x{8C84}\x{8C85}\x{8C86}\x{8C88}\x{8C89}\x{8C8A}' .
+'\x{8C8C}\x{8C8D}\x{8C8F}\x{8C90}\x{8C91}\x{8C92}\x{8C93}\x{8C94}\x{8C95}' .
+'\x{8C96}\x{8C97}\x{8C98}\x{8C99}\x{8C9A}\x{8C9C}\x{8C9D}\x{8C9E}\x{8C9F}' .
+'\x{8CA0}\x{8CA1}\x{8CA2}\x{8CA3}\x{8CA4}\x{8CA5}\x{8CA7}\x{8CA8}\x{8CA9}' .
+'\x{8CAA}\x{8CAB}\x{8CAC}\x{8CAD}\x{8CAE}\x{8CAF}\x{8CB0}\x{8CB1}\x{8CB2}' .
+'\x{8CB3}\x{8CB4}\x{8CB5}\x{8CB6}\x{8CB7}\x{8CB8}\x{8CB9}\x{8CBA}\x{8CBB}' .
+'\x{8CBC}\x{8CBD}\x{8CBE}\x{8CBF}\x{8CC0}\x{8CC1}\x{8CC2}\x{8CC3}\x{8CC4}' .
+'\x{8CC5}\x{8CC6}\x{8CC7}\x{8CC8}\x{8CC9}\x{8CCA}\x{8CCC}\x{8CCE}\x{8CCF}' .
+'\x{8CD0}\x{8CD1}\x{8CD2}\x{8CD3}\x{8CD4}\x{8CD5}\x{8CD7}\x{8CD9}\x{8CDA}' .
+'\x{8CDB}\x{8CDC}\x{8CDD}\x{8CDE}\x{8CDF}\x{8CE0}\x{8CE1}\x{8CE2}\x{8CE3}' .
+'\x{8CE4}\x{8CE5}\x{8CE6}\x{8CE7}\x{8CE8}\x{8CEA}\x{8CEB}\x{8CEC}\x{8CED}' .
+'\x{8CEE}\x{8CEF}\x{8CF0}\x{8CF1}\x{8CF2}\x{8CF3}\x{8CF4}\x{8CF5}\x{8CF6}' .
+'\x{8CF8}\x{8CF9}\x{8CFA}\x{8CFB}\x{8CFC}\x{8CFD}\x{8CFE}\x{8CFF}\x{8D00}' .
+'\x{8D02}\x{8D03}\x{8D04}\x{8D05}\x{8D06}\x{8D07}\x{8D08}\x{8D09}\x{8D0A}' .
+'\x{8D0B}\x{8D0C}\x{8D0D}\x{8D0E}\x{8D0F}\x{8D10}\x{8D13}\x{8D14}\x{8D15}' .
+'\x{8D16}\x{8D17}\x{8D18}\x{8D19}\x{8D1A}\x{8D1B}\x{8D1C}\x{8D1D}\x{8D1E}' .
+'\x{8D1F}\x{8D20}\x{8D21}\x{8D22}\x{8D23}\x{8D24}\x{8D25}\x{8D26}\x{8D27}' .
+'\x{8D28}\x{8D29}\x{8D2A}\x{8D2B}\x{8D2C}\x{8D2D}\x{8D2E}\x{8D2F}\x{8D30}' .
+'\x{8D31}\x{8D32}\x{8D33}\x{8D34}\x{8D35}\x{8D36}\x{8D37}\x{8D38}\x{8D39}' .
+'\x{8D3A}\x{8D3B}\x{8D3C}\x{8D3D}\x{8D3E}\x{8D3F}\x{8D40}\x{8D41}\x{8D42}' .
+'\x{8D43}\x{8D44}\x{8D45}\x{8D46}\x{8D47}\x{8D48}\x{8D49}\x{8D4A}\x{8D4B}' .
+'\x{8D4C}\x{8D4D}\x{8D4E}\x{8D4F}\x{8D50}\x{8D51}\x{8D52}\x{8D53}\x{8D54}' .
+'\x{8D55}\x{8D56}\x{8D57}\x{8D58}\x{8D59}\x{8D5A}\x{8D5B}\x{8D5C}\x{8D5D}' .
+'\x{8D5E}\x{8D5F}\x{8D60}\x{8D61}\x{8D62}\x{8D63}\x{8D64}\x{8D65}\x{8D66}' .
+'\x{8D67}\x{8D68}\x{8D69}\x{8D6A}\x{8D6B}\x{8D6C}\x{8D6D}\x{8D6E}\x{8D6F}' .
+'\x{8D70}\x{8D71}\x{8D72}\x{8D73}\x{8D74}\x{8D75}\x{8D76}\x{8D77}\x{8D78}' .
+'\x{8D79}\x{8D7A}\x{8D7B}\x{8D7D}\x{8D7E}\x{8D7F}\x{8D80}\x{8D81}\x{8D82}' .
+'\x{8D83}\x{8D84}\x{8D85}\x{8D86}\x{8D87}\x{8D88}\x{8D89}\x{8D8A}\x{8D8B}' .
+'\x{8D8C}\x{8D8D}\x{8D8E}\x{8D8F}\x{8D90}\x{8D91}\x{8D92}\x{8D93}\x{8D94}' .
+'\x{8D95}\x{8D96}\x{8D97}\x{8D98}\x{8D99}\x{8D9A}\x{8D9B}\x{8D9C}\x{8D9D}' .
+'\x{8D9E}\x{8D9F}\x{8DA0}\x{8DA1}\x{8DA2}\x{8DA3}\x{8DA4}\x{8DA5}\x{8DA7}' .
+'\x{8DA8}\x{8DA9}\x{8DAA}\x{8DAB}\x{8DAC}\x{8DAD}\x{8DAE}\x{8DAF}\x{8DB0}' .
+'\x{8DB1}\x{8DB2}\x{8DB3}\x{8DB4}\x{8DB5}\x{8DB6}\x{8DB7}\x{8DB8}\x{8DB9}' .
+'\x{8DBA}\x{8DBB}\x{8DBC}\x{8DBD}\x{8DBE}\x{8DBF}\x{8DC1}\x{8DC2}\x{8DC3}' .
+'\x{8DC4}\x{8DC5}\x{8DC6}\x{8DC7}\x{8DC8}\x{8DC9}\x{8DCA}\x{8DCB}\x{8DCC}' .
+'\x{8DCD}\x{8DCE}\x{8DCF}\x{8DD0}\x{8DD1}\x{8DD2}\x{8DD3}\x{8DD4}\x{8DD5}' .
+'\x{8DD6}\x{8DD7}\x{8DD8}\x{8DD9}\x{8DDA}\x{8DDB}\x{8DDC}\x{8DDD}\x{8DDE}' .
+'\x{8DDF}\x{8DE0}\x{8DE1}\x{8DE2}\x{8DE3}\x{8DE4}\x{8DE6}\x{8DE7}\x{8DE8}' .
+'\x{8DE9}\x{8DEA}\x{8DEB}\x{8DEC}\x{8DED}\x{8DEE}\x{8DEF}\x{8DF0}\x{8DF1}' .
+'\x{8DF2}\x{8DF3}\x{8DF4}\x{8DF5}\x{8DF6}\x{8DF7}\x{8DF8}\x{8DF9}\x{8DFA}' .
+'\x{8DFB}\x{8DFC}\x{8DFD}\x{8DFE}\x{8DFF}\x{8E00}\x{8E02}\x{8E03}\x{8E04}' .
+'\x{8E05}\x{8E06}\x{8E07}\x{8E08}\x{8E09}\x{8E0A}\x{8E0C}\x{8E0D}\x{8E0E}' .
+'\x{8E0F}\x{8E10}\x{8E11}\x{8E12}\x{8E13}\x{8E14}\x{8E15}\x{8E16}\x{8E17}' .
+'\x{8E18}\x{8E19}\x{8E1A}\x{8E1B}\x{8E1C}\x{8E1D}\x{8E1E}\x{8E1F}\x{8E20}' .
+'\x{8E21}\x{8E22}\x{8E23}\x{8E24}\x{8E25}\x{8E26}\x{8E27}\x{8E28}\x{8E29}' .
+'\x{8E2A}\x{8E2B}\x{8E2C}\x{8E2D}\x{8E2E}\x{8E2F}\x{8E30}\x{8E31}\x{8E33}' .
+'\x{8E34}\x{8E35}\x{8E36}\x{8E37}\x{8E38}\x{8E39}\x{8E3A}\x{8E3B}\x{8E3C}' .
+'\x{8E3D}\x{8E3E}\x{8E3F}\x{8E40}\x{8E41}\x{8E42}\x{8E43}\x{8E44}\x{8E45}' .
+'\x{8E47}\x{8E48}\x{8E49}\x{8E4A}\x{8E4B}\x{8E4C}\x{8E4D}\x{8E4E}\x{8E50}' .
+'\x{8E51}\x{8E52}\x{8E53}\x{8E54}\x{8E55}\x{8E56}\x{8E57}\x{8E58}\x{8E59}' .
+'\x{8E5A}\x{8E5B}\x{8E5C}\x{8E5D}\x{8E5E}\x{8E5F}\x{8E60}\x{8E61}\x{8E62}' .
+'\x{8E63}\x{8E64}\x{8E65}\x{8E66}\x{8E67}\x{8E68}\x{8E69}\x{8E6A}\x{8E6B}' .
+'\x{8E6C}\x{8E6D}\x{8E6F}\x{8E70}\x{8E71}\x{8E72}\x{8E73}\x{8E74}\x{8E76}' .
+'\x{8E78}\x{8E7A}\x{8E7B}\x{8E7C}\x{8E7D}\x{8E7E}\x{8E7F}\x{8E80}\x{8E81}' .
+'\x{8E82}\x{8E83}\x{8E84}\x{8E85}\x{8E86}\x{8E87}\x{8E88}\x{8E89}\x{8E8A}' .
+'\x{8E8B}\x{8E8C}\x{8E8D}\x{8E8E}\x{8E8F}\x{8E90}\x{8E91}\x{8E92}\x{8E93}' .
+'\x{8E94}\x{8E95}\x{8E96}\x{8E97}\x{8E98}\x{8E9A}\x{8E9C}\x{8E9D}\x{8E9E}' .
+'\x{8E9F}\x{8EA0}\x{8EA1}\x{8EA3}\x{8EA4}\x{8EA5}\x{8EA6}\x{8EA7}\x{8EA8}' .
+'\x{8EA9}\x{8EAA}\x{8EAB}\x{8EAC}\x{8EAD}\x{8EAE}\x{8EAF}\x{8EB0}\x{8EB1}' .
+'\x{8EB2}\x{8EB4}\x{8EB5}\x{8EB8}\x{8EB9}\x{8EBA}\x{8EBB}\x{8EBC}\x{8EBD}' .
+'\x{8EBE}\x{8EBF}\x{8EC0}\x{8EC2}\x{8EC3}\x{8EC5}\x{8EC6}\x{8EC7}\x{8EC8}' .
+'\x{8EC9}\x{8ECA}\x{8ECB}\x{8ECC}\x{8ECD}\x{8ECE}\x{8ECF}\x{8ED0}\x{8ED1}' .
+'\x{8ED2}\x{8ED3}\x{8ED4}\x{8ED5}\x{8ED6}\x{8ED7}\x{8ED8}\x{8EDA}\x{8EDB}' .
+'\x{8EDC}\x{8EDD}\x{8EDE}\x{8EDF}\x{8EE0}\x{8EE1}\x{8EE4}\x{8EE5}\x{8EE6}' .
+'\x{8EE7}\x{8EE8}\x{8EE9}\x{8EEA}\x{8EEB}\x{8EEC}\x{8EED}\x{8EEE}\x{8EEF}' .
+'\x{8EF1}\x{8EF2}\x{8EF3}\x{8EF4}\x{8EF5}\x{8EF6}\x{8EF7}\x{8EF8}\x{8EF9}' .
+'\x{8EFA}\x{8EFB}\x{8EFC}\x{8EFD}\x{8EFE}\x{8EFF}\x{8F00}\x{8F01}\x{8F02}' .
+'\x{8F03}\x{8F04}\x{8F05}\x{8F06}\x{8F07}\x{8F08}\x{8F09}\x{8F0A}\x{8F0B}' .
+'\x{8F0D}\x{8F0E}\x{8F10}\x{8F11}\x{8F12}\x{8F13}\x{8F14}\x{8F15}\x{8F16}' .
+'\x{8F17}\x{8F18}\x{8F1A}\x{8F1B}\x{8F1C}\x{8F1D}\x{8F1E}\x{8F1F}\x{8F20}' .
+'\x{8F21}\x{8F22}\x{8F23}\x{8F24}\x{8F25}\x{8F26}\x{8F27}\x{8F28}\x{8F29}' .
+'\x{8F2A}\x{8F2B}\x{8F2C}\x{8F2E}\x{8F2F}\x{8F30}\x{8F31}\x{8F32}\x{8F33}' .
+'\x{8F34}\x{8F35}\x{8F36}\x{8F37}\x{8F38}\x{8F39}\x{8F3B}\x{8F3C}\x{8F3D}' .
+'\x{8F3E}\x{8F3F}\x{8F40}\x{8F42}\x{8F43}\x{8F44}\x{8F45}\x{8F46}\x{8F47}' .
+'\x{8F48}\x{8F49}\x{8F4A}\x{8F4B}\x{8F4C}\x{8F4D}\x{8F4E}\x{8F4F}\x{8F50}' .
+'\x{8F51}\x{8F52}\x{8F53}\x{8F54}\x{8F55}\x{8F56}\x{8F57}\x{8F58}\x{8F59}' .
+'\x{8F5A}\x{8F5B}\x{8F5D}\x{8F5E}\x{8F5F}\x{8F60}\x{8F61}\x{8F62}\x{8F63}' .
+'\x{8F64}\x{8F65}\x{8F66}\x{8F67}\x{8F68}\x{8F69}\x{8F6A}\x{8F6B}\x{8F6C}' .
+'\x{8F6D}\x{8F6E}\x{8F6F}\x{8F70}\x{8F71}\x{8F72}\x{8F73}\x{8F74}\x{8F75}' .
+'\x{8F76}\x{8F77}\x{8F78}\x{8F79}\x{8F7A}\x{8F7B}\x{8F7C}\x{8F7D}\x{8F7E}' .
+'\x{8F7F}\x{8F80}\x{8F81}\x{8F82}\x{8F83}\x{8F84}\x{8F85}\x{8F86}\x{8F87}' .
+'\x{8F88}\x{8F89}\x{8F8A}\x{8F8B}\x{8F8C}\x{8F8D}\x{8F8E}\x{8F8F}\x{8F90}' .
+'\x{8F91}\x{8F92}\x{8F93}\x{8F94}\x{8F95}\x{8F96}\x{8F97}\x{8F98}\x{8F99}' .
+'\x{8F9A}\x{8F9B}\x{8F9C}\x{8F9E}\x{8F9F}\x{8FA0}\x{8FA1}\x{8FA2}\x{8FA3}' .
+'\x{8FA5}\x{8FA6}\x{8FA7}\x{8FA8}\x{8FA9}\x{8FAA}\x{8FAB}\x{8FAC}\x{8FAD}' .
+'\x{8FAE}\x{8FAF}\x{8FB0}\x{8FB1}\x{8FB2}\x{8FB4}\x{8FB5}\x{8FB6}\x{8FB7}' .
+'\x{8FB8}\x{8FB9}\x{8FBB}\x{8FBC}\x{8FBD}\x{8FBE}\x{8FBF}\x{8FC0}\x{8FC1}' .
+'\x{8FC2}\x{8FC4}\x{8FC5}\x{8FC6}\x{8FC7}\x{8FC8}\x{8FC9}\x{8FCB}\x{8FCC}' .
+'\x{8FCD}\x{8FCE}\x{8FCF}\x{8FD0}\x{8FD1}\x{8FD2}\x{8FD3}\x{8FD4}\x{8FD5}' .
+'\x{8FD6}\x{8FD7}\x{8FD8}\x{8FD9}\x{8FDA}\x{8FDB}\x{8FDC}\x{8FDD}\x{8FDE}' .
+'\x{8FDF}\x{8FE0}\x{8FE1}\x{8FE2}\x{8FE3}\x{8FE4}\x{8FE5}\x{8FE6}\x{8FE8}' .
+'\x{8FE9}\x{8FEA}\x{8FEB}\x{8FEC}\x{8FED}\x{8FEE}\x{8FEF}\x{8FF0}\x{8FF1}' .
+'\x{8FF2}\x{8FF3}\x{8FF4}\x{8FF5}\x{8FF6}\x{8FF7}\x{8FF8}\x{8FF9}\x{8FFA}' .
+'\x{8FFB}\x{8FFC}\x{8FFD}\x{8FFE}\x{8FFF}\x{9000}\x{9001}\x{9002}\x{9003}' .
+'\x{9004}\x{9005}\x{9006}\x{9007}\x{9008}\x{9009}\x{900A}\x{900B}\x{900C}' .
+'\x{900D}\x{900F}\x{9010}\x{9011}\x{9012}\x{9013}\x{9014}\x{9015}\x{9016}' .
+'\x{9017}\x{9018}\x{9019}\x{901A}\x{901B}\x{901C}\x{901D}\x{901E}\x{901F}' .
+'\x{9020}\x{9021}\x{9022}\x{9023}\x{9024}\x{9025}\x{9026}\x{9027}\x{9028}' .
+'\x{9029}\x{902B}\x{902D}\x{902E}\x{902F}\x{9030}\x{9031}\x{9032}\x{9033}' .
+'\x{9034}\x{9035}\x{9036}\x{9038}\x{903A}\x{903B}\x{903C}\x{903D}\x{903E}' .
+'\x{903F}\x{9041}\x{9042}\x{9043}\x{9044}\x{9045}\x{9047}\x{9048}\x{9049}' .
+'\x{904A}\x{904B}\x{904C}\x{904D}\x{904E}\x{904F}\x{9050}\x{9051}\x{9052}' .
+'\x{9053}\x{9054}\x{9055}\x{9056}\x{9057}\x{9058}\x{9059}\x{905A}\x{905B}' .
+'\x{905C}\x{905D}\x{905E}\x{905F}\x{9060}\x{9061}\x{9062}\x{9063}\x{9064}' .
+'\x{9065}\x{9066}\x{9067}\x{9068}\x{9069}\x{906A}\x{906B}\x{906C}\x{906D}' .
+'\x{906E}\x{906F}\x{9070}\x{9071}\x{9072}\x{9073}\x{9074}\x{9075}\x{9076}' .
+'\x{9077}\x{9078}\x{9079}\x{907A}\x{907B}\x{907C}\x{907D}\x{907E}\x{907F}' .
+'\x{9080}\x{9081}\x{9082}\x{9083}\x{9084}\x{9085}\x{9086}\x{9087}\x{9088}' .
+'\x{9089}\x{908A}\x{908B}\x{908C}\x{908D}\x{908E}\x{908F}\x{9090}\x{9091}' .
+'\x{9092}\x{9093}\x{9094}\x{9095}\x{9096}\x{9097}\x{9098}\x{9099}\x{909A}' .
+'\x{909B}\x{909C}\x{909D}\x{909E}\x{909F}\x{90A0}\x{90A1}\x{90A2}\x{90A3}' .
+'\x{90A4}\x{90A5}\x{90A6}\x{90A7}\x{90A8}\x{90A9}\x{90AA}\x{90AC}\x{90AD}' .
+'\x{90AE}\x{90AF}\x{90B0}\x{90B1}\x{90B2}\x{90B3}\x{90B4}\x{90B5}\x{90B6}' .
+'\x{90B7}\x{90B8}\x{90B9}\x{90BA}\x{90BB}\x{90BC}\x{90BD}\x{90BE}\x{90BF}' .
+'\x{90C0}\x{90C1}\x{90C2}\x{90C3}\x{90C4}\x{90C5}\x{90C6}\x{90C7}\x{90C8}' .
+'\x{90C9}\x{90CA}\x{90CB}\x{90CE}\x{90CF}\x{90D0}\x{90D1}\x{90D3}\x{90D4}' .
+'\x{90D5}\x{90D6}\x{90D7}\x{90D8}\x{90D9}\x{90DA}\x{90DB}\x{90DC}\x{90DD}' .
+'\x{90DE}\x{90DF}\x{90E0}\x{90E1}\x{90E2}\x{90E3}\x{90E4}\x{90E5}\x{90E6}' .
+'\x{90E7}\x{90E8}\x{90E9}\x{90EA}\x{90EB}\x{90EC}\x{90ED}\x{90EE}\x{90EF}' .
+'\x{90F0}\x{90F1}\x{90F2}\x{90F3}\x{90F4}\x{90F5}\x{90F7}\x{90F8}\x{90F9}' .
+'\x{90FA}\x{90FB}\x{90FC}\x{90FD}\x{90FE}\x{90FF}\x{9100}\x{9101}\x{9102}' .
+'\x{9103}\x{9104}\x{9105}\x{9106}\x{9107}\x{9108}\x{9109}\x{910B}\x{910C}' .
+'\x{910D}\x{910E}\x{910F}\x{9110}\x{9111}\x{9112}\x{9113}\x{9114}\x{9115}' .
+'\x{9116}\x{9117}\x{9118}\x{9119}\x{911A}\x{911B}\x{911C}\x{911D}\x{911E}' .
+'\x{911F}\x{9120}\x{9121}\x{9122}\x{9123}\x{9124}\x{9125}\x{9126}\x{9127}' .
+'\x{9128}\x{9129}\x{912A}\x{912B}\x{912C}\x{912D}\x{912E}\x{912F}\x{9130}' .
+'\x{9131}\x{9132}\x{9133}\x{9134}\x{9135}\x{9136}\x{9137}\x{9138}\x{9139}' .
+'\x{913A}\x{913B}\x{913E}\x{913F}\x{9140}\x{9141}\x{9142}\x{9143}\x{9144}' .
+'\x{9145}\x{9146}\x{9147}\x{9148}\x{9149}\x{914A}\x{914B}\x{914C}\x{914D}' .
+'\x{914E}\x{914F}\x{9150}\x{9151}\x{9152}\x{9153}\x{9154}\x{9155}\x{9156}' .
+'\x{9157}\x{9158}\x{915A}\x{915B}\x{915C}\x{915D}\x{915E}\x{915F}\x{9160}' .
+'\x{9161}\x{9162}\x{9163}\x{9164}\x{9165}\x{9166}\x{9167}\x{9168}\x{9169}' .
+'\x{916A}\x{916B}\x{916C}\x{916D}\x{916E}\x{916F}\x{9170}\x{9171}\x{9172}' .
+'\x{9173}\x{9174}\x{9175}\x{9176}\x{9177}\x{9178}\x{9179}\x{917A}\x{917C}' .
+'\x{917D}\x{917E}\x{917F}\x{9180}\x{9181}\x{9182}\x{9183}\x{9184}\x{9185}' .
+'\x{9186}\x{9187}\x{9188}\x{9189}\x{918A}\x{918B}\x{918C}\x{918D}\x{918E}' .
+'\x{918F}\x{9190}\x{9191}\x{9192}\x{9193}\x{9194}\x{9196}\x{9199}\x{919A}' .
+'\x{919B}\x{919C}\x{919D}\x{919E}\x{919F}\x{91A0}\x{91A1}\x{91A2}\x{91A3}' .
+'\x{91A5}\x{91A6}\x{91A7}\x{91A8}\x{91AA}\x{91AB}\x{91AC}\x{91AD}\x{91AE}' .
+'\x{91AF}\x{91B0}\x{91B1}\x{91B2}\x{91B3}\x{91B4}\x{91B5}\x{91B6}\x{91B7}' .
+'\x{91B9}\x{91BA}\x{91BB}\x{91BC}\x{91BD}\x{91BE}\x{91C0}\x{91C1}\x{91C2}' .
+'\x{91C3}\x{91C5}\x{91C6}\x{91C7}\x{91C9}\x{91CA}\x{91CB}\x{91CC}\x{91CD}' .
+'\x{91CE}\x{91CF}\x{91D0}\x{91D1}\x{91D2}\x{91D3}\x{91D4}\x{91D5}\x{91D7}' .
+'\x{91D8}\x{91D9}\x{91DA}\x{91DB}\x{91DC}\x{91DD}\x{91DE}\x{91DF}\x{91E2}' .
+'\x{91E3}\x{91E4}\x{91E5}\x{91E6}\x{91E7}\x{91E8}\x{91E9}\x{91EA}\x{91EB}' .
+'\x{91EC}\x{91ED}\x{91EE}\x{91F0}\x{91F1}\x{91F2}\x{91F3}\x{91F4}\x{91F5}' .
+'\x{91F7}\x{91F8}\x{91F9}\x{91FA}\x{91FB}\x{91FD}\x{91FE}\x{91FF}\x{9200}' .
+'\x{9201}\x{9202}\x{9203}\x{9204}\x{9205}\x{9206}\x{9207}\x{9208}\x{9209}' .
+'\x{920A}\x{920B}\x{920C}\x{920D}\x{920E}\x{920F}\x{9210}\x{9211}\x{9212}' .
+'\x{9214}\x{9215}\x{9216}\x{9217}\x{9218}\x{9219}\x{921A}\x{921B}\x{921C}' .
+'\x{921D}\x{921E}\x{9220}\x{9221}\x{9223}\x{9224}\x{9225}\x{9226}\x{9227}' .
+'\x{9228}\x{9229}\x{922A}\x{922B}\x{922D}\x{922E}\x{922F}\x{9230}\x{9231}' .
+'\x{9232}\x{9233}\x{9234}\x{9235}\x{9236}\x{9237}\x{9238}\x{9239}\x{923A}' .
+'\x{923B}\x{923C}\x{923D}\x{923E}\x{923F}\x{9240}\x{9241}\x{9242}\x{9245}' .
+'\x{9246}\x{9247}\x{9248}\x{9249}\x{924A}\x{924B}\x{924C}\x{924D}\x{924E}' .
+'\x{924F}\x{9250}\x{9251}\x{9252}\x{9253}\x{9254}\x{9255}\x{9256}\x{9257}' .
+'\x{9258}\x{9259}\x{925A}\x{925B}\x{925C}\x{925D}\x{925E}\x{925F}\x{9260}' .
+'\x{9261}\x{9262}\x{9263}\x{9264}\x{9265}\x{9266}\x{9267}\x{9268}\x{926B}' .
+'\x{926C}\x{926D}\x{926E}\x{926F}\x{9270}\x{9272}\x{9273}\x{9274}\x{9275}' .
+'\x{9276}\x{9277}\x{9278}\x{9279}\x{927A}\x{927B}\x{927C}\x{927D}\x{927E}' .
+'\x{927F}\x{9280}\x{9282}\x{9283}\x{9285}\x{9286}\x{9287}\x{9288}\x{9289}' .
+'\x{928A}\x{928B}\x{928C}\x{928D}\x{928E}\x{928F}\x{9290}\x{9291}\x{9292}' .
+'\x{9293}\x{9294}\x{9295}\x{9296}\x{9297}\x{9298}\x{9299}\x{929A}\x{929B}' .
+'\x{929C}\x{929D}\x{929F}\x{92A0}\x{92A1}\x{92A2}\x{92A3}\x{92A4}\x{92A5}' .
+'\x{92A6}\x{92A7}\x{92A8}\x{92A9}\x{92AA}\x{92AB}\x{92AC}\x{92AD}\x{92AE}' .
+'\x{92AF}\x{92B0}\x{92B1}\x{92B2}\x{92B3}\x{92B4}\x{92B5}\x{92B6}\x{92B7}' .
+'\x{92B8}\x{92B9}\x{92BA}\x{92BB}\x{92BC}\x{92BE}\x{92BF}\x{92C0}\x{92C1}' .
+'\x{92C2}\x{92C3}\x{92C4}\x{92C5}\x{92C6}\x{92C7}\x{92C8}\x{92C9}\x{92CA}' .
+'\x{92CB}\x{92CC}\x{92CD}\x{92CE}\x{92CF}\x{92D0}\x{92D1}\x{92D2}\x{92D3}' .
+'\x{92D5}\x{92D6}\x{92D7}\x{92D8}\x{92D9}\x{92DA}\x{92DC}\x{92DD}\x{92DE}' .
+'\x{92DF}\x{92E0}\x{92E1}\x{92E3}\x{92E4}\x{92E5}\x{92E6}\x{92E7}\x{92E8}' .
+'\x{92E9}\x{92EA}\x{92EB}\x{92EC}\x{92ED}\x{92EE}\x{92EF}\x{92F0}\x{92F1}' .
+'\x{92F2}\x{92F3}\x{92F4}\x{92F5}\x{92F6}\x{92F7}\x{92F8}\x{92F9}\x{92FA}' .
+'\x{92FB}\x{92FC}\x{92FD}\x{92FE}\x{92FF}\x{9300}\x{9301}\x{9302}\x{9303}' .
+'\x{9304}\x{9305}\x{9306}\x{9307}\x{9308}\x{9309}\x{930A}\x{930B}\x{930C}' .
+'\x{930D}\x{930E}\x{930F}\x{9310}\x{9311}\x{9312}\x{9313}\x{9314}\x{9315}' .
+'\x{9316}\x{9317}\x{9318}\x{9319}\x{931A}\x{931B}\x{931D}\x{931E}\x{931F}' .
+'\x{9320}\x{9321}\x{9322}\x{9323}\x{9324}\x{9325}\x{9326}\x{9327}\x{9328}' .
+'\x{9329}\x{932A}\x{932B}\x{932D}\x{932E}\x{932F}\x{9332}\x{9333}\x{9334}' .
+'\x{9335}\x{9336}\x{9337}\x{9338}\x{9339}\x{933A}\x{933B}\x{933C}\x{933D}' .
+'\x{933E}\x{933F}\x{9340}\x{9341}\x{9342}\x{9343}\x{9344}\x{9345}\x{9346}' .
+'\x{9347}\x{9348}\x{9349}\x{934A}\x{934B}\x{934C}\x{934D}\x{934E}\x{934F}' .
+'\x{9350}\x{9351}\x{9352}\x{9353}\x{9354}\x{9355}\x{9356}\x{9357}\x{9358}' .
+'\x{9359}\x{935A}\x{935B}\x{935C}\x{935D}\x{935E}\x{935F}\x{9360}\x{9361}' .
+'\x{9363}\x{9364}\x{9365}\x{9366}\x{9367}\x{9369}\x{936A}\x{936C}\x{936D}' .
+'\x{936E}\x{9370}\x{9371}\x{9372}\x{9374}\x{9375}\x{9376}\x{9377}\x{9379}' .
+'\x{937A}\x{937B}\x{937C}\x{937D}\x{937E}\x{9380}\x{9382}\x{9383}\x{9384}' .
+'\x{9385}\x{9386}\x{9387}\x{9388}\x{9389}\x{938A}\x{938C}\x{938D}\x{938E}' .
+'\x{938F}\x{9390}\x{9391}\x{9392}\x{9393}\x{9394}\x{9395}\x{9396}\x{9397}' .
+'\x{9398}\x{9399}\x{939A}\x{939B}\x{939D}\x{939E}\x{939F}\x{93A1}\x{93A2}' .
+'\x{93A3}\x{93A4}\x{93A5}\x{93A6}\x{93A7}\x{93A8}\x{93A9}\x{93AA}\x{93AC}' .
+'\x{93AD}\x{93AE}\x{93AF}\x{93B0}\x{93B1}\x{93B2}\x{93B3}\x{93B4}\x{93B5}' .
+'\x{93B6}\x{93B7}\x{93B8}\x{93B9}\x{93BA}\x{93BC}\x{93BD}\x{93BE}\x{93BF}' .
+'\x{93C0}\x{93C1}\x{93C2}\x{93C3}\x{93C4}\x{93C5}\x{93C6}\x{93C7}\x{93C8}' .
+'\x{93C9}\x{93CA}\x{93CB}\x{93CC}\x{93CD}\x{93CE}\x{93CF}\x{93D0}\x{93D1}' .
+'\x{93D2}\x{93D3}\x{93D4}\x{93D5}\x{93D6}\x{93D7}\x{93D8}\x{93D9}\x{93DA}' .
+'\x{93DB}\x{93DC}\x{93DD}\x{93DE}\x{93DF}\x{93E1}\x{93E2}\x{93E3}\x{93E4}' .
+'\x{93E6}\x{93E7}\x{93E8}\x{93E9}\x{93EA}\x{93EB}\x{93EC}\x{93ED}\x{93EE}' .
+'\x{93EF}\x{93F0}\x{93F1}\x{93F2}\x{93F4}\x{93F5}\x{93F6}\x{93F7}\x{93F8}' .
+'\x{93F9}\x{93FA}\x{93FB}\x{93FC}\x{93FD}\x{93FE}\x{93FF}\x{9400}\x{9401}' .
+'\x{9403}\x{9404}\x{9405}\x{9406}\x{9407}\x{9408}\x{9409}\x{940A}\x{940B}' .
+'\x{940C}\x{940D}\x{940E}\x{940F}\x{9410}\x{9411}\x{9412}\x{9413}\x{9414}' .
+'\x{9415}\x{9416}\x{9418}\x{9419}\x{941B}\x{941D}\x{9420}\x{9422}\x{9423}' .
+'\x{9425}\x{9426}\x{9427}\x{9428}\x{9429}\x{942A}\x{942B}\x{942C}\x{942D}' .
+'\x{942E}\x{942F}\x{9430}\x{9431}\x{9432}\x{9433}\x{9434}\x{9435}\x{9436}' .
+'\x{9437}\x{9438}\x{9439}\x{943A}\x{943B}\x{943C}\x{943D}\x{943E}\x{943F}' .
+'\x{9440}\x{9441}\x{9442}\x{9444}\x{9445}\x{9446}\x{9447}\x{9448}\x{9449}' .
+'\x{944A}\x{944B}\x{944C}\x{944D}\x{944F}\x{9450}\x{9451}\x{9452}\x{9453}' .
+'\x{9454}\x{9455}\x{9456}\x{9457}\x{9458}\x{9459}\x{945B}\x{945C}\x{945D}' .
+'\x{945E}\x{945F}\x{9460}\x{9461}\x{9462}\x{9463}\x{9464}\x{9465}\x{9466}' .
+'\x{9467}\x{9468}\x{9469}\x{946A}\x{946B}\x{946D}\x{946E}\x{946F}\x{9470}' .
+'\x{9471}\x{9472}\x{9473}\x{9474}\x{9475}\x{9476}\x{9477}\x{9478}\x{9479}' .
+'\x{947A}\x{947C}\x{947D}\x{947E}\x{947F}\x{9480}\x{9481}\x{9482}\x{9483}' .
+'\x{9484}\x{9485}\x{9486}\x{9487}\x{9488}\x{9489}\x{948A}\x{948B}\x{948C}' .
+'\x{948D}\x{948E}\x{948F}\x{9490}\x{9491}\x{9492}\x{9493}\x{9494}\x{9495}' .
+'\x{9496}\x{9497}\x{9498}\x{9499}\x{949A}\x{949B}\x{949C}\x{949D}\x{949E}' .
+'\x{949F}\x{94A0}\x{94A1}\x{94A2}\x{94A3}\x{94A4}\x{94A5}\x{94A6}\x{94A7}' .
+'\x{94A8}\x{94A9}\x{94AA}\x{94AB}\x{94AC}\x{94AD}\x{94AE}\x{94AF}\x{94B0}' .
+'\x{94B1}\x{94B2}\x{94B3}\x{94B4}\x{94B5}\x{94B6}\x{94B7}\x{94B8}\x{94B9}' .
+'\x{94BA}\x{94BB}\x{94BC}\x{94BD}\x{94BE}\x{94BF}\x{94C0}\x{94C1}\x{94C2}' .
+'\x{94C3}\x{94C4}\x{94C5}\x{94C6}\x{94C7}\x{94C8}\x{94C9}\x{94CA}\x{94CB}' .
+'\x{94CC}\x{94CD}\x{94CE}\x{94CF}\x{94D0}\x{94D1}\x{94D2}\x{94D3}\x{94D4}' .
+'\x{94D5}\x{94D6}\x{94D7}\x{94D8}\x{94D9}\x{94DA}\x{94DB}\x{94DC}\x{94DD}' .
+'\x{94DE}\x{94DF}\x{94E0}\x{94E1}\x{94E2}\x{94E3}\x{94E4}\x{94E5}\x{94E6}' .
+'\x{94E7}\x{94E8}\x{94E9}\x{94EA}\x{94EB}\x{94EC}\x{94ED}\x{94EE}\x{94EF}' .
+'\x{94F0}\x{94F1}\x{94F2}\x{94F3}\x{94F4}\x{94F5}\x{94F6}\x{94F7}\x{94F8}' .
+'\x{94F9}\x{94FA}\x{94FB}\x{94FC}\x{94FD}\x{94FE}\x{94FF}\x{9500}\x{9501}' .
+'\x{9502}\x{9503}\x{9504}\x{9505}\x{9506}\x{9507}\x{9508}\x{9509}\x{950A}' .
+'\x{950B}\x{950C}\x{950D}\x{950E}\x{950F}\x{9510}\x{9511}\x{9512}\x{9513}' .
+'\x{9514}\x{9515}\x{9516}\x{9517}\x{9518}\x{9519}\x{951A}\x{951B}\x{951C}' .
+'\x{951D}\x{951E}\x{951F}\x{9520}\x{9521}\x{9522}\x{9523}\x{9524}\x{9525}' .
+'\x{9526}\x{9527}\x{9528}\x{9529}\x{952A}\x{952B}\x{952C}\x{952D}\x{952E}' .
+'\x{952F}\x{9530}\x{9531}\x{9532}\x{9533}\x{9534}\x{9535}\x{9536}\x{9537}' .
+'\x{9538}\x{9539}\x{953A}\x{953B}\x{953C}\x{953D}\x{953E}\x{953F}\x{9540}' .
+'\x{9541}\x{9542}\x{9543}\x{9544}\x{9545}\x{9546}\x{9547}\x{9548}\x{9549}' .
+'\x{954A}\x{954B}\x{954C}\x{954D}\x{954E}\x{954F}\x{9550}\x{9551}\x{9552}' .
+'\x{9553}\x{9554}\x{9555}\x{9556}\x{9557}\x{9558}\x{9559}\x{955A}\x{955B}' .
+'\x{955C}\x{955D}\x{955E}\x{955F}\x{9560}\x{9561}\x{9562}\x{9563}\x{9564}' .
+'\x{9565}\x{9566}\x{9567}\x{9568}\x{9569}\x{956A}\x{956B}\x{956C}\x{956D}' .
+'\x{956E}\x{956F}\x{9570}\x{9571}\x{9572}\x{9573}\x{9574}\x{9575}\x{9576}' .
+'\x{9577}\x{957A}\x{957B}\x{957C}\x{957D}\x{957F}\x{9580}\x{9581}\x{9582}' .
+'\x{9583}\x{9584}\x{9586}\x{9587}\x{9588}\x{9589}\x{958A}\x{958B}\x{958C}' .
+'\x{958D}\x{958E}\x{958F}\x{9590}\x{9591}\x{9592}\x{9593}\x{9594}\x{9595}' .
+'\x{9596}\x{9598}\x{9599}\x{959A}\x{959B}\x{959C}\x{959D}\x{959E}\x{959F}' .
+'\x{95A1}\x{95A2}\x{95A3}\x{95A4}\x{95A5}\x{95A6}\x{95A7}\x{95A8}\x{95A9}' .
+'\x{95AA}\x{95AB}\x{95AC}\x{95AD}\x{95AE}\x{95AF}\x{95B0}\x{95B1}\x{95B2}' .
+'\x{95B5}\x{95B6}\x{95B7}\x{95B9}\x{95BA}\x{95BB}\x{95BC}\x{95BD}\x{95BE}' .
+'\x{95BF}\x{95C0}\x{95C2}\x{95C3}\x{95C4}\x{95C5}\x{95C6}\x{95C7}\x{95C8}' .
+'\x{95C9}\x{95CA}\x{95CB}\x{95CC}\x{95CD}\x{95CE}\x{95CF}\x{95D0}\x{95D1}' .
+'\x{95D2}\x{95D3}\x{95D4}\x{95D5}\x{95D6}\x{95D7}\x{95D8}\x{95DA}\x{95DB}' .
+'\x{95DC}\x{95DE}\x{95DF}\x{95E0}\x{95E1}\x{95E2}\x{95E3}\x{95E4}\x{95E5}' .
+'\x{95E6}\x{95E7}\x{95E8}\x{95E9}\x{95EA}\x{95EB}\x{95EC}\x{95ED}\x{95EE}' .
+'\x{95EF}\x{95F0}\x{95F1}\x{95F2}\x{95F3}\x{95F4}\x{95F5}\x{95F6}\x{95F7}' .
+'\x{95F8}\x{95F9}\x{95FA}\x{95FB}\x{95FC}\x{95FD}\x{95FE}\x{95FF}\x{9600}' .
+'\x{9601}\x{9602}\x{9603}\x{9604}\x{9605}\x{9606}\x{9607}\x{9608}\x{9609}' .
+'\x{960A}\x{960B}\x{960C}\x{960D}\x{960E}\x{960F}\x{9610}\x{9611}\x{9612}' .
+'\x{9613}\x{9614}\x{9615}\x{9616}\x{9617}\x{9618}\x{9619}\x{961A}\x{961B}' .
+'\x{961C}\x{961D}\x{961E}\x{961F}\x{9620}\x{9621}\x{9622}\x{9623}\x{9624}' .
+'\x{9627}\x{9628}\x{962A}\x{962B}\x{962C}\x{962D}\x{962E}\x{962F}\x{9630}' .
+'\x{9631}\x{9632}\x{9633}\x{9634}\x{9635}\x{9636}\x{9637}\x{9638}\x{9639}' .
+'\x{963A}\x{963B}\x{963C}\x{963D}\x{963F}\x{9640}\x{9641}\x{9642}\x{9643}' .
+'\x{9644}\x{9645}\x{9646}\x{9647}\x{9648}\x{9649}\x{964A}\x{964B}\x{964C}' .
+'\x{964D}\x{964E}\x{964F}\x{9650}\x{9651}\x{9652}\x{9653}\x{9654}\x{9655}' .
+'\x{9658}\x{9659}\x{965A}\x{965B}\x{965C}\x{965D}\x{965E}\x{965F}\x{9660}' .
+'\x{9661}\x{9662}\x{9663}\x{9664}\x{9666}\x{9667}\x{9668}\x{9669}\x{966A}' .
+'\x{966B}\x{966C}\x{966D}\x{966E}\x{966F}\x{9670}\x{9671}\x{9672}\x{9673}' .
+'\x{9674}\x{9675}\x{9676}\x{9677}\x{9678}\x{967C}\x{967D}\x{967E}\x{9680}' .
+'\x{9683}\x{9684}\x{9685}\x{9686}\x{9687}\x{9688}\x{9689}\x{968A}\x{968B}' .
+'\x{968D}\x{968E}\x{968F}\x{9690}\x{9691}\x{9692}\x{9693}\x{9694}\x{9695}' .
+'\x{9697}\x{9698}\x{9699}\x{969B}\x{969C}\x{969E}\x{96A0}\x{96A1}\x{96A2}' .
+'\x{96A3}\x{96A4}\x{96A5}\x{96A6}\x{96A7}\x{96A8}\x{96A9}\x{96AA}\x{96AC}' .
+'\x{96AD}\x{96AE}\x{96B0}\x{96B1}\x{96B3}\x{96B4}\x{96B6}\x{96B7}\x{96B8}' .
+'\x{96B9}\x{96BA}\x{96BB}\x{96BC}\x{96BD}\x{96BE}\x{96BF}\x{96C0}\x{96C1}' .
+'\x{96C2}\x{96C3}\x{96C4}\x{96C5}\x{96C6}\x{96C7}\x{96C8}\x{96C9}\x{96CA}' .
+'\x{96CB}\x{96CC}\x{96CD}\x{96CE}\x{96CF}\x{96D0}\x{96D1}\x{96D2}\x{96D3}' .
+'\x{96D4}\x{96D5}\x{96D6}\x{96D7}\x{96D8}\x{96D9}\x{96DA}\x{96DB}\x{96DC}' .
+'\x{96DD}\x{96DE}\x{96DF}\x{96E0}\x{96E1}\x{96E2}\x{96E3}\x{96E5}\x{96E8}' .
+'\x{96E9}\x{96EA}\x{96EB}\x{96EC}\x{96ED}\x{96EE}\x{96EF}\x{96F0}\x{96F1}' .
+'\x{96F2}\x{96F3}\x{96F4}\x{96F5}\x{96F6}\x{96F7}\x{96F8}\x{96F9}\x{96FA}' .
+'\x{96FB}\x{96FD}\x{96FE}\x{96FF}\x{9700}\x{9701}\x{9702}\x{9703}\x{9704}' .
+'\x{9705}\x{9706}\x{9707}\x{9708}\x{9709}\x{970A}\x{970B}\x{970C}\x{970D}' .
+'\x{970E}\x{970F}\x{9710}\x{9711}\x{9712}\x{9713}\x{9715}\x{9716}\x{9718}' .
+'\x{9719}\x{971C}\x{971D}\x{971E}\x{971F}\x{9720}\x{9721}\x{9722}\x{9723}' .
+'\x{9724}\x{9725}\x{9726}\x{9727}\x{9728}\x{9729}\x{972A}\x{972B}\x{972C}' .
+'\x{972D}\x{972E}\x{972F}\x{9730}\x{9731}\x{9732}\x{9735}\x{9736}\x{9738}' .
+'\x{9739}\x{973A}\x{973B}\x{973C}\x{973D}\x{973E}\x{973F}\x{9742}\x{9743}' .
+'\x{9744}\x{9745}\x{9746}\x{9747}\x{9748}\x{9749}\x{974A}\x{974B}\x{974C}' .
+'\x{974E}\x{974F}\x{9750}\x{9751}\x{9752}\x{9753}\x{9754}\x{9755}\x{9756}' .
+'\x{9758}\x{9759}\x{975A}\x{975B}\x{975C}\x{975D}\x{975E}\x{975F}\x{9760}' .
+'\x{9761}\x{9762}\x{9765}\x{9766}\x{9767}\x{9768}\x{9769}\x{976A}\x{976B}' .
+'\x{976C}\x{976D}\x{976E}\x{976F}\x{9770}\x{9772}\x{9773}\x{9774}\x{9776}' .
+'\x{9777}\x{9778}\x{9779}\x{977A}\x{977B}\x{977C}\x{977D}\x{977E}\x{977F}' .
+'\x{9780}\x{9781}\x{9782}\x{9783}\x{9784}\x{9785}\x{9786}\x{9788}\x{978A}' .
+'\x{978B}\x{978C}\x{978D}\x{978E}\x{978F}\x{9790}\x{9791}\x{9792}\x{9793}' .
+'\x{9794}\x{9795}\x{9796}\x{9797}\x{9798}\x{9799}\x{979A}\x{979C}\x{979D}' .
+'\x{979E}\x{979F}\x{97A0}\x{97A1}\x{97A2}\x{97A3}\x{97A4}\x{97A5}\x{97A6}' .
+'\x{97A7}\x{97A8}\x{97AA}\x{97AB}\x{97AC}\x{97AD}\x{97AE}\x{97AF}\x{97B2}' .
+'\x{97B3}\x{97B4}\x{97B6}\x{97B7}\x{97B8}\x{97B9}\x{97BA}\x{97BB}\x{97BC}' .
+'\x{97BD}\x{97BF}\x{97C1}\x{97C2}\x{97C3}\x{97C4}\x{97C5}\x{97C6}\x{97C7}' .
+'\x{97C8}\x{97C9}\x{97CA}\x{97CB}\x{97CC}\x{97CD}\x{97CE}\x{97CF}\x{97D0}' .
+'\x{97D1}\x{97D3}\x{97D4}\x{97D5}\x{97D6}\x{97D7}\x{97D8}\x{97D9}\x{97DA}' .
+'\x{97DB}\x{97DC}\x{97DD}\x{97DE}\x{97DF}\x{97E0}\x{97E1}\x{97E2}\x{97E3}' .
+'\x{97E4}\x{97E5}\x{97E6}\x{97E7}\x{97E8}\x{97E9}\x{97EA}\x{97EB}\x{97EC}' .
+'\x{97ED}\x{97EE}\x{97EF}\x{97F0}\x{97F1}\x{97F2}\x{97F3}\x{97F4}\x{97F5}' .
+'\x{97F6}\x{97F7}\x{97F8}\x{97F9}\x{97FA}\x{97FB}\x{97FD}\x{97FE}\x{97FF}' .
+'\x{9800}\x{9801}\x{9802}\x{9803}\x{9804}\x{9805}\x{9806}\x{9807}\x{9808}' .
+'\x{9809}\x{980A}\x{980B}\x{980C}\x{980D}\x{980E}\x{980F}\x{9810}\x{9811}' .
+'\x{9812}\x{9813}\x{9814}\x{9815}\x{9816}\x{9817}\x{9818}\x{9819}\x{981A}' .
+'\x{981B}\x{981C}\x{981D}\x{981E}\x{9820}\x{9821}\x{9822}\x{9823}\x{9824}' .
+'\x{9826}\x{9827}\x{9828}\x{9829}\x{982B}\x{982D}\x{982E}\x{982F}\x{9830}' .
+'\x{9831}\x{9832}\x{9834}\x{9835}\x{9836}\x{9837}\x{9838}\x{9839}\x{983B}' .
+'\x{983C}\x{983D}\x{983F}\x{9840}\x{9841}\x{9843}\x{9844}\x{9845}\x{9846}' .
+'\x{9848}\x{9849}\x{984A}\x{984C}\x{984D}\x{984E}\x{984F}\x{9850}\x{9851}' .
+'\x{9852}\x{9853}\x{9854}\x{9855}\x{9857}\x{9858}\x{9859}\x{985A}\x{985B}' .
+'\x{985C}\x{985D}\x{985E}\x{985F}\x{9860}\x{9861}\x{9862}\x{9863}\x{9864}' .
+'\x{9865}\x{9867}\x{9869}\x{986A}\x{986B}\x{986C}\x{986D}\x{986E}\x{986F}' .
+'\x{9870}\x{9871}\x{9872}\x{9873}\x{9874}\x{9875}\x{9876}\x{9877}\x{9878}' .
+'\x{9879}\x{987A}\x{987B}\x{987C}\x{987D}\x{987E}\x{987F}\x{9880}\x{9881}' .
+'\x{9882}\x{9883}\x{9884}\x{9885}\x{9886}\x{9887}\x{9888}\x{9889}\x{988A}' .
+'\x{988B}\x{988C}\x{988D}\x{988E}\x{988F}\x{9890}\x{9891}\x{9892}\x{9893}' .
+'\x{9894}\x{9895}\x{9896}\x{9897}\x{9898}\x{9899}\x{989A}\x{989B}\x{989C}' .
+'\x{989D}\x{989E}\x{989F}\x{98A0}\x{98A1}\x{98A2}\x{98A3}\x{98A4}\x{98A5}' .
+'\x{98A6}\x{98A7}\x{98A8}\x{98A9}\x{98AA}\x{98AB}\x{98AC}\x{98AD}\x{98AE}' .
+'\x{98AF}\x{98B0}\x{98B1}\x{98B2}\x{98B3}\x{98B4}\x{98B5}\x{98B6}\x{98B8}' .
+'\x{98B9}\x{98BA}\x{98BB}\x{98BC}\x{98BD}\x{98BE}\x{98BF}\x{98C0}\x{98C1}' .
+'\x{98C2}\x{98C3}\x{98C4}\x{98C5}\x{98C6}\x{98C8}\x{98C9}\x{98CB}\x{98CC}' .
+'\x{98CD}\x{98CE}\x{98CF}\x{98D0}\x{98D1}\x{98D2}\x{98D3}\x{98D4}\x{98D5}' .
+'\x{98D6}\x{98D7}\x{98D8}\x{98D9}\x{98DA}\x{98DB}\x{98DC}\x{98DD}\x{98DE}' .
+'\x{98DF}\x{98E0}\x{98E2}\x{98E3}\x{98E5}\x{98E6}\x{98E7}\x{98E8}\x{98E9}' .
+'\x{98EA}\x{98EB}\x{98ED}\x{98EF}\x{98F0}\x{98F2}\x{98F3}\x{98F4}\x{98F5}' .
+'\x{98F6}\x{98F7}\x{98F9}\x{98FA}\x{98FC}\x{98FD}\x{98FE}\x{98FF}\x{9900}' .
+'\x{9901}\x{9902}\x{9903}\x{9904}\x{9905}\x{9906}\x{9907}\x{9908}\x{9909}' .
+'\x{990A}\x{990B}\x{990C}\x{990D}\x{990E}\x{990F}\x{9910}\x{9911}\x{9912}' .
+'\x{9913}\x{9914}\x{9915}\x{9916}\x{9917}\x{9918}\x{991A}\x{991B}\x{991C}' .
+'\x{991D}\x{991E}\x{991F}\x{9920}\x{9921}\x{9922}\x{9923}\x{9924}\x{9925}' .
+'\x{9926}\x{9927}\x{9928}\x{9929}\x{992A}\x{992B}\x{992C}\x{992D}\x{992E}' .
+'\x{992F}\x{9930}\x{9931}\x{9932}\x{9933}\x{9934}\x{9935}\x{9936}\x{9937}' .
+'\x{9938}\x{9939}\x{993A}\x{993C}\x{993D}\x{993E}\x{993F}\x{9940}\x{9941}' .
+'\x{9942}\x{9943}\x{9945}\x{9946}\x{9947}\x{9948}\x{9949}\x{994A}\x{994B}' .
+'\x{994C}\x{994E}\x{994F}\x{9950}\x{9951}\x{9952}\x{9953}\x{9954}\x{9955}' .
+'\x{9956}\x{9957}\x{9958}\x{9959}\x{995B}\x{995C}\x{995E}\x{995F}\x{9960}' .
+'\x{9961}\x{9962}\x{9963}\x{9964}\x{9965}\x{9966}\x{9967}\x{9968}\x{9969}' .
+'\x{996A}\x{996B}\x{996C}\x{996D}\x{996E}\x{996F}\x{9970}\x{9971}\x{9972}' .
+'\x{9973}\x{9974}\x{9975}\x{9976}\x{9977}\x{9978}\x{9979}\x{997A}\x{997B}' .
+'\x{997C}\x{997D}\x{997E}\x{997F}\x{9980}\x{9981}\x{9982}\x{9983}\x{9984}' .
+'\x{9985}\x{9986}\x{9987}\x{9988}\x{9989}\x{998A}\x{998B}\x{998C}\x{998D}' .
+'\x{998E}\x{998F}\x{9990}\x{9991}\x{9992}\x{9993}\x{9994}\x{9995}\x{9996}' .
+'\x{9997}\x{9998}\x{9999}\x{999A}\x{999B}\x{999C}\x{999D}\x{999E}\x{999F}' .
+'\x{99A0}\x{99A1}\x{99A2}\x{99A3}\x{99A4}\x{99A5}\x{99A6}\x{99A7}\x{99A8}' .
+'\x{99A9}\x{99AA}\x{99AB}\x{99AC}\x{99AD}\x{99AE}\x{99AF}\x{99B0}\x{99B1}' .
+'\x{99B2}\x{99B3}\x{99B4}\x{99B5}\x{99B6}\x{99B7}\x{99B8}\x{99B9}\x{99BA}' .
+'\x{99BB}\x{99BC}\x{99BD}\x{99BE}\x{99C0}\x{99C1}\x{99C2}\x{99C3}\x{99C4}' .
+'\x{99C6}\x{99C7}\x{99C8}\x{99C9}\x{99CA}\x{99CB}\x{99CC}\x{99CD}\x{99CE}' .
+'\x{99CF}\x{99D0}\x{99D1}\x{99D2}\x{99D3}\x{99D4}\x{99D5}\x{99D6}\x{99D7}' .
+'\x{99D8}\x{99D9}\x{99DA}\x{99DB}\x{99DC}\x{99DD}\x{99DE}\x{99DF}\x{99E1}' .
+'\x{99E2}\x{99E3}\x{99E4}\x{99E5}\x{99E7}\x{99E8}\x{99E9}\x{99EA}\x{99EC}' .
+'\x{99ED}\x{99EE}\x{99EF}\x{99F0}\x{99F1}\x{99F2}\x{99F3}\x{99F4}\x{99F6}' .
+'\x{99F7}\x{99F8}\x{99F9}\x{99FA}\x{99FB}\x{99FC}\x{99FD}\x{99FE}\x{99FF}' .
+'\x{9A00}\x{9A01}\x{9A02}\x{9A03}\x{9A04}\x{9A05}\x{9A06}\x{9A07}\x{9A08}' .
+'\x{9A09}\x{9A0A}\x{9A0B}\x{9A0C}\x{9A0D}\x{9A0E}\x{9A0F}\x{9A11}\x{9A14}' .
+'\x{9A15}\x{9A16}\x{9A19}\x{9A1A}\x{9A1B}\x{9A1C}\x{9A1D}\x{9A1E}\x{9A1F}' .
+'\x{9A20}\x{9A21}\x{9A22}\x{9A23}\x{9A24}\x{9A25}\x{9A26}\x{9A27}\x{9A29}' .
+'\x{9A2A}\x{9A2B}\x{9A2C}\x{9A2D}\x{9A2E}\x{9A2F}\x{9A30}\x{9A31}\x{9A32}' .
+'\x{9A33}\x{9A34}\x{9A35}\x{9A36}\x{9A37}\x{9A38}\x{9A39}\x{9A3A}\x{9A3C}' .
+'\x{9A3D}\x{9A3E}\x{9A3F}\x{9A40}\x{9A41}\x{9A42}\x{9A43}\x{9A44}\x{9A45}' .
+'\x{9A46}\x{9A47}\x{9A48}\x{9A49}\x{9A4A}\x{9A4B}\x{9A4C}\x{9A4D}\x{9A4E}' .
+'\x{9A4F}\x{9A50}\x{9A52}\x{9A53}\x{9A54}\x{9A55}\x{9A56}\x{9A57}\x{9A59}' .
+'\x{9A5A}\x{9A5B}\x{9A5C}\x{9A5E}\x{9A5F}\x{9A60}\x{9A61}\x{9A62}\x{9A64}' .
+'\x{9A65}\x{9A66}\x{9A67}\x{9A68}\x{9A69}\x{9A6A}\x{9A6B}\x{9A6C}\x{9A6D}' .
+'\x{9A6E}\x{9A6F}\x{9A70}\x{9A71}\x{9A72}\x{9A73}\x{9A74}\x{9A75}\x{9A76}' .
+'\x{9A77}\x{9A78}\x{9A79}\x{9A7A}\x{9A7B}\x{9A7C}\x{9A7D}\x{9A7E}\x{9A7F}' .
+'\x{9A80}\x{9A81}\x{9A82}\x{9A83}\x{9A84}\x{9A85}\x{9A86}\x{9A87}\x{9A88}' .
+'\x{9A89}\x{9A8A}\x{9A8B}\x{9A8C}\x{9A8D}\x{9A8E}\x{9A8F}\x{9A90}\x{9A91}' .
+'\x{9A92}\x{9A93}\x{9A94}\x{9A95}\x{9A96}\x{9A97}\x{9A98}\x{9A99}\x{9A9A}' .
+'\x{9A9B}\x{9A9C}\x{9A9D}\x{9A9E}\x{9A9F}\x{9AA0}\x{9AA1}\x{9AA2}\x{9AA3}' .
+'\x{9AA4}\x{9AA5}\x{9AA6}\x{9AA7}\x{9AA8}\x{9AAA}\x{9AAB}\x{9AAC}\x{9AAD}' .
+'\x{9AAE}\x{9AAF}\x{9AB0}\x{9AB1}\x{9AB2}\x{9AB3}\x{9AB4}\x{9AB5}\x{9AB6}' .
+'\x{9AB7}\x{9AB8}\x{9AB9}\x{9ABA}\x{9ABB}\x{9ABC}\x{9ABE}\x{9ABF}\x{9AC0}' .
+'\x{9AC1}\x{9AC2}\x{9AC3}\x{9AC4}\x{9AC5}\x{9AC6}\x{9AC7}\x{9AC9}\x{9ACA}' .
+'\x{9ACB}\x{9ACC}\x{9ACD}\x{9ACE}\x{9ACF}\x{9AD0}\x{9AD1}\x{9AD2}\x{9AD3}' .
+'\x{9AD4}\x{9AD5}\x{9AD6}\x{9AD8}\x{9AD9}\x{9ADA}\x{9ADB}\x{9ADC}\x{9ADD}' .
+'\x{9ADE}\x{9ADF}\x{9AE1}\x{9AE2}\x{9AE3}\x{9AE5}\x{9AE6}\x{9AE7}\x{9AEA}' .
+'\x{9AEB}\x{9AEC}\x{9AED}\x{9AEE}\x{9AEF}\x{9AF1}\x{9AF2}\x{9AF3}\x{9AF4}' .
+'\x{9AF5}\x{9AF6}\x{9AF7}\x{9AF8}\x{9AF9}\x{9AFA}\x{9AFB}\x{9AFC}\x{9AFD}' .
+'\x{9AFE}\x{9AFF}\x{9B01}\x{9B03}\x{9B04}\x{9B05}\x{9B06}\x{9B07}\x{9B08}' .
+'\x{9B0A}\x{9B0B}\x{9B0C}\x{9B0D}\x{9B0E}\x{9B0F}\x{9B10}\x{9B11}\x{9B12}' .
+'\x{9B13}\x{9B15}\x{9B16}\x{9B17}\x{9B18}\x{9B19}\x{9B1A}\x{9B1C}\x{9B1D}' .
+'\x{9B1E}\x{9B1F}\x{9B20}\x{9B21}\x{9B22}\x{9B23}\x{9B24}\x{9B25}\x{9B26}' .
+'\x{9B27}\x{9B28}\x{9B29}\x{9B2A}\x{9B2B}\x{9B2C}\x{9B2D}\x{9B2E}\x{9B2F}' .
+'\x{9B30}\x{9B31}\x{9B32}\x{9B33}\x{9B35}\x{9B36}\x{9B37}\x{9B38}\x{9B39}' .
+'\x{9B3A}\x{9B3B}\x{9B3C}\x{9B3E}\x{9B3F}\x{9B41}\x{9B42}\x{9B43}\x{9B44}' .
+'\x{9B45}\x{9B46}\x{9B47}\x{9B48}\x{9B49}\x{9B4A}\x{9B4B}\x{9B4C}\x{9B4D}' .
+'\x{9B4E}\x{9B4F}\x{9B51}\x{9B52}\x{9B53}\x{9B54}\x{9B55}\x{9B56}\x{9B58}' .
+'\x{9B59}\x{9B5A}\x{9B5B}\x{9B5C}\x{9B5D}\x{9B5E}\x{9B5F}\x{9B60}\x{9B61}' .
+'\x{9B63}\x{9B64}\x{9B65}\x{9B66}\x{9B67}\x{9B68}\x{9B69}\x{9B6A}\x{9B6B}' .
+'\x{9B6C}\x{9B6D}\x{9B6E}\x{9B6F}\x{9B70}\x{9B71}\x{9B73}\x{9B74}\x{9B75}' .
+'\x{9B76}\x{9B77}\x{9B78}\x{9B79}\x{9B7A}\x{9B7B}\x{9B7C}\x{9B7D}\x{9B7E}' .
+'\x{9B7F}\x{9B80}\x{9B81}\x{9B82}\x{9B83}\x{9B84}\x{9B85}\x{9B86}\x{9B87}' .
+'\x{9B88}\x{9B8A}\x{9B8B}\x{9B8D}\x{9B8E}\x{9B8F}\x{9B90}\x{9B91}\x{9B92}' .
+'\x{9B93}\x{9B94}\x{9B95}\x{9B96}\x{9B97}\x{9B98}\x{9B9A}\x{9B9B}\x{9B9C}' .
+'\x{9B9D}\x{9B9E}\x{9B9F}\x{9BA0}\x{9BA1}\x{9BA2}\x{9BA3}\x{9BA4}\x{9BA5}' .
+'\x{9BA6}\x{9BA7}\x{9BA8}\x{9BA9}\x{9BAA}\x{9BAB}\x{9BAC}\x{9BAD}\x{9BAE}' .
+'\x{9BAF}\x{9BB0}\x{9BB1}\x{9BB2}\x{9BB3}\x{9BB4}\x{9BB5}\x{9BB6}\x{9BB7}' .
+'\x{9BB8}\x{9BB9}\x{9BBA}\x{9BBB}\x{9BBC}\x{9BBD}\x{9BBE}\x{9BBF}\x{9BC0}' .
+'\x{9BC1}\x{9BC3}\x{9BC4}\x{9BC5}\x{9BC6}\x{9BC7}\x{9BC8}\x{9BC9}\x{9BCA}' .
+'\x{9BCB}\x{9BCC}\x{9BCD}\x{9BCE}\x{9BCF}\x{9BD0}\x{9BD1}\x{9BD2}\x{9BD3}' .
+'\x{9BD4}\x{9BD5}\x{9BD6}\x{9BD7}\x{9BD8}\x{9BD9}\x{9BDA}\x{9BDB}\x{9BDC}' .
+'\x{9BDD}\x{9BDE}\x{9BDF}\x{9BE0}\x{9BE1}\x{9BE2}\x{9BE3}\x{9BE4}\x{9BE5}' .
+'\x{9BE6}\x{9BE7}\x{9BE8}\x{9BE9}\x{9BEA}\x{9BEB}\x{9BEC}\x{9BED}\x{9BEE}' .
+'\x{9BEF}\x{9BF0}\x{9BF1}\x{9BF2}\x{9BF3}\x{9BF4}\x{9BF5}\x{9BF7}\x{9BF8}' .
+'\x{9BF9}\x{9BFA}\x{9BFB}\x{9BFC}\x{9BFD}\x{9BFE}\x{9BFF}\x{9C02}\x{9C05}' .
+'\x{9C06}\x{9C07}\x{9C08}\x{9C09}\x{9C0A}\x{9C0B}\x{9C0C}\x{9C0D}\x{9C0E}' .
+'\x{9C0F}\x{9C10}\x{9C11}\x{9C12}\x{9C13}\x{9C14}\x{9C15}\x{9C16}\x{9C17}' .
+'\x{9C18}\x{9C19}\x{9C1A}\x{9C1B}\x{9C1C}\x{9C1D}\x{9C1E}\x{9C1F}\x{9C20}' .
+'\x{9C21}\x{9C22}\x{9C23}\x{9C24}\x{9C25}\x{9C26}\x{9C27}\x{9C28}\x{9C29}' .
+'\x{9C2A}\x{9C2B}\x{9C2C}\x{9C2D}\x{9C2F}\x{9C30}\x{9C31}\x{9C32}\x{9C33}' .
+'\x{9C34}\x{9C35}\x{9C36}\x{9C37}\x{9C38}\x{9C39}\x{9C3A}\x{9C3B}\x{9C3C}' .
+'\x{9C3D}\x{9C3E}\x{9C3F}\x{9C40}\x{9C41}\x{9C43}\x{9C44}\x{9C45}\x{9C46}' .
+'\x{9C47}\x{9C48}\x{9C49}\x{9C4A}\x{9C4B}\x{9C4C}\x{9C4D}\x{9C4E}\x{9C50}' .
+'\x{9C52}\x{9C53}\x{9C54}\x{9C55}\x{9C56}\x{9C57}\x{9C58}\x{9C59}\x{9C5A}' .
+'\x{9C5B}\x{9C5C}\x{9C5D}\x{9C5E}\x{9C5F}\x{9C60}\x{9C62}\x{9C63}\x{9C65}' .
+'\x{9C66}\x{9C67}\x{9C68}\x{9C69}\x{9C6A}\x{9C6B}\x{9C6C}\x{9C6D}\x{9C6E}' .
+'\x{9C6F}\x{9C70}\x{9C71}\x{9C72}\x{9C73}\x{9C74}\x{9C75}\x{9C77}\x{9C78}' .
+'\x{9C79}\x{9C7A}\x{9C7C}\x{9C7D}\x{9C7E}\x{9C7F}\x{9C80}\x{9C81}\x{9C82}' .
+'\x{9C83}\x{9C84}\x{9C85}\x{9C86}\x{9C87}\x{9C88}\x{9C89}\x{9C8A}\x{9C8B}' .
+'\x{9C8C}\x{9C8D}\x{9C8E}\x{9C8F}\x{9C90}\x{9C91}\x{9C92}\x{9C93}\x{9C94}' .
+'\x{9C95}\x{9C96}\x{9C97}\x{9C98}\x{9C99}\x{9C9A}\x{9C9B}\x{9C9C}\x{9C9D}' .
+'\x{9C9E}\x{9C9F}\x{9CA0}\x{9CA1}\x{9CA2}\x{9CA3}\x{9CA4}\x{9CA5}\x{9CA6}' .
+'\x{9CA7}\x{9CA8}\x{9CA9}\x{9CAA}\x{9CAB}\x{9CAC}\x{9CAD}\x{9CAE}\x{9CAF}' .
+'\x{9CB0}\x{9CB1}\x{9CB2}\x{9CB3}\x{9CB4}\x{9CB5}\x{9CB6}\x{9CB7}\x{9CB8}' .
+'\x{9CB9}\x{9CBA}\x{9CBB}\x{9CBC}\x{9CBD}\x{9CBE}\x{9CBF}\x{9CC0}\x{9CC1}' .
+'\x{9CC2}\x{9CC3}\x{9CC4}\x{9CC5}\x{9CC6}\x{9CC7}\x{9CC8}\x{9CC9}\x{9CCA}' .
+'\x{9CCB}\x{9CCC}\x{9CCD}\x{9CCE}\x{9CCF}\x{9CD0}\x{9CD1}\x{9CD2}\x{9CD3}' .
+'\x{9CD4}\x{9CD5}\x{9CD6}\x{9CD7}\x{9CD8}\x{9CD9}\x{9CDA}\x{9CDB}\x{9CDC}' .
+'\x{9CDD}\x{9CDE}\x{9CDF}\x{9CE0}\x{9CE1}\x{9CE2}\x{9CE3}\x{9CE4}\x{9CE5}' .
+'\x{9CE6}\x{9CE7}\x{9CE8}\x{9CE9}\x{9CEA}\x{9CEB}\x{9CEC}\x{9CED}\x{9CEE}' .
+'\x{9CEF}\x{9CF0}\x{9CF1}\x{9CF2}\x{9CF3}\x{9CF4}\x{9CF5}\x{9CF6}\x{9CF7}' .
+'\x{9CF8}\x{9CF9}\x{9CFA}\x{9CFB}\x{9CFC}\x{9CFD}\x{9CFE}\x{9CFF}\x{9D00}' .
+'\x{9D01}\x{9D02}\x{9D03}\x{9D04}\x{9D05}\x{9D06}\x{9D07}\x{9D08}\x{9D09}' .
+'\x{9D0A}\x{9D0B}\x{9D0F}\x{9D10}\x{9D12}\x{9D13}\x{9D14}\x{9D15}\x{9D16}' .
+'\x{9D17}\x{9D18}\x{9D19}\x{9D1A}\x{9D1B}\x{9D1C}\x{9D1D}\x{9D1E}\x{9D1F}' .
+'\x{9D20}\x{9D21}\x{9D22}\x{9D23}\x{9D24}\x{9D25}\x{9D26}\x{9D28}\x{9D29}' .
+'\x{9D2B}\x{9D2D}\x{9D2E}\x{9D2F}\x{9D30}\x{9D31}\x{9D32}\x{9D33}\x{9D34}' .
+'\x{9D36}\x{9D37}\x{9D38}\x{9D39}\x{9D3A}\x{9D3B}\x{9D3D}\x{9D3E}\x{9D3F}' .
+'\x{9D40}\x{9D41}\x{9D42}\x{9D43}\x{9D45}\x{9D46}\x{9D47}\x{9D48}\x{9D49}' .
+'\x{9D4A}\x{9D4B}\x{9D4C}\x{9D4D}\x{9D4E}\x{9D4F}\x{9D50}\x{9D51}\x{9D52}' .
+'\x{9D53}\x{9D54}\x{9D55}\x{9D56}\x{9D57}\x{9D58}\x{9D59}\x{9D5A}\x{9D5B}' .
+'\x{9D5C}\x{9D5D}\x{9D5E}\x{9D5F}\x{9D60}\x{9D61}\x{9D62}\x{9D63}\x{9D64}' .
+'\x{9D65}\x{9D66}\x{9D67}\x{9D68}\x{9D69}\x{9D6A}\x{9D6B}\x{9D6C}\x{9D6E}' .
+'\x{9D6F}\x{9D70}\x{9D71}\x{9D72}\x{9D73}\x{9D74}\x{9D75}\x{9D76}\x{9D77}' .
+'\x{9D78}\x{9D79}\x{9D7A}\x{9D7B}\x{9D7C}\x{9D7D}\x{9D7E}\x{9D7F}\x{9D80}' .
+'\x{9D81}\x{9D82}\x{9D83}\x{9D84}\x{9D85}\x{9D86}\x{9D87}\x{9D88}\x{9D89}' .
+'\x{9D8A}\x{9D8B}\x{9D8C}\x{9D8D}\x{9D8E}\x{9D90}\x{9D91}\x{9D92}\x{9D93}' .
+'\x{9D94}\x{9D96}\x{9D97}\x{9D98}\x{9D99}\x{9D9A}\x{9D9B}\x{9D9C}\x{9D9D}' .
+'\x{9D9E}\x{9D9F}\x{9DA0}\x{9DA1}\x{9DA2}\x{9DA3}\x{9DA4}\x{9DA5}\x{9DA6}' .
+'\x{9DA7}\x{9DA8}\x{9DA9}\x{9DAA}\x{9DAB}\x{9DAC}\x{9DAD}\x{9DAF}\x{9DB0}' .
+'\x{9DB1}\x{9DB2}\x{9DB3}\x{9DB4}\x{9DB5}\x{9DB6}\x{9DB7}\x{9DB8}\x{9DB9}' .
+'\x{9DBA}\x{9DBB}\x{9DBC}\x{9DBE}\x{9DBF}\x{9DC1}\x{9DC2}\x{9DC3}\x{9DC4}' .
+'\x{9DC5}\x{9DC7}\x{9DC8}\x{9DC9}\x{9DCA}\x{9DCB}\x{9DCC}\x{9DCD}\x{9DCE}' .
+'\x{9DCF}\x{9DD0}\x{9DD1}\x{9DD2}\x{9DD3}\x{9DD4}\x{9DD5}\x{9DD6}\x{9DD7}' .
+'\x{9DD8}\x{9DD9}\x{9DDA}\x{9DDB}\x{9DDC}\x{9DDD}\x{9DDE}\x{9DDF}\x{9DE0}' .
+'\x{9DE1}\x{9DE2}\x{9DE3}\x{9DE4}\x{9DE5}\x{9DE6}\x{9DE7}\x{9DE8}\x{9DE9}' .
+'\x{9DEB}\x{9DEC}\x{9DED}\x{9DEE}\x{9DEF}\x{9DF0}\x{9DF1}\x{9DF2}\x{9DF3}' .
+'\x{9DF4}\x{9DF5}\x{9DF6}\x{9DF7}\x{9DF8}\x{9DF9}\x{9DFA}\x{9DFB}\x{9DFD}' .
+'\x{9DFE}\x{9DFF}\x{9E00}\x{9E01}\x{9E02}\x{9E03}\x{9E04}\x{9E05}\x{9E06}' .
+'\x{9E07}\x{9E08}\x{9E09}\x{9E0A}\x{9E0B}\x{9E0C}\x{9E0D}\x{9E0F}\x{9E10}' .
+'\x{9E11}\x{9E12}\x{9E13}\x{9E14}\x{9E15}\x{9E17}\x{9E18}\x{9E19}\x{9E1A}' .
+'\x{9E1B}\x{9E1D}\x{9E1E}\x{9E1F}\x{9E20}\x{9E21}\x{9E22}\x{9E23}\x{9E24}' .
+'\x{9E25}\x{9E26}\x{9E27}\x{9E28}\x{9E29}\x{9E2A}\x{9E2B}\x{9E2C}\x{9E2D}' .
+'\x{9E2E}\x{9E2F}\x{9E30}\x{9E31}\x{9E32}\x{9E33}\x{9E34}\x{9E35}\x{9E36}' .
+'\x{9E37}\x{9E38}\x{9E39}\x{9E3A}\x{9E3B}\x{9E3C}\x{9E3D}\x{9E3E}\x{9E3F}' .
+'\x{9E40}\x{9E41}\x{9E42}\x{9E43}\x{9E44}\x{9E45}\x{9E46}\x{9E47}\x{9E48}' .
+'\x{9E49}\x{9E4A}\x{9E4B}\x{9E4C}\x{9E4D}\x{9E4E}\x{9E4F}\x{9E50}\x{9E51}' .
+'\x{9E52}\x{9E53}\x{9E54}\x{9E55}\x{9E56}\x{9E57}\x{9E58}\x{9E59}\x{9E5A}' .
+'\x{9E5B}\x{9E5C}\x{9E5D}\x{9E5E}\x{9E5F}\x{9E60}\x{9E61}\x{9E62}\x{9E63}' .
+'\x{9E64}\x{9E65}\x{9E66}\x{9E67}\x{9E68}\x{9E69}\x{9E6A}\x{9E6B}\x{9E6C}' .
+'\x{9E6D}\x{9E6E}\x{9E6F}\x{9E70}\x{9E71}\x{9E72}\x{9E73}\x{9E74}\x{9E75}' .
+'\x{9E76}\x{9E77}\x{9E79}\x{9E7A}\x{9E7C}\x{9E7D}\x{9E7E}\x{9E7F}\x{9E80}' .
+'\x{9E81}\x{9E82}\x{9E83}\x{9E84}\x{9E85}\x{9E86}\x{9E87}\x{9E88}\x{9E89}' .
+'\x{9E8A}\x{9E8B}\x{9E8C}\x{9E8D}\x{9E8E}\x{9E91}\x{9E92}\x{9E93}\x{9E94}' .
+'\x{9E96}\x{9E97}\x{9E99}\x{9E9A}\x{9E9B}\x{9E9C}\x{9E9D}\x{9E9F}\x{9EA0}' .
+'\x{9EA1}\x{9EA3}\x{9EA4}\x{9EA5}\x{9EA6}\x{9EA7}\x{9EA8}\x{9EA9}\x{9EAA}' .
+'\x{9EAD}\x{9EAE}\x{9EAF}\x{9EB0}\x{9EB2}\x{9EB3}\x{9EB4}\x{9EB5}\x{9EB6}' .
+'\x{9EB7}\x{9EB8}\x{9EBB}\x{9EBC}\x{9EBD}\x{9EBE}\x{9EBF}\x{9EC0}\x{9EC1}' .
+'\x{9EC2}\x{9EC3}\x{9EC4}\x{9EC5}\x{9EC6}\x{9EC7}\x{9EC8}\x{9EC9}\x{9ECA}' .
+'\x{9ECB}\x{9ECC}\x{9ECD}\x{9ECE}\x{9ECF}\x{9ED0}\x{9ED1}\x{9ED2}\x{9ED3}' .
+'\x{9ED4}\x{9ED5}\x{9ED6}\x{9ED7}\x{9ED8}\x{9ED9}\x{9EDA}\x{9EDB}\x{9EDC}' .
+'\x{9EDD}\x{9EDE}\x{9EDF}\x{9EE0}\x{9EE1}\x{9EE2}\x{9EE3}\x{9EE4}\x{9EE5}' .
+'\x{9EE6}\x{9EE7}\x{9EE8}\x{9EE9}\x{9EEA}\x{9EEB}\x{9EED}\x{9EEE}\x{9EEF}' .
+'\x{9EF0}\x{9EF2}\x{9EF3}\x{9EF4}\x{9EF5}\x{9EF6}\x{9EF7}\x{9EF8}\x{9EF9}' .
+'\x{9EFA}\x{9EFB}\x{9EFC}\x{9EFD}\x{9EFE}\x{9EFF}\x{9F00}\x{9F01}\x{9F02}' .
+'\x{9F04}\x{9F05}\x{9F06}\x{9F07}\x{9F08}\x{9F09}\x{9F0A}\x{9F0B}\x{9F0C}' .
+'\x{9F0D}\x{9F0E}\x{9F0F}\x{9F10}\x{9F12}\x{9F13}\x{9F15}\x{9F16}\x{9F17}' .
+'\x{9F18}\x{9F19}\x{9F1A}\x{9F1B}\x{9F1C}\x{9F1D}\x{9F1E}\x{9F1F}\x{9F20}' .
+'\x{9F22}\x{9F23}\x{9F24}\x{9F25}\x{9F27}\x{9F28}\x{9F29}\x{9F2A}\x{9F2B}' .
+'\x{9F2C}\x{9F2D}\x{9F2E}\x{9F2F}\x{9F30}\x{9F31}\x{9F32}\x{9F33}\x{9F34}' .
+'\x{9F35}\x{9F36}\x{9F37}\x{9F38}\x{9F39}\x{9F3A}\x{9F3B}\x{9F3C}\x{9F3D}' .
+'\x{9F3E}\x{9F3F}\x{9F40}\x{9F41}\x{9F42}\x{9F43}\x{9F44}\x{9F46}\x{9F47}' .
+'\x{9F48}\x{9F49}\x{9F4A}\x{9F4B}\x{9F4C}\x{9F4D}\x{9F4E}\x{9F4F}\x{9F50}' .
+'\x{9F51}\x{9F52}\x{9F54}\x{9F55}\x{9F56}\x{9F57}\x{9F58}\x{9F59}\x{9F5A}' .
+'\x{9F5B}\x{9F5C}\x{9F5D}\x{9F5E}\x{9F5F}\x{9F60}\x{9F61}\x{9F63}\x{9F64}' .
+'\x{9F65}\x{9F66}\x{9F67}\x{9F68}\x{9F69}\x{9F6A}\x{9F6B}\x{9F6C}\x{9F6E}' .
+'\x{9F6F}\x{9F70}\x{9F71}\x{9F72}\x{9F73}\x{9F74}\x{9F75}\x{9F76}\x{9F77}' .
+'\x{9F78}\x{9F79}\x{9F7A}\x{9F7B}\x{9F7C}\x{9F7D}\x{9F7E}\x{9F7F}\x{9F80}' .
+'\x{9F81}\x{9F82}\x{9F83}\x{9F84}\x{9F85}\x{9F86}\x{9F87}\x{9F88}\x{9F89}' .
+'\x{9F8A}\x{9F8B}\x{9F8C}\x{9F8D}\x{9F8E}\x{9F8F}\x{9F90}\x{9F91}\x{9F92}' .
+'\x{9F93}\x{9F94}\x{9F95}\x{9F96}\x{9F97}\x{9F98}\x{9F99}\x{9F9A}\x{9F9B}' .
+'\x{9F9C}\x{9F9D}\x{9F9E}\x{9F9F}\x{9FA0}\x{9FA2}\x{9FA4}\x{9FA5}]{1,20}$/iu');
Index: trunk/library/Zend/Validate/Hostname/Biz.php
===================================================================
--- trunk/library/Zend/Validate/Hostname/Biz.php	(revision 5146)
+++ trunk/library/Zend/Validate/Hostname/Biz.php	(revision 5146)
@@ -0,0 +1,2919 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Biz.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Ressource file for biz idn validation
+ *
+ * @category   Zend
+ * @package    Zend_Validate
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+return array(
+    1  => '/^[\x{002d}0-9a-zÃ€Ã¥ÃŠÃ©Ã¶ÃžÃŒ]{1,63}$/iu',
+    2  => '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã±Ã³ÃºÃŒ]{1,63}$/iu',
+    3  => '/^[\x{002d}0-9a-zÃ¡Ã©Ã­Ã³Ã¶ÃºÃŒÅÅ±]{1,63}$/iu',
+    4  => '/^[\x{002d}0-9a-zÃ¡ÃŠÃ©Ã­Ã°Ã³Ã¶ÃºÃœÃŸ]{1,63}$/iu',
+    5  => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
+    6  => '/^[\x{002d}0-9a-zÄ
+ÄÄÄÄ¯Å¡Å«Å³ÅŸ]{1,63}$/iu',
+    7  => '/^[\x{002d}0-9a-zÄÄÄÄ£Ä«Ä·ÄŒÅÅÅÅ¡Å«ÅŸ]{1,63}$/iu',
+    8  => '/^[\x{002d}0-9a-zÃ Ã¡Ã€-Ã©ÃªÃ±-ÃŽÃ¶ÃžÃŒÄÄÅÅÅ¡Å§ÅŸ]{1,63}$/iu',
+    9  => '/^[\x{002d}0-9a-zÃ³Ä
+ÄÄÅÅÅÅºÅŒ]{1,63}$/iu',
+    10 => '/^[\x{002d}0-9a-zÃ¡Ã Ã¢Ã£Ã§Ã©ÃªÃ­Ã³ÃŽÃµÃº]{1,63}$/iu',
+    11 => '/^[\x{002d}0-9a-z\x{3005}-\x{3007}\x{3041}-\x{3093}\x{309D}\x{309E}\x{30A1}-\x{30F6}\x{30FC}' .
+'\x{30FD}\x{30FE}\x{4E00}\x{4E01}\x{4E03}\x{4E07}\x{4E08}\x{4E09}\x{4E0A}' .
+'\x{4E0B}\x{4E0D}\x{4E0E}\x{4E10}\x{4E11}\x{4E14}\x{4E15}\x{4E16}\x{4E17}' .
+'\x{4E18}\x{4E19}\x{4E1E}\x{4E21}\x{4E26}\x{4E2A}\x{4E2D}\x{4E31}\x{4E32}' .
+'\x{4E36}\x{4E38}\x{4E39}\x{4E3B}\x{4E3C}\x{4E3F}\x{4E42}\x{4E43}\x{4E45}' .
+'\x{4E4B}\x{4E4D}\x{4E4E}\x{4E4F}\x{4E55}\x{4E56}\x{4E57}\x{4E58}\x{4E59}' .
+'\x{4E5D}\x{4E5E}\x{4E5F}\x{4E62}\x{4E71}\x{4E73}\x{4E7E}\x{4E80}\x{4E82}' .
+'\x{4E85}\x{4E86}\x{4E88}\x{4E89}\x{4E8A}\x{4E8B}\x{4E8C}\x{4E8E}\x{4E91}' .
+'\x{4E92}\x{4E94}\x{4E95}\x{4E98}\x{4E99}\x{4E9B}\x{4E9C}\x{4E9E}\x{4E9F}' .
+'\x{4EA0}\x{4EA1}\x{4EA2}\x{4EA4}\x{4EA5}\x{4EA6}\x{4EA8}\x{4EAB}\x{4EAC}' .
+'\x{4EAD}\x{4EAE}\x{4EB0}\x{4EB3}\x{4EB6}\x{4EBA}\x{4EC0}\x{4EC1}\x{4EC2}' .
+'\x{4EC4}\x{4EC6}\x{4EC7}\x{4ECA}\x{4ECB}\x{4ECD}\x{4ECE}\x{4ECF}\x{4ED4}' .
+'\x{4ED5}\x{4ED6}\x{4ED7}\x{4ED8}\x{4ED9}\x{4EDD}\x{4EDE}\x{4EDF}\x{4EE3}' .
+'\x{4EE4}\x{4EE5}\x{4EED}\x{4EEE}\x{4EF0}\x{4EF2}\x{4EF6}\x{4EF7}\x{4EFB}' .
+'\x{4F01}\x{4F09}\x{4F0A}\x{4F0D}\x{4F0E}\x{4F0F}\x{4F10}\x{4F11}\x{4F1A}' .
+'\x{4F1C}\x{4F1D}\x{4F2F}\x{4F30}\x{4F34}\x{4F36}\x{4F38}\x{4F3A}\x{4F3C}' .
+'\x{4F3D}\x{4F43}\x{4F46}\x{4F47}\x{4F4D}\x{4F4E}\x{4F4F}\x{4F50}\x{4F51}' .
+'\x{4F53}\x{4F55}\x{4F57}\x{4F59}\x{4F5A}\x{4F5B}\x{4F5C}\x{4F5D}\x{4F5E}' .
+'\x{4F69}\x{4F6F}\x{4F70}\x{4F73}\x{4F75}\x{4F76}\x{4F7B}\x{4F7C}\x{4F7F}' .
+'\x{4F83}\x{4F86}\x{4F88}\x{4F8B}\x{4F8D}\x{4F8F}\x{4F91}\x{4F96}\x{4F98}' .
+'\x{4F9B}\x{4F9D}\x{4FA0}\x{4FA1}\x{4FAB}\x{4FAD}\x{4FAE}\x{4FAF}\x{4FB5}' .
+'\x{4FB6}\x{4FBF}\x{4FC2}\x{4FC3}\x{4FC4}\x{4FCA}\x{4FCE}\x{4FD0}\x{4FD1}' .
+'\x{4FD4}\x{4FD7}\x{4FD8}\x{4FDA}\x{4FDB}\x{4FDD}\x{4FDF}\x{4FE1}\x{4FE3}' .
+'\x{4FE4}\x{4FE5}\x{4FEE}\x{4FEF}\x{4FF3}\x{4FF5}\x{4FF6}\x{4FF8}\x{4FFA}' .
+'\x{4FFE}\x{5005}\x{5006}\x{5009}\x{500B}\x{500D}\x{500F}\x{5011}\x{5012}' .
+'\x{5014}\x{5016}\x{5019}\x{501A}\x{501F}\x{5021}\x{5023}\x{5024}\x{5025}' .
+'\x{5026}\x{5028}\x{5029}\x{502A}\x{502B}\x{502C}\x{502D}\x{5036}\x{5039}' .
+'\x{5043}\x{5047}\x{5048}\x{5049}\x{504F}\x{5050}\x{5055}\x{5056}\x{505A}' .
+'\x{505C}\x{5065}\x{506C}\x{5072}\x{5074}\x{5075}\x{5076}\x{5078}\x{507D}' .
+'\x{5080}\x{5085}\x{508D}\x{5091}\x{5098}\x{5099}\x{509A}\x{50AC}\x{50AD}' .
+'\x{50B2}\x{50B3}\x{50B4}\x{50B5}\x{50B7}\x{50BE}\x{50C2}\x{50C5}\x{50C9}' .
+'\x{50CA}\x{50CD}\x{50CF}\x{50D1}\x{50D5}\x{50D6}\x{50DA}\x{50DE}\x{50E3}' .
+'\x{50E5}\x{50E7}\x{50ED}\x{50EE}\x{50F5}\x{50F9}\x{50FB}\x{5100}\x{5101}' .
+'\x{5102}\x{5104}\x{5109}\x{5112}\x{5114}\x{5115}\x{5116}\x{5118}\x{511A}' .
+'\x{511F}\x{5121}\x{512A}\x{5132}\x{5137}\x{513A}\x{513B}\x{513C}\x{513F}' .
+'\x{5140}\x{5141}\x{5143}\x{5144}\x{5145}\x{5146}\x{5147}\x{5148}\x{5149}' .
+'\x{514B}\x{514C}\x{514D}\x{514E}\x{5150}\x{5152}\x{5154}\x{515A}\x{515C}' .
+'\x{5162}\x{5165}\x{5168}\x{5169}\x{516A}\x{516B}\x{516C}\x{516D}\x{516E}' .
+'\x{5171}\x{5175}\x{5176}\x{5177}\x{5178}\x{517C}\x{5180}\x{5182}\x{5185}' .
+'\x{5186}\x{5189}\x{518A}\x{518C}\x{518D}\x{518F}\x{5190}\x{5191}\x{5192}' .
+'\x{5193}\x{5195}\x{5196}\x{5197}\x{5199}\x{51A0}\x{51A2}\x{51A4}\x{51A5}' .
+'\x{51A6}\x{51A8}\x{51A9}\x{51AA}\x{51AB}\x{51AC}\x{51B0}\x{51B1}\x{51B2}' .
+'\x{51B3}\x{51B4}\x{51B5}\x{51B6}\x{51B7}\x{51BD}\x{51C4}\x{51C5}\x{51C6}' .
+'\x{51C9}\x{51CB}\x{51CC}\x{51CD}\x{51D6}\x{51DB}\x{51DC}\x{51DD}\x{51E0}' .
+'\x{51E1}\x{51E6}\x{51E7}\x{51E9}\x{51EA}\x{51ED}\x{51F0}\x{51F1}\x{51F5}' .
+'\x{51F6}\x{51F8}\x{51F9}\x{51FA}\x{51FD}\x{51FE}\x{5200}\x{5203}\x{5204}' .
+'\x{5206}\x{5207}\x{5208}\x{520A}\x{520B}\x{520E}\x{5211}\x{5214}\x{5217}' .
+'\x{521D}\x{5224}\x{5225}\x{5227}\x{5229}\x{522A}\x{522E}\x{5230}\x{5233}' .
+'\x{5236}\x{5237}\x{5238}\x{5239}\x{523A}\x{523B}\x{5243}\x{5244}\x{5247}' .
+'\x{524A}\x{524B}\x{524C}\x{524D}\x{524F}\x{5254}\x{5256}\x{525B}\x{525E}' .
+'\x{5263}\x{5264}\x{5265}\x{5269}\x{526A}\x{526F}\x{5270}\x{5271}\x{5272}' .
+'\x{5273}\x{5274}\x{5275}\x{527D}\x{527F}\x{5283}\x{5287}\x{5288}\x{5289}' .
+'\x{528D}\x{5291}\x{5292}\x{5294}\x{529B}\x{529F}\x{52A0}\x{52A3}\x{52A9}' .
+'\x{52AA}\x{52AB}\x{52AC}\x{52AD}\x{52B1}\x{52B4}\x{52B5}\x{52B9}\x{52BC}' .
+'\x{52BE}\x{52C1}\x{52C3}\x{52C5}\x{52C7}\x{52C9}\x{52CD}\x{52D2}\x{52D5}' .
+'\x{52D7}\x{52D8}\x{52D9}\x{52DD}\x{52DE}\x{52DF}\x{52E0}\x{52E2}\x{52E3}' .
+'\x{52E4}\x{52E6}\x{52E7}\x{52F2}\x{52F3}\x{52F5}\x{52F8}\x{52F9}\x{52FA}' .
+'\x{52FE}\x{52FF}\x{5301}\x{5302}\x{5305}\x{5306}\x{5308}\x{530D}\x{530F}' .
+'\x{5310}\x{5315}\x{5316}\x{5317}\x{5319}\x{531A}\x{531D}\x{5320}\x{5321}' .
+'\x{5323}\x{532A}\x{532F}\x{5331}\x{5333}\x{5338}\x{5339}\x{533A}\x{533B}' .
+'\x{533F}\x{5340}\x{5341}\x{5343}\x{5345}\x{5346}\x{5347}\x{5348}\x{5349}' .
+'\x{534A}\x{534D}\x{5351}\x{5352}\x{5353}\x{5354}\x{5357}\x{5358}\x{535A}' .
+'\x{535C}\x{535E}\x{5360}\x{5366}\x{5369}\x{536E}\x{536F}\x{5370}\x{5371}' .
+'\x{5373}\x{5374}\x{5375}\x{5377}\x{5378}\x{537B}\x{537F}\x{5382}\x{5384}' .
+'\x{5396}\x{5398}\x{539A}\x{539F}\x{53A0}\x{53A5}\x{53A6}\x{53A8}\x{53A9}' .
+'\x{53AD}\x{53AE}\x{53B0}\x{53B3}\x{53B6}\x{53BB}\x{53C2}\x{53C3}\x{53C8}' .
+'\x{53C9}\x{53CA}\x{53CB}\x{53CC}\x{53CD}\x{53CE}\x{53D4}\x{53D6}\x{53D7}' .
+'\x{53D9}\x{53DB}\x{53DF}\x{53E1}\x{53E2}\x{53E3}\x{53E4}\x{53E5}\x{53E8}' .
+'\x{53E9}\x{53EA}\x{53EB}\x{53EC}\x{53ED}\x{53EE}\x{53EF}\x{53F0}\x{53F1}' .
+'\x{53F2}\x{53F3}\x{53F6}\x{53F7}\x{53F8}\x{53FA}\x{5401}\x{5403}\x{5404}' .
+'\x{5408}\x{5409}\x{540A}\x{540B}\x{540C}\x{540D}\x{540E}\x{540F}\x{5410}' .
+'\x{5411}\x{541B}\x{541D}\x{541F}\x{5420}\x{5426}\x{5429}\x{542B}\x{542C}' .
+'\x{542D}\x{542E}\x{5436}\x{5438}\x{5439}\x{543B}\x{543C}\x{543D}\x{543E}' .
+'\x{5440}\x{5442}\x{5446}\x{5448}\x{5449}\x{544A}\x{544E}\x{5451}\x{545F}' .
+'\x{5468}\x{546A}\x{5470}\x{5471}\x{5473}\x{5475}\x{5476}\x{5477}\x{547B}' .
+'\x{547C}\x{547D}\x{5480}\x{5484}\x{5486}\x{548B}\x{548C}\x{548E}\x{548F}' .
+'\x{5490}\x{5492}\x{54A2}\x{54A4}\x{54A5}\x{54A8}\x{54AB}\x{54AC}\x{54AF}' .
+'\x{54B2}\x{54B3}\x{54B8}\x{54BC}\x{54BD}\x{54BE}\x{54C0}\x{54C1}\x{54C2}' .
+'\x{54C4}\x{54C7}\x{54C8}\x{54C9}\x{54D8}\x{54E1}\x{54E2}\x{54E5}\x{54E6}' .
+'\x{54E8}\x{54E9}\x{54ED}\x{54EE}\x{54F2}\x{54FA}\x{54FD}\x{5504}\x{5506}' .
+'\x{5507}\x{550F}\x{5510}\x{5514}\x{5516}\x{552E}\x{552F}\x{5531}\x{5533}' .
+'\x{5538}\x{5539}\x{553E}\x{5540}\x{5544}\x{5545}\x{5546}\x{554C}\x{554F}' .
+'\x{5553}\x{5556}\x{5557}\x{555C}\x{555D}\x{5563}\x{557B}\x{557C}\x{557E}' .
+'\x{5580}\x{5583}\x{5584}\x{5587}\x{5589}\x{558A}\x{558B}\x{5598}\x{5599}' .
+'\x{559A}\x{559C}\x{559D}\x{559E}\x{559F}\x{55A7}\x{55A8}\x{55A9}\x{55AA}' .
+'\x{55AB}\x{55AC}\x{55AE}\x{55B0}\x{55B6}\x{55C4}\x{55C5}\x{55C7}\x{55D4}' .
+'\x{55DA}\x{55DC}\x{55DF}\x{55E3}\x{55E4}\x{55F7}\x{55F9}\x{55FD}\x{55FE}' .
+'\x{5606}\x{5609}\x{5614}\x{5616}\x{5617}\x{5618}\x{561B}\x{5629}\x{562F}' .
+'\x{5631}\x{5632}\x{5634}\x{5636}\x{5638}\x{5642}\x{564C}\x{564E}\x{5650}' .
+'\x{565B}\x{5664}\x{5668}\x{566A}\x{566B}\x{566C}\x{5674}\x{5678}\x{567A}' .
+'\x{5680}\x{5686}\x{5687}\x{568A}\x{568F}\x{5694}\x{56A0}\x{56A2}\x{56A5}' .
+'\x{56AE}\x{56B4}\x{56B6}\x{56BC}\x{56C0}\x{56C1}\x{56C2}\x{56C3}\x{56C8}' .
+'\x{56CE}\x{56D1}\x{56D3}\x{56D7}\x{56D8}\x{56DA}\x{56DB}\x{56DE}\x{56E0}' .
+'\x{56E3}\x{56EE}\x{56F0}\x{56F2}\x{56F3}\x{56F9}\x{56FA}\x{56FD}\x{56FF}' .
+'\x{5700}\x{5703}\x{5704}\x{5708}\x{5709}\x{570B}\x{570D}\x{570F}\x{5712}' .
+'\x{5713}\x{5716}\x{5718}\x{571C}\x{571F}\x{5726}\x{5727}\x{5728}\x{572D}' .
+'\x{5730}\x{5737}\x{5738}\x{573B}\x{5740}\x{5742}\x{5747}\x{574A}\x{574E}' .
+'\x{574F}\x{5750}\x{5751}\x{5761}\x{5764}\x{5766}\x{5769}\x{576A}\x{577F}' .
+'\x{5782}\x{5788}\x{5789}\x{578B}\x{5793}\x{57A0}\x{57A2}\x{57A3}\x{57A4}' .
+'\x{57AA}\x{57B0}\x{57B3}\x{57C0}\x{57C3}\x{57C6}\x{57CB}\x{57CE}\x{57D2}' .
+'\x{57D3}\x{57D4}\x{57D6}\x{57DC}\x{57DF}\x{57E0}\x{57E3}\x{57F4}\x{57F7}' .
+'\x{57F9}\x{57FA}\x{57FC}\x{5800}\x{5802}\x{5805}\x{5806}\x{580A}\x{580B}' .
+'\x{5815}\x{5819}\x{581D}\x{5821}\x{5824}\x{582A}\x{582F}\x{5830}\x{5831}' .
+'\x{5834}\x{5835}\x{583A}\x{583D}\x{5840}\x{5841}\x{584A}\x{584B}\x{5851}' .
+'\x{5852}\x{5854}\x{5857}\x{5858}\x{5859}\x{585A}\x{585E}\x{5862}\x{5869}' .
+'\x{586B}\x{5870}\x{5872}\x{5875}\x{5879}\x{587E}\x{5883}\x{5885}\x{5893}' .
+'\x{5897}\x{589C}\x{589F}\x{58A8}\x{58AB}\x{58AE}\x{58B3}\x{58B8}\x{58B9}' .
+'\x{58BA}\x{58BB}\x{58BE}\x{58C1}\x{58C5}\x{58C7}\x{58CA}\x{58CC}\x{58D1}' .
+'\x{58D3}\x{58D5}\x{58D7}\x{58D8}\x{58D9}\x{58DC}\x{58DE}\x{58DF}\x{58E4}' .
+'\x{58E5}\x{58EB}\x{58EC}\x{58EE}\x{58EF}\x{58F0}\x{58F1}\x{58F2}\x{58F7}' .
+'\x{58F9}\x{58FA}\x{58FB}\x{58FC}\x{58FD}\x{5902}\x{5909}\x{590A}\x{590F}' .
+'\x{5910}\x{5915}\x{5916}\x{5918}\x{5919}\x{591A}\x{591B}\x{591C}\x{5922}' .
+'\x{5925}\x{5927}\x{5929}\x{592A}\x{592B}\x{592C}\x{592D}\x{592E}\x{5931}' .
+'\x{5932}\x{5937}\x{5938}\x{593E}\x{5944}\x{5947}\x{5948}\x{5949}\x{594E}' .
+'\x{594F}\x{5950}\x{5951}\x{5954}\x{5955}\x{5957}\x{5958}\x{595A}\x{5960}' .
+'\x{5962}\x{5965}\x{5967}\x{5968}\x{5969}\x{596A}\x{596C}\x{596E}\x{5973}' .
+'\x{5974}\x{5978}\x{597D}\x{5981}\x{5982}\x{5983}\x{5984}\x{598A}\x{598D}' .
+'\x{5993}\x{5996}\x{5999}\x{599B}\x{599D}\x{59A3}\x{59A5}\x{59A8}\x{59AC}' .
+'\x{59B2}\x{59B9}\x{59BB}\x{59BE}\x{59C6}\x{59C9}\x{59CB}\x{59D0}\x{59D1}' .
+'\x{59D3}\x{59D4}\x{59D9}\x{59DA}\x{59DC}\x{59E5}\x{59E6}\x{59E8}\x{59EA}' .
+'\x{59EB}\x{59F6}\x{59FB}\x{59FF}\x{5A01}\x{5A03}\x{5A09}\x{5A11}\x{5A18}' .
+'\x{5A1A}\x{5A1C}\x{5A1F}\x{5A20}\x{5A25}\x{5A29}\x{5A2F}\x{5A35}\x{5A36}' .
+'\x{5A3C}\x{5A40}\x{5A41}\x{5A46}\x{5A49}\x{5A5A}\x{5A62}\x{5A66}\x{5A6A}' .
+'\x{5A6C}\x{5A7F}\x{5A92}\x{5A9A}\x{5A9B}\x{5ABC}\x{5ABD}\x{5ABE}\x{5AC1}' .
+'\x{5AC2}\x{5AC9}\x{5ACB}\x{5ACC}\x{5AD0}\x{5AD6}\x{5AD7}\x{5AE1}\x{5AE3}' .
+'\x{5AE6}\x{5AE9}\x{5AFA}\x{5AFB}\x{5B09}\x{5B0B}\x{5B0C}\x{5B16}\x{5B22}' .
+'\x{5B2A}\x{5B2C}\x{5B30}\x{5B32}\x{5B36}\x{5B3E}\x{5B40}\x{5B43}\x{5B45}' .
+'\x{5B50}\x{5B51}\x{5B54}\x{5B55}\x{5B57}\x{5B58}\x{5B5A}\x{5B5B}\x{5B5C}' .
+'\x{5B5D}\x{5B5F}\x{5B63}\x{5B64}\x{5B65}\x{5B66}\x{5B69}\x{5B6B}\x{5B70}' .
+'\x{5B71}\x{5B73}\x{5B75}\x{5B78}\x{5B7A}\x{5B80}\x{5B83}\x{5B85}\x{5B87}' .
+'\x{5B88}\x{5B89}\x{5B8B}\x{5B8C}\x{5B8D}\x{5B8F}\x{5B95}\x{5B97}\x{5B98}' .
+'\x{5B99}\x{5B9A}\x{5B9B}\x{5B9C}\x{5B9D}\x{5B9F}\x{5BA2}\x{5BA3}\x{5BA4}' .
+'\x{5BA5}\x{5BA6}\x{5BAE}\x{5BB0}\x{5BB3}\x{5BB4}\x{5BB5}\x{5BB6}\x{5BB8}' .
+'\x{5BB9}\x{5BBF}\x{5BC2}\x{5BC3}\x{5BC4}\x{5BC5}\x{5BC6}\x{5BC7}\x{5BC9}' .
+'\x{5BCC}\x{5BD0}\x{5BD2}\x{5BD3}\x{5BD4}\x{5BDB}\x{5BDD}\x{5BDE}\x{5BDF}' .
+'\x{5BE1}\x{5BE2}\x{5BE4}\x{5BE5}\x{5BE6}\x{5BE7}\x{5BE8}\x{5BE9}\x{5BEB}' .
+'\x{5BEE}\x{5BF0}\x{5BF3}\x{5BF5}\x{5BF6}\x{5BF8}\x{5BFA}\x{5BFE}\x{5BFF}' .
+'\x{5C01}\x{5C02}\x{5C04}\x{5C05}\x{5C06}\x{5C07}\x{5C08}\x{5C09}\x{5C0A}' .
+'\x{5C0B}\x{5C0D}\x{5C0E}\x{5C0F}\x{5C11}\x{5C13}\x{5C16}\x{5C1A}\x{5C20}' .
+'\x{5C22}\x{5C24}\x{5C28}\x{5C2D}\x{5C31}\x{5C38}\x{5C39}\x{5C3A}\x{5C3B}' .
+'\x{5C3C}\x{5C3D}\x{5C3E}\x{5C3F}\x{5C40}\x{5C41}\x{5C45}\x{5C46}\x{5C48}' .
+'\x{5C4A}\x{5C4B}\x{5C4D}\x{5C4E}\x{5C4F}\x{5C50}\x{5C51}\x{5C53}\x{5C55}' .
+'\x{5C5E}\x{5C60}\x{5C61}\x{5C64}\x{5C65}\x{5C6C}\x{5C6E}\x{5C6F}\x{5C71}' .
+'\x{5C76}\x{5C79}\x{5C8C}\x{5C90}\x{5C91}\x{5C94}\x{5CA1}\x{5CA8}\x{5CA9}' .
+'\x{5CAB}\x{5CAC}\x{5CB1}\x{5CB3}\x{5CB6}\x{5CB7}\x{5CB8}\x{5CBB}\x{5CBC}' .
+'\x{5CBE}\x{5CC5}\x{5CC7}\x{5CD9}\x{5CE0}\x{5CE1}\x{5CE8}\x{5CE9}\x{5CEA}' .
+'\x{5CED}\x{5CEF}\x{5CF0}\x{5CF6}\x{5CFA}\x{5CFB}\x{5CFD}\x{5D07}\x{5D0B}' .
+'\x{5D0E}\x{5D11}\x{5D14}\x{5D15}\x{5D16}\x{5D17}\x{5D18}\x{5D19}\x{5D1A}' .
+'\x{5D1B}\x{5D1F}\x{5D22}\x{5D29}\x{5D4B}\x{5D4C}\x{5D4E}\x{5D50}\x{5D52}' .
+'\x{5D5C}\x{5D69}\x{5D6C}\x{5D6F}\x{5D73}\x{5D76}\x{5D82}\x{5D84}\x{5D87}' .
+'\x{5D8B}\x{5D8C}\x{5D90}\x{5D9D}\x{5DA2}\x{5DAC}\x{5DAE}\x{5DB7}\x{5DBA}' .
+'\x{5DBC}\x{5DBD}\x{5DC9}\x{5DCC}\x{5DCD}\x{5DD2}\x{5DD3}\x{5DD6}\x{5DDB}' .
+'\x{5DDD}\x{5DDE}\x{5DE1}\x{5DE3}\x{5DE5}\x{5DE6}\x{5DE7}\x{5DE8}\x{5DEB}' .
+'\x{5DEE}\x{5DF1}\x{5DF2}\x{5DF3}\x{5DF4}\x{5DF5}\x{5DF7}\x{5DFB}\x{5DFD}' .
+'\x{5DFE}\x{5E02}\x{5E03}\x{5E06}\x{5E0B}\x{5E0C}\x{5E11}\x{5E16}\x{5E19}' .
+'\x{5E1A}\x{5E1B}\x{5E1D}\x{5E25}\x{5E2B}\x{5E2D}\x{5E2F}\x{5E30}\x{5E33}' .
+'\x{5E36}\x{5E37}\x{5E38}\x{5E3D}\x{5E40}\x{5E43}\x{5E44}\x{5E45}\x{5E47}' .
+'\x{5E4C}\x{5E4E}\x{5E54}\x{5E55}\x{5E57}\x{5E5F}\x{5E61}\x{5E62}\x{5E63}' .
+'\x{5E64}\x{5E72}\x{5E73}\x{5E74}\x{5E75}\x{5E76}\x{5E78}\x{5E79}\x{5E7A}' .
+'\x{5E7B}\x{5E7C}\x{5E7D}\x{5E7E}\x{5E7F}\x{5E81}\x{5E83}\x{5E84}\x{5E87}' .
+'\x{5E8A}\x{5E8F}\x{5E95}\x{5E96}\x{5E97}\x{5E9A}\x{5E9C}\x{5EA0}\x{5EA6}' .
+'\x{5EA7}\x{5EAB}\x{5EAD}\x{5EB5}\x{5EB6}\x{5EB7}\x{5EB8}\x{5EC1}\x{5EC2}' .
+'\x{5EC3}\x{5EC8}\x{5EC9}\x{5ECA}\x{5ECF}\x{5ED0}\x{5ED3}\x{5ED6}\x{5EDA}' .
+'\x{5EDB}\x{5EDD}\x{5EDF}\x{5EE0}\x{5EE1}\x{5EE2}\x{5EE3}\x{5EE8}\x{5EE9}' .
+'\x{5EEC}\x{5EF0}\x{5EF1}\x{5EF3}\x{5EF4}\x{5EF6}\x{5EF7}\x{5EF8}\x{5EFA}' .
+'\x{5EFB}\x{5EFC}\x{5EFE}\x{5EFF}\x{5F01}\x{5F03}\x{5F04}\x{5F09}\x{5F0A}' .
+'\x{5F0B}\x{5F0C}\x{5F0D}\x{5F0F}\x{5F10}\x{5F11}\x{5F13}\x{5F14}\x{5F15}' .
+'\x{5F16}\x{5F17}\x{5F18}\x{5F1B}\x{5F1F}\x{5F25}\x{5F26}\x{5F27}\x{5F29}' .
+'\x{5F2D}\x{5F2F}\x{5F31}\x{5F35}\x{5F37}\x{5F38}\x{5F3C}\x{5F3E}\x{5F41}' .
+'\x{5F48}\x{5F4A}\x{5F4C}\x{5F4E}\x{5F51}\x{5F53}\x{5F56}\x{5F57}\x{5F59}' .
+'\x{5F5C}\x{5F5D}\x{5F61}\x{5F62}\x{5F66}\x{5F69}\x{5F6A}\x{5F6B}\x{5F6C}' .
+'\x{5F6D}\x{5F70}\x{5F71}\x{5F73}\x{5F77}\x{5F79}\x{5F7C}\x{5F7F}\x{5F80}' .
+'\x{5F81}\x{5F82}\x{5F83}\x{5F84}\x{5F85}\x{5F87}\x{5F88}\x{5F8A}\x{5F8B}' .
+'\x{5F8C}\x{5F90}\x{5F91}\x{5F92}\x{5F93}\x{5F97}\x{5F98}\x{5F99}\x{5F9E}' .
+'\x{5FA0}\x{5FA1}\x{5FA8}\x{5FA9}\x{5FAA}\x{5FAD}\x{5FAE}\x{5FB3}\x{5FB4}' .
+'\x{5FB9}\x{5FBC}\x{5FBD}\x{5FC3}\x{5FC5}\x{5FCC}\x{5FCD}\x{5FD6}\x{5FD7}' .
+'\x{5FD8}\x{5FD9}\x{5FDC}\x{5FDD}\x{5FE0}\x{5FE4}\x{5FEB}\x{5FF0}\x{5FF1}' .
+'\x{5FF5}\x{5FF8}\x{5FFB}\x{5FFD}\x{5FFF}\x{600E}\x{600F}\x{6010}\x{6012}' .
+'\x{6015}\x{6016}\x{6019}\x{601B}\x{601C}\x{601D}\x{6020}\x{6021}\x{6025}' .
+'\x{6026}\x{6027}\x{6028}\x{6029}\x{602A}\x{602B}\x{602F}\x{6031}\x{603A}' .
+'\x{6041}\x{6042}\x{6043}\x{6046}\x{604A}\x{604B}\x{604D}\x{6050}\x{6052}' .
+'\x{6055}\x{6059}\x{605A}\x{605F}\x{6060}\x{6062}\x{6063}\x{6064}\x{6065}' .
+'\x{6068}\x{6069}\x{606A}\x{606B}\x{606C}\x{606D}\x{606F}\x{6070}\x{6075}' .
+'\x{6077}\x{6081}\x{6083}\x{6084}\x{6089}\x{608B}\x{608C}\x{608D}\x{6092}' .
+'\x{6094}\x{6096}\x{6097}\x{609A}\x{609B}\x{609F}\x{60A0}\x{60A3}\x{60A6}' .
+'\x{60A7}\x{60A9}\x{60AA}\x{60B2}\x{60B3}\x{60B4}\x{60B5}\x{60B6}\x{60B8}' .
+'\x{60BC}\x{60BD}\x{60C5}\x{60C6}\x{60C7}\x{60D1}\x{60D3}\x{60D8}\x{60DA}' .
+'\x{60DC}\x{60DF}\x{60E0}\x{60E1}\x{60E3}\x{60E7}\x{60E8}\x{60F0}\x{60F1}' .
+'\x{60F3}\x{60F4}\x{60F6}\x{60F7}\x{60F9}\x{60FA}\x{60FB}\x{6100}\x{6101}' .
+'\x{6103}\x{6106}\x{6108}\x{6109}\x{610D}\x{610E}\x{610F}\x{6115}\x{611A}' .
+'\x{611B}\x{611F}\x{6121}\x{6127}\x{6128}\x{612C}\x{6134}\x{613C}\x{613D}' .
+'\x{613E}\x{613F}\x{6142}\x{6144}\x{6147}\x{6148}\x{614A}\x{614B}\x{614C}' .
+'\x{614D}\x{614E}\x{6153}\x{6155}\x{6158}\x{6159}\x{615A}\x{615D}\x{615F}' .
+'\x{6162}\x{6163}\x{6165}\x{6167}\x{6168}\x{616B}\x{616E}\x{616F}\x{6170}' .
+'\x{6171}\x{6173}\x{6174}\x{6175}\x{6176}\x{6177}\x{617E}\x{6182}\x{6187}' .
+'\x{618A}\x{618E}\x{6190}\x{6191}\x{6194}\x{6196}\x{6199}\x{619A}\x{61A4}' .
+'\x{61A7}\x{61A9}\x{61AB}\x{61AC}\x{61AE}\x{61B2}\x{61B6}\x{61BA}\x{61BE}' .
+'\x{61C3}\x{61C6}\x{61C7}\x{61C8}\x{61C9}\x{61CA}\x{61CB}\x{61CC}\x{61CD}' .
+'\x{61D0}\x{61E3}\x{61E6}\x{61F2}\x{61F4}\x{61F6}\x{61F7}\x{61F8}\x{61FA}' .
+'\x{61FC}\x{61FD}\x{61FE}\x{61FF}\x{6200}\x{6208}\x{6209}\x{620A}\x{620C}' .
+'\x{620D}\x{620E}\x{6210}\x{6211}\x{6212}\x{6214}\x{6216}\x{621A}\x{621B}' .
+'\x{621D}\x{621E}\x{621F}\x{6221}\x{6226}\x{622A}\x{622E}\x{622F}\x{6230}' .
+'\x{6232}\x{6233}\x{6234}\x{6238}\x{623B}\x{623F}\x{6240}\x{6241}\x{6247}' .
+'\x{6248}\x{6249}\x{624B}\x{624D}\x{624E}\x{6253}\x{6255}\x{6258}\x{625B}' .
+'\x{625E}\x{6260}\x{6263}\x{6268}\x{626E}\x{6271}\x{6276}\x{6279}\x{627C}' .
+'\x{627E}\x{627F}\x{6280}\x{6282}\x{6283}\x{6284}\x{6289}\x{628A}\x{6291}' .
+'\x{6292}\x{6293}\x{6294}\x{6295}\x{6296}\x{6297}\x{6298}\x{629B}\x{629C}' .
+'\x{629E}\x{62AB}\x{62AC}\x{62B1}\x{62B5}\x{62B9}\x{62BB}\x{62BC}\x{62BD}' .
+'\x{62C2}\x{62C5}\x{62C6}\x{62C7}\x{62C8}\x{62C9}\x{62CA}\x{62CC}\x{62CD}' .
+'\x{62CF}\x{62D0}\x{62D1}\x{62D2}\x{62D3}\x{62D4}\x{62D7}\x{62D8}\x{62D9}' .
+'\x{62DB}\x{62DC}\x{62DD}\x{62E0}\x{62E1}\x{62EC}\x{62ED}\x{62EE}\x{62EF}' .
+'\x{62F1}\x{62F3}\x{62F5}\x{62F6}\x{62F7}\x{62FE}\x{62FF}\x{6301}\x{6302}' .
+'\x{6307}\x{6308}\x{6309}\x{630C}\x{6311}\x{6319}\x{631F}\x{6327}\x{6328}' .
+'\x{632B}\x{632F}\x{633A}\x{633D}\x{633E}\x{633F}\x{6349}\x{634C}\x{634D}' .
+'\x{634F}\x{6350}\x{6355}\x{6357}\x{635C}\x{6367}\x{6368}\x{6369}\x{636B}' .
+'\x{636E}\x{6372}\x{6376}\x{6377}\x{637A}\x{637B}\x{6380}\x{6383}\x{6388}' .
+'\x{6389}\x{638C}\x{638E}\x{638F}\x{6392}\x{6396}\x{6398}\x{639B}\x{639F}' .
+'\x{63A0}\x{63A1}\x{63A2}\x{63A3}\x{63A5}\x{63A7}\x{63A8}\x{63A9}\x{63AA}' .
+'\x{63AB}\x{63AC}\x{63B2}\x{63B4}\x{63B5}\x{63BB}\x{63BE}\x{63C0}\x{63C3}' .
+'\x{63C4}\x{63C6}\x{63C9}\x{63CF}\x{63D0}\x{63D2}\x{63D6}\x{63DA}\x{63DB}' .
+'\x{63E1}\x{63E3}\x{63E9}\x{63EE}\x{63F4}\x{63F6}\x{63FA}\x{6406}\x{640D}' .
+'\x{640F}\x{6413}\x{6416}\x{6417}\x{641C}\x{6426}\x{6428}\x{642C}\x{642D}' .
+'\x{6434}\x{6436}\x{643A}\x{643E}\x{6442}\x{644E}\x{6458}\x{6467}\x{6469}' .
+'\x{646F}\x{6476}\x{6478}\x{647A}\x{6483}\x{6488}\x{6492}\x{6493}\x{6495}' .
+'\x{649A}\x{649E}\x{64A4}\x{64A5}\x{64A9}\x{64AB}\x{64AD}\x{64AE}\x{64B0}' .
+'\x{64B2}\x{64B9}\x{64BB}\x{64BC}\x{64C1}\x{64C2}\x{64C5}\x{64C7}\x{64CD}' .
+'\x{64D2}\x{64D4}\x{64D8}\x{64DA}\x{64E0}\x{64E1}\x{64E2}\x{64E3}\x{64E6}' .
+'\x{64E7}\x{64EC}\x{64EF}\x{64F1}\x{64F2}\x{64F4}\x{64F6}\x{64FA}\x{64FD}' .
+'\x{64FE}\x{6500}\x{6505}\x{6518}\x{651C}\x{651D}\x{6523}\x{6524}\x{652A}' .
+'\x{652B}\x{652C}\x{652F}\x{6534}\x{6535}\x{6536}\x{6537}\x{6538}\x{6539}' .
+'\x{653B}\x{653E}\x{653F}\x{6545}\x{6548}\x{654D}\x{654F}\x{6551}\x{6555}' .
+'\x{6556}\x{6557}\x{6558}\x{6559}\x{655D}\x{655E}\x{6562}\x{6563}\x{6566}' .
+'\x{656C}\x{6570}\x{6572}\x{6574}\x{6575}\x{6577}\x{6578}\x{6582}\x{6583}' .
+'\x{6587}\x{6588}\x{6589}\x{658C}\x{658E}\x{6590}\x{6591}\x{6597}\x{6599}' .
+'\x{659B}\x{659C}\x{659F}\x{65A1}\x{65A4}\x{65A5}\x{65A7}\x{65AB}\x{65AC}' .
+'\x{65AD}\x{65AF}\x{65B0}\x{65B7}\x{65B9}\x{65BC}\x{65BD}\x{65C1}\x{65C3}' .
+'\x{65C4}\x{65C5}\x{65C6}\x{65CB}\x{65CC}\x{65CF}\x{65D2}\x{65D7}\x{65D9}' .
+'\x{65DB}\x{65E0}\x{65E1}\x{65E2}\x{65E5}\x{65E6}\x{65E7}\x{65E8}\x{65E9}' .
+'\x{65EC}\x{65ED}\x{65F1}\x{65FA}\x{65FB}\x{6602}\x{6603}\x{6606}\x{6607}' .
+'\x{660A}\x{660C}\x{660E}\x{660F}\x{6613}\x{6614}\x{661C}\x{661F}\x{6620}' .
+'\x{6625}\x{6627}\x{6628}\x{662D}\x{662F}\x{6634}\x{6635}\x{6636}\x{663C}' .
+'\x{663F}\x{6641}\x{6642}\x{6643}\x{6644}\x{6649}\x{664B}\x{664F}\x{6652}' .
+'\x{665D}\x{665E}\x{665F}\x{6662}\x{6664}\x{6666}\x{6667}\x{6668}\x{6669}' .
+'\x{666E}\x{666F}\x{6670}\x{6674}\x{6676}\x{667A}\x{6681}\x{6683}\x{6684}' .
+'\x{6687}\x{6688}\x{6689}\x{668E}\x{6691}\x{6696}\x{6697}\x{6698}\x{669D}' .
+'\x{66A2}\x{66A6}\x{66AB}\x{66AE}\x{66B4}\x{66B8}\x{66B9}\x{66BC}\x{66BE}' .
+'\x{66C1}\x{66C4}\x{66C7}\x{66C9}\x{66D6}\x{66D9}\x{66DA}\x{66DC}\x{66DD}' .
+'\x{66E0}\x{66E6}\x{66E9}\x{66F0}\x{66F2}\x{66F3}\x{66F4}\x{66F5}\x{66F7}' .
+'\x{66F8}\x{66F9}\x{66FC}\x{66FD}\x{66FE}\x{66FF}\x{6700}\x{6703}\x{6708}' .
+'\x{6709}\x{670B}\x{670D}\x{670F}\x{6714}\x{6715}\x{6716}\x{6717}\x{671B}' .
+'\x{671D}\x{671E}\x{671F}\x{6726}\x{6727}\x{6728}\x{672A}\x{672B}\x{672C}' .
+'\x{672D}\x{672E}\x{6731}\x{6734}\x{6736}\x{6737}\x{6738}\x{673A}\x{673D}' .
+'\x{673F}\x{6741}\x{6746}\x{6749}\x{674E}\x{674F}\x{6750}\x{6751}\x{6753}' .
+'\x{6756}\x{6759}\x{675C}\x{675E}\x{675F}\x{6760}\x{6761}\x{6762}\x{6763}' .
+'\x{6764}\x{6765}\x{676A}\x{676D}\x{676F}\x{6770}\x{6771}\x{6772}\x{6773}' .
+'\x{6775}\x{6777}\x{677C}\x{677E}\x{677F}\x{6785}\x{6787}\x{6789}\x{678B}' .
+'\x{678C}\x{6790}\x{6795}\x{6797}\x{679A}\x{679C}\x{679D}\x{67A0}\x{67A1}' .
+'\x{67A2}\x{67A6}\x{67A9}\x{67AF}\x{67B3}\x{67B4}\x{67B6}\x{67B7}\x{67B8}' .
+'\x{67B9}\x{67C1}\x{67C4}\x{67C6}\x{67CA}\x{67CE}\x{67CF}\x{67D0}\x{67D1}' .
+'\x{67D3}\x{67D4}\x{67D8}\x{67DA}\x{67DD}\x{67DE}\x{67E2}\x{67E4}\x{67E7}' .
+'\x{67E9}\x{67EC}\x{67EE}\x{67EF}\x{67F1}\x{67F3}\x{67F4}\x{67F5}\x{67FB}' .
+'\x{67FE}\x{67FF}\x{6802}\x{6803}\x{6804}\x{6813}\x{6816}\x{6817}\x{681E}' .
+'\x{6821}\x{6822}\x{6829}\x{682A}\x{682B}\x{6832}\x{6834}\x{6838}\x{6839}' .
+'\x{683C}\x{683D}\x{6840}\x{6841}\x{6842}\x{6843}\x{6846}\x{6848}\x{684D}' .
+'\x{684E}\x{6850}\x{6851}\x{6853}\x{6854}\x{6859}\x{685C}\x{685D}\x{685F}' .
+'\x{6863}\x{6867}\x{6874}\x{6876}\x{6877}\x{687E}\x{687F}\x{6881}\x{6883}' .
+'\x{6885}\x{688D}\x{688F}\x{6893}\x{6894}\x{6897}\x{689B}\x{689D}\x{689F}' .
+'\x{68A0}\x{68A2}\x{68A6}\x{68A7}\x{68A8}\x{68AD}\x{68AF}\x{68B0}\x{68B1}' .
+'\x{68B3}\x{68B5}\x{68B6}\x{68B9}\x{68BA}\x{68BC}\x{68C4}\x{68C6}\x{68C9}' .
+'\x{68CA}\x{68CB}\x{68CD}\x{68D2}\x{68D4}\x{68D5}\x{68D7}\x{68D8}\x{68DA}' .
+'\x{68DF}\x{68E0}\x{68E1}\x{68E3}\x{68E7}\x{68EE}\x{68EF}\x{68F2}\x{68F9}' .
+'\x{68FA}\x{6900}\x{6901}\x{6904}\x{6905}\x{6908}\x{690B}\x{690C}\x{690D}' .
+'\x{690E}\x{690F}\x{6912}\x{6919}\x{691A}\x{691B}\x{691C}\x{6921}\x{6922}' .
+'\x{6923}\x{6925}\x{6926}\x{6928}\x{692A}\x{6930}\x{6934}\x{6936}\x{6939}' .
+'\x{693D}\x{693F}\x{694A}\x{6953}\x{6954}\x{6955}\x{6959}\x{695A}\x{695C}' .
+'\x{695D}\x{695E}\x{6960}\x{6961}\x{6962}\x{696A}\x{696B}\x{696D}\x{696E}' .
+'\x{696F}\x{6973}\x{6974}\x{6975}\x{6977}\x{6978}\x{6979}\x{697C}\x{697D}' .
+'\x{697E}\x{6981}\x{6982}\x{698A}\x{698E}\x{6991}\x{6994}\x{6995}\x{699B}' .
+'\x{699C}\x{69A0}\x{69A7}\x{69AE}\x{69B1}\x{69B2}\x{69B4}\x{69BB}\x{69BE}' .
+'\x{69BF}\x{69C1}\x{69C3}\x{69C7}\x{69CA}\x{69CB}\x{69CC}\x{69CD}\x{69CE}' .
+'\x{69D0}\x{69D3}\x{69D8}\x{69D9}\x{69DD}\x{69DE}\x{69E7}\x{69E8}\x{69EB}' .
+'\x{69ED}\x{69F2}\x{69F9}\x{69FB}\x{69FD}\x{69FF}\x{6A02}\x{6A05}\x{6A0A}' .
+'\x{6A0B}\x{6A0C}\x{6A12}\x{6A13}\x{6A14}\x{6A17}\x{6A19}\x{6A1B}\x{6A1E}' .
+'\x{6A1F}\x{6A21}\x{6A22}\x{6A23}\x{6A29}\x{6A2A}\x{6A2B}\x{6A2E}\x{6A35}' .
+'\x{6A36}\x{6A38}\x{6A39}\x{6A3A}\x{6A3D}\x{6A44}\x{6A47}\x{6A48}\x{6A4B}' .
+'\x{6A58}\x{6A59}\x{6A5F}\x{6A61}\x{6A62}\x{6A66}\x{6A72}\x{6A78}\x{6A7F}' .
+'\x{6A80}\x{6A84}\x{6A8D}\x{6A8E}\x{6A90}\x{6A97}\x{6A9C}\x{6AA0}\x{6AA2}' .
+'\x{6AA3}\x{6AAA}\x{6AAC}\x{6AAE}\x{6AB3}\x{6AB8}\x{6ABB}\x{6AC1}\x{6AC2}' .
+'\x{6AC3}\x{6AD1}\x{6AD3}\x{6ADA}\x{6ADB}\x{6ADE}\x{6ADF}\x{6AE8}\x{6AEA}' .
+'\x{6AFA}\x{6AFB}\x{6B04}\x{6B05}\x{6B0A}\x{6B12}\x{6B16}\x{6B1D}\x{6B1F}' .
+'\x{6B20}\x{6B21}\x{6B23}\x{6B27}\x{6B32}\x{6B37}\x{6B38}\x{6B39}\x{6B3A}' .
+'\x{6B3D}\x{6B3E}\x{6B43}\x{6B47}\x{6B49}\x{6B4C}\x{6B4E}\x{6B50}\x{6B53}' .
+'\x{6B54}\x{6B59}\x{6B5B}\x{6B5F}\x{6B61}\x{6B62}\x{6B63}\x{6B64}\x{6B66}' .
+'\x{6B69}\x{6B6A}\x{6B6F}\x{6B73}\x{6B74}\x{6B78}\x{6B79}\x{6B7B}\x{6B7F}' .
+'\x{6B80}\x{6B83}\x{6B84}\x{6B86}\x{6B89}\x{6B8A}\x{6B8B}\x{6B8D}\x{6B95}' .
+'\x{6B96}\x{6B98}\x{6B9E}\x{6BA4}\x{6BAA}\x{6BAB}\x{6BAF}\x{6BB1}\x{6BB2}' .
+'\x{6BB3}\x{6BB4}\x{6BB5}\x{6BB7}\x{6BBA}\x{6BBB}\x{6BBC}\x{6BBF}\x{6BC0}' .
+'\x{6BC5}\x{6BC6}\x{6BCB}\x{6BCD}\x{6BCE}\x{6BD2}\x{6BD3}\x{6BD4}\x{6BD8}' .
+'\x{6BDB}\x{6BDF}\x{6BEB}\x{6BEC}\x{6BEF}\x{6BF3}\x{6C08}\x{6C0F}\x{6C11}' .
+'\x{6C13}\x{6C14}\x{6C17}\x{6C1B}\x{6C23}\x{6C24}\x{6C34}\x{6C37}\x{6C38}' .
+'\x{6C3E}\x{6C40}\x{6C41}\x{6C42}\x{6C4E}\x{6C50}\x{6C55}\x{6C57}\x{6C5A}' .
+'\x{6C5D}\x{6C5E}\x{6C5F}\x{6C60}\x{6C62}\x{6C68}\x{6C6A}\x{6C70}\x{6C72}' .
+'\x{6C73}\x{6C7A}\x{6C7D}\x{6C7E}\x{6C81}\x{6C82}\x{6C83}\x{6C88}\x{6C8C}' .
+'\x{6C8D}\x{6C90}\x{6C92}\x{6C93}\x{6C96}\x{6C99}\x{6C9A}\x{6C9B}\x{6CA1}' .
+'\x{6CA2}\x{6CAB}\x{6CAE}\x{6CB1}\x{6CB3}\x{6CB8}\x{6CB9}\x{6CBA}\x{6CBB}' .
+'\x{6CBC}\x{6CBD}\x{6CBE}\x{6CBF}\x{6CC1}\x{6CC4}\x{6CC5}\x{6CC9}\x{6CCA}' .
+'\x{6CCC}\x{6CD3}\x{6CD5}\x{6CD7}\x{6CD9}\x{6CDB}\x{6CDD}\x{6CE1}\x{6CE2}' .
+'\x{6CE3}\x{6CE5}\x{6CE8}\x{6CEA}\x{6CEF}\x{6CF0}\x{6CF1}\x{6CF3}\x{6D0B}' .
+'\x{6D0C}\x{6D12}\x{6D17}\x{6D19}\x{6D1B}\x{6D1E}\x{6D1F}\x{6D25}\x{6D29}' .
+'\x{6D2A}\x{6D2B}\x{6D32}\x{6D33}\x{6D35}\x{6D36}\x{6D38}\x{6D3B}\x{6D3D}' .
+'\x{6D3E}\x{6D41}\x{6D44}\x{6D45}\x{6D59}\x{6D5A}\x{6D5C}\x{6D63}\x{6D64}' .
+'\x{6D66}\x{6D69}\x{6D6A}\x{6D6C}\x{6D6E}\x{6D74}\x{6D77}\x{6D78}\x{6D79}' .
+'\x{6D85}\x{6D88}\x{6D8C}\x{6D8E}\x{6D93}\x{6D95}\x{6D99}\x{6D9B}\x{6D9C}' .
+'\x{6DAF}\x{6DB2}\x{6DB5}\x{6DB8}\x{6DBC}\x{6DC0}\x{6DC5}\x{6DC6}\x{6DC7}' .
+'\x{6DCB}\x{6DCC}\x{6DD1}\x{6DD2}\x{6DD5}\x{6DD8}\x{6DD9}\x{6DDE}\x{6DE1}' .
+'\x{6DE4}\x{6DE6}\x{6DE8}\x{6DEA}\x{6DEB}\x{6DEC}\x{6DEE}\x{6DF1}\x{6DF3}' .
+'\x{6DF5}\x{6DF7}\x{6DF9}\x{6DFA}\x{6DFB}\x{6E05}\x{6E07}\x{6E08}\x{6E09}' .
+'\x{6E0A}\x{6E0B}\x{6E13}\x{6E15}\x{6E19}\x{6E1A}\x{6E1B}\x{6E1D}\x{6E1F}' .
+'\x{6E20}\x{6E21}\x{6E23}\x{6E24}\x{6E25}\x{6E26}\x{6E29}\x{6E2B}\x{6E2C}' .
+'\x{6E2D}\x{6E2E}\x{6E2F}\x{6E38}\x{6E3A}\x{6E3E}\x{6E43}\x{6E4A}\x{6E4D}' .
+'\x{6E4E}\x{6E56}\x{6E58}\x{6E5B}\x{6E5F}\x{6E67}\x{6E6B}\x{6E6E}\x{6E6F}' .
+'\x{6E72}\x{6E76}\x{6E7E}\x{6E7F}\x{6E80}\x{6E82}\x{6E8C}\x{6E8F}\x{6E90}' .
+'\x{6E96}\x{6E98}\x{6E9C}\x{6E9D}\x{6E9F}\x{6EA2}\x{6EA5}\x{6EAA}\x{6EAF}' .
+'\x{6EB2}\x{6EB6}\x{6EB7}\x{6EBA}\x{6EBD}\x{6EC2}\x{6EC4}\x{6EC5}\x{6EC9}' .
+'\x{6ECB}\x{6ECC}\x{6ED1}\x{6ED3}\x{6ED4}\x{6ED5}\x{6EDD}\x{6EDE}\x{6EEC}' .
+'\x{6EEF}\x{6EF2}\x{6EF4}\x{6EF7}\x{6EF8}\x{6EFE}\x{6EFF}\x{6F01}\x{6F02}' .
+'\x{6F06}\x{6F09}\x{6F0F}\x{6F11}\x{6F13}\x{6F14}\x{6F15}\x{6F20}\x{6F22}' .
+'\x{6F23}\x{6F2B}\x{6F2C}\x{6F31}\x{6F32}\x{6F38}\x{6F3E}\x{6F3F}\x{6F41}' .
+'\x{6F45}\x{6F54}\x{6F58}\x{6F5B}\x{6F5C}\x{6F5F}\x{6F64}\x{6F66}\x{6F6D}' .
+'\x{6F6E}\x{6F6F}\x{6F70}\x{6F74}\x{6F78}\x{6F7A}\x{6F7C}\x{6F80}\x{6F81}' .
+'\x{6F82}\x{6F84}\x{6F86}\x{6F8E}\x{6F91}\x{6F97}\x{6FA1}\x{6FA3}\x{6FA4}' .
+'\x{6FAA}\x{6FB1}\x{6FB3}\x{6FB9}\x{6FC0}\x{6FC1}\x{6FC2}\x{6FC3}\x{6FC6}' .
+'\x{6FD4}\x{6FD5}\x{6FD8}\x{6FDB}\x{6FDF}\x{6FE0}\x{6FE1}\x{6FE4}\x{6FEB}' .
+'\x{6FEC}\x{6FEE}\x{6FEF}\x{6FF1}\x{6FF3}\x{6FF6}\x{6FFA}\x{6FFE}\x{7001}' .
+'\x{7009}\x{700B}\x{700F}\x{7011}\x{7015}\x{7018}\x{701A}\x{701B}\x{701D}' .
+'\x{701E}\x{701F}\x{7026}\x{7027}\x{702C}\x{7030}\x{7032}\x{703E}\x{704C}' .
+'\x{7051}\x{7058}\x{7063}\x{706B}\x{706F}\x{7070}\x{7078}\x{707C}\x{707D}' .
+'\x{7089}\x{708A}\x{708E}\x{7092}\x{7099}\x{70AC}\x{70AD}\x{70AE}\x{70AF}' .
+'\x{70B3}\x{70B8}\x{70B9}\x{70BA}\x{70C8}\x{70CB}\x{70CF}\x{70D9}\x{70DD}' .
+'\x{70DF}\x{70F1}\x{70F9}\x{70FD}\x{7109}\x{7114}\x{7119}\x{711A}\x{711C}' .
+'\x{7121}\x{7126}\x{7136}\x{713C}\x{7149}\x{714C}\x{714E}\x{7155}\x{7156}' .
+'\x{7159}\x{7162}\x{7164}\x{7165}\x{7166}\x{7167}\x{7169}\x{716C}\x{716E}' .
+'\x{717D}\x{7184}\x{7188}\x{718A}\x{718F}\x{7194}\x{7195}\x{7199}\x{719F}' .
+'\x{71A8}\x{71AC}\x{71B1}\x{71B9}\x{71BE}\x{71C3}\x{71C8}\x{71C9}\x{71CE}' .
+'\x{71D0}\x{71D2}\x{71D4}\x{71D5}\x{71D7}\x{71DF}\x{71E0}\x{71E5}\x{71E6}' .
+'\x{71E7}\x{71EC}\x{71ED}\x{71EE}\x{71F5}\x{71F9}\x{71FB}\x{71FC}\x{71FF}' .
+'\x{7206}\x{720D}\x{7210}\x{721B}\x{7228}\x{722A}\x{722C}\x{722D}\x{7230}' .
+'\x{7232}\x{7235}\x{7236}\x{723A}\x{723B}\x{723C}\x{723D}\x{723E}\x{723F}' .
+'\x{7240}\x{7246}\x{7247}\x{7248}\x{724B}\x{724C}\x{7252}\x{7258}\x{7259}' .
+'\x{725B}\x{725D}\x{725F}\x{7261}\x{7262}\x{7267}\x{7269}\x{7272}\x{7274}' .
+'\x{7279}\x{727D}\x{727E}\x{7280}\x{7281}\x{7282}\x{7287}\x{7292}\x{7296}' .
+'\x{72A0}\x{72A2}\x{72A7}\x{72AC}\x{72AF}\x{72B2}\x{72B6}\x{72B9}\x{72C2}' .
+'\x{72C3}\x{72C4}\x{72C6}\x{72CE}\x{72D0}\x{72D2}\x{72D7}\x{72D9}\x{72DB}' .
+'\x{72E0}\x{72E1}\x{72E2}\x{72E9}\x{72EC}\x{72ED}\x{72F7}\x{72F8}\x{72F9}' .
+'\x{72FC}\x{72FD}\x{730A}\x{7316}\x{7317}\x{731B}\x{731C}\x{731D}\x{731F}' .
+'\x{7325}\x{7329}\x{732A}\x{732B}\x{732E}\x{732F}\x{7334}\x{7336}\x{7337}' .
+'\x{733E}\x{733F}\x{7344}\x{7345}\x{734E}\x{734F}\x{7357}\x{7363}\x{7368}' .
+'\x{736A}\x{7370}\x{7372}\x{7375}\x{7378}\x{737A}\x{737B}\x{7384}\x{7387}' .
+'\x{7389}\x{738B}\x{7396}\x{73A9}\x{73B2}\x{73B3}\x{73BB}\x{73C0}\x{73C2}' .
+'\x{73C8}\x{73CA}\x{73CD}\x{73CE}\x{73DE}\x{73E0}\x{73E5}\x{73EA}\x{73ED}' .
+'\x{73EE}\x{73F1}\x{73F8}\x{73FE}\x{7403}\x{7405}\x{7406}\x{7409}\x{7422}' .
+'\x{7425}\x{7432}\x{7433}\x{7434}\x{7435}\x{7436}\x{743A}\x{743F}\x{7441}' .
+'\x{7455}\x{7459}\x{745A}\x{745B}\x{745C}\x{745E}\x{745F}\x{7460}\x{7463}' .
+'\x{7464}\x{7469}\x{746A}\x{746F}\x{7470}\x{7473}\x{7476}\x{747E}\x{7483}' .
+'\x{748B}\x{749E}\x{74A2}\x{74A7}\x{74B0}\x{74BD}\x{74CA}\x{74CF}\x{74D4}' .
+'\x{74DC}\x{74E0}\x{74E2}\x{74E3}\x{74E6}\x{74E7}\x{74E9}\x{74EE}\x{74F0}' .
+'\x{74F1}\x{74F2}\x{74F6}\x{74F7}\x{74F8}\x{7503}\x{7504}\x{7505}\x{750C}' .
+'\x{750D}\x{750E}\x{7511}\x{7513}\x{7515}\x{7518}\x{751A}\x{751C}\x{751E}' .
+'\x{751F}\x{7523}\x{7525}\x{7526}\x{7528}\x{752B}\x{752C}\x{7530}\x{7531}' .
+'\x{7532}\x{7533}\x{7537}\x{7538}\x{753A}\x{753B}\x{753C}\x{7544}\x{7546}' .
+'\x{7549}\x{754A}\x{754B}\x{754C}\x{754D}\x{754F}\x{7551}\x{7554}\x{7559}' .
+'\x{755A}\x{755B}\x{755C}\x{755D}\x{7560}\x{7562}\x{7564}\x{7565}\x{7566}' .
+'\x{7567}\x{7569}\x{756A}\x{756B}\x{756D}\x{7570}\x{7573}\x{7574}\x{7576}' .
+'\x{7577}\x{7578}\x{757F}\x{7582}\x{7586}\x{7587}\x{7589}\x{758A}\x{758B}' .
+'\x{758E}\x{758F}\x{7591}\x{7594}\x{759A}\x{759D}\x{75A3}\x{75A5}\x{75AB}' .
+'\x{75B1}\x{75B2}\x{75B3}\x{75B5}\x{75B8}\x{75B9}\x{75BC}\x{75BD}\x{75BE}' .
+'\x{75C2}\x{75C3}\x{75C5}\x{75C7}\x{75CA}\x{75CD}\x{75D2}\x{75D4}\x{75D5}' .
+'\x{75D8}\x{75D9}\x{75DB}\x{75DE}\x{75E2}\x{75E3}\x{75E9}\x{75F0}\x{75F2}' .
+'\x{75F3}\x{75F4}\x{75FA}\x{75FC}\x{75FE}\x{75FF}\x{7601}\x{7609}\x{760B}' .
+'\x{760D}\x{761F}\x{7620}\x{7621}\x{7622}\x{7624}\x{7627}\x{7630}\x{7634}' .
+'\x{763B}\x{7642}\x{7646}\x{7647}\x{7648}\x{764C}\x{7652}\x{7656}\x{7658}' .
+'\x{765C}\x{7661}\x{7662}\x{7667}\x{7668}\x{7669}\x{766A}\x{766C}\x{7670}' .
+'\x{7672}\x{7676}\x{7678}\x{767A}\x{767B}\x{767C}\x{767D}\x{767E}\x{7680}' .
+'\x{7683}\x{7684}\x{7686}\x{7687}\x{7688}\x{768B}\x{768E}\x{7690}\x{7693}' .
+'\x{7696}\x{7699}\x{769A}\x{76AE}\x{76B0}\x{76B4}\x{76B7}\x{76B8}\x{76B9}' .
+'\x{76BA}\x{76BF}\x{76C2}\x{76C3}\x{76C6}\x{76C8}\x{76CA}\x{76CD}\x{76D2}' .
+'\x{76D6}\x{76D7}\x{76DB}\x{76DC}\x{76DE}\x{76DF}\x{76E1}\x{76E3}\x{76E4}' .
+'\x{76E5}\x{76E7}\x{76EA}\x{76EE}\x{76F2}\x{76F4}\x{76F8}\x{76FB}\x{76FE}' .
+'\x{7701}\x{7704}\x{7707}\x{7708}\x{7709}\x{770B}\x{770C}\x{771B}\x{771E}' .
+'\x{771F}\x{7720}\x{7724}\x{7725}\x{7726}\x{7729}\x{7737}\x{7738}\x{773A}' .
+'\x{773C}\x{7740}\x{7747}\x{775A}\x{775B}\x{7761}\x{7763}\x{7765}\x{7766}' .
+'\x{7768}\x{776B}\x{7779}\x{777E}\x{777F}\x{778B}\x{778E}\x{7791}\x{779E}' .
+'\x{77A0}\x{77A5}\x{77AC}\x{77AD}\x{77B0}\x{77B3}\x{77B6}\x{77B9}\x{77BB}' .
+'\x{77BC}\x{77BD}\x{77BF}\x{77C7}\x{77CD}\x{77D7}\x{77DA}\x{77DB}\x{77DC}' .
+'\x{77E2}\x{77E3}\x{77E5}\x{77E7}\x{77E9}\x{77ED}\x{77EE}\x{77EF}\x{77F3}' .
+'\x{77FC}\x{7802}\x{780C}\x{7812}\x{7814}\x{7815}\x{7820}\x{7825}\x{7826}' .
+'\x{7827}\x{7832}\x{7834}\x{783A}\x{783F}\x{7845}\x{785D}\x{786B}\x{786C}' .
+'\x{786F}\x{7872}\x{7874}\x{787C}\x{7881}\x{7886}\x{7887}\x{788C}\x{788D}' .
+'\x{788E}\x{7891}\x{7893}\x{7895}\x{7897}\x{789A}\x{78A3}\x{78A7}\x{78A9}' .
+'\x{78AA}\x{78AF}\x{78B5}\x{78BA}\x{78BC}\x{78BE}\x{78C1}\x{78C5}\x{78C6}' .
+'\x{78CA}\x{78CB}\x{78D0}\x{78D1}\x{78D4}\x{78DA}\x{78E7}\x{78E8}\x{78EC}' .
+'\x{78EF}\x{78F4}\x{78FD}\x{7901}\x{7907}\x{790E}\x{7911}\x{7912}\x{7919}' .
+'\x{7926}\x{792A}\x{792B}\x{792C}\x{793A}\x{793C}\x{793E}\x{7940}\x{7941}' .
+'\x{7947}\x{7948}\x{7949}\x{7950}\x{7953}\x{7955}\x{7956}\x{7957}\x{795A}' .
+'\x{795D}\x{795E}\x{795F}\x{7960}\x{7962}\x{7965}\x{7968}\x{796D}\x{7977}' .
+'\x{797A}\x{797F}\x{7980}\x{7981}\x{7984}\x{7985}\x{798A}\x{798D}\x{798E}' .
+'\x{798F}\x{799D}\x{79A6}\x{79A7}\x{79AA}\x{79AE}\x{79B0}\x{79B3}\x{79B9}' .
+'\x{79BA}\x{79BD}\x{79BE}\x{79BF}\x{79C0}\x{79C1}\x{79C9}\x{79CB}\x{79D1}' .
+'\x{79D2}\x{79D5}\x{79D8}\x{79DF}\x{79E1}\x{79E3}\x{79E4}\x{79E6}\x{79E7}' .
+'\x{79E9}\x{79EC}\x{79F0}\x{79FB}\x{7A00}\x{7A08}\x{7A0B}\x{7A0D}\x{7A0E}' .
+'\x{7A14}\x{7A17}\x{7A18}\x{7A19}\x{7A1A}\x{7A1C}\x{7A1F}\x{7A20}\x{7A2E}' .
+'\x{7A31}\x{7A32}\x{7A37}\x{7A3B}\x{7A3C}\x{7A3D}\x{7A3E}\x{7A3F}\x{7A40}' .
+'\x{7A42}\x{7A43}\x{7A46}\x{7A49}\x{7A4D}\x{7A4E}\x{7A4F}\x{7A50}\x{7A57}' .
+'\x{7A61}\x{7A62}\x{7A63}\x{7A69}\x{7A6B}\x{7A70}\x{7A74}\x{7A76}\x{7A79}' .
+'\x{7A7A}\x{7A7D}\x{7A7F}\x{7A81}\x{7A83}\x{7A84}\x{7A88}\x{7A92}\x{7A93}' .
+'\x{7A95}\x{7A96}\x{7A97}\x{7A98}\x{7A9F}\x{7AA9}\x{7AAA}\x{7AAE}\x{7AAF}' .
+'\x{7AB0}\x{7AB6}\x{7ABA}\x{7ABF}\x{7AC3}\x{7AC4}\x{7AC5}\x{7AC7}\x{7AC8}' .
+'\x{7ACA}\x{7ACB}\x{7ACD}\x{7ACF}\x{7AD2}\x{7AD3}\x{7AD5}\x{7AD9}\x{7ADA}' .
+'\x{7ADC}\x{7ADD}\x{7ADF}\x{7AE0}\x{7AE1}\x{7AE2}\x{7AE3}\x{7AE5}\x{7AE6}' .
+'\x{7AEA}\x{7AED}\x{7AEF}\x{7AF0}\x{7AF6}\x{7AF8}\x{7AF9}\x{7AFA}\x{7AFF}' .
+'\x{7B02}\x{7B04}\x{7B06}\x{7B08}\x{7B0A}\x{7B0B}\x{7B0F}\x{7B11}\x{7B18}' .
+'\x{7B19}\x{7B1B}\x{7B1E}\x{7B20}\x{7B25}\x{7B26}\x{7B28}\x{7B2C}\x{7B33}' .
+'\x{7B35}\x{7B36}\x{7B39}\x{7B45}\x{7B46}\x{7B48}\x{7B49}\x{7B4B}\x{7B4C}' .
+'\x{7B4D}\x{7B4F}\x{7B50}\x{7B51}\x{7B52}\x{7B54}\x{7B56}\x{7B5D}\x{7B65}' .
+'\x{7B67}\x{7B6C}\x{7B6E}\x{7B70}\x{7B71}\x{7B74}\x{7B75}\x{7B7A}\x{7B86}' .
+'\x{7B87}\x{7B8B}\x{7B8D}\x{7B8F}\x{7B92}\x{7B94}\x{7B95}\x{7B97}\x{7B98}' .
+'\x{7B99}\x{7B9A}\x{7B9C}\x{7B9D}\x{7B9F}\x{7BA1}\x{7BAA}\x{7BAD}\x{7BB1}' .
+'\x{7BB4}\x{7BB8}\x{7BC0}\x{7BC1}\x{7BC4}\x{7BC6}\x{7BC7}\x{7BC9}\x{7BCB}' .
+'\x{7BCC}\x{7BCF}\x{7BDD}\x{7BE0}\x{7BE4}\x{7BE5}\x{7BE6}\x{7BE9}\x{7BED}' .
+'\x{7BF3}\x{7BF6}\x{7BF7}\x{7C00}\x{7C07}\x{7C0D}\x{7C11}\x{7C12}\x{7C13}' .
+'\x{7C14}\x{7C17}\x{7C1F}\x{7C21}\x{7C23}\x{7C27}\x{7C2A}\x{7C2B}\x{7C37}' .
+'\x{7C38}\x{7C3D}\x{7C3E}\x{7C3F}\x{7C40}\x{7C43}\x{7C4C}\x{7C4D}\x{7C4F}' .
+'\x{7C50}\x{7C54}\x{7C56}\x{7C58}\x{7C5F}\x{7C60}\x{7C64}\x{7C65}\x{7C6C}' .
+'\x{7C73}\x{7C75}\x{7C7E}\x{7C81}\x{7C82}\x{7C83}\x{7C89}\x{7C8B}\x{7C8D}' .
+'\x{7C90}\x{7C92}\x{7C95}\x{7C97}\x{7C98}\x{7C9B}\x{7C9F}\x{7CA1}\x{7CA2}' .
+'\x{7CA4}\x{7CA5}\x{7CA7}\x{7CA8}\x{7CAB}\x{7CAD}\x{7CAE}\x{7CB1}\x{7CB2}' .
+'\x{7CB3}\x{7CB9}\x{7CBD}\x{7CBE}\x{7CC0}\x{7CC2}\x{7CC5}\x{7CCA}\x{7CCE}' .
+'\x{7CD2}\x{7CD6}\x{7CD8}\x{7CDC}\x{7CDE}\x{7CDF}\x{7CE0}\x{7CE2}\x{7CE7}' .
+'\x{7CEF}\x{7CF2}\x{7CF4}\x{7CF6}\x{7CF8}\x{7CFA}\x{7CFB}\x{7CFE}\x{7D00}' .
+'\x{7D02}\x{7D04}\x{7D05}\x{7D06}\x{7D0A}\x{7D0B}\x{7D0D}\x{7D10}\x{7D14}' .
+'\x{7D15}\x{7D17}\x{7D18}\x{7D19}\x{7D1A}\x{7D1B}\x{7D1C}\x{7D20}\x{7D21}' .
+'\x{7D22}\x{7D2B}\x{7D2C}\x{7D2E}\x{7D2F}\x{7D30}\x{7D32}\x{7D33}\x{7D35}' .
+'\x{7D39}\x{7D3A}\x{7D3F}\x{7D42}\x{7D43}\x{7D44}\x{7D45}\x{7D46}\x{7D4B}' .
+'\x{7D4C}\x{7D4E}\x{7D4F}\x{7D50}\x{7D56}\x{7D5B}\x{7D5E}\x{7D61}\x{7D62}' .
+'\x{7D63}\x{7D66}\x{7D68}\x{7D6E}\x{7D71}\x{7D72}\x{7D73}\x{7D75}\x{7D76}' .
+'\x{7D79}\x{7D7D}\x{7D89}\x{7D8F}\x{7D93}\x{7D99}\x{7D9A}\x{7D9B}\x{7D9C}' .
+'\x{7D9F}\x{7DA2}\x{7DA3}\x{7DAB}\x{7DAC}\x{7DAD}\x{7DAE}\x{7DAF}\x{7DB0}' .
+'\x{7DB1}\x{7DB2}\x{7DB4}\x{7DB5}\x{7DB8}\x{7DBA}\x{7DBB}\x{7DBD}\x{7DBE}' .
+'\x{7DBF}\x{7DC7}\x{7DCA}\x{7DCB}\x{7DCF}\x{7DD1}\x{7DD2}\x{7DD5}\x{7DD8}' .
+'\x{7DDA}\x{7DDC}\x{7DDD}\x{7DDE}\x{7DE0}\x{7DE1}\x{7DE4}\x{7DE8}\x{7DE9}' .
+'\x{7DEC}\x{7DEF}\x{7DF2}\x{7DF4}\x{7DFB}\x{7E01}\x{7E04}\x{7E05}\x{7E09}' .
+'\x{7E0A}\x{7E0B}\x{7E12}\x{7E1B}\x{7E1E}\x{7E1F}\x{7E21}\x{7E22}\x{7E23}' .
+'\x{7E26}\x{7E2B}\x{7E2E}\x{7E31}\x{7E32}\x{7E35}\x{7E37}\x{7E39}\x{7E3A}' .
+'\x{7E3B}\x{7E3D}\x{7E3E}\x{7E41}\x{7E43}\x{7E46}\x{7E4A}\x{7E4B}\x{7E4D}' .
+'\x{7E54}\x{7E55}\x{7E56}\x{7E59}\x{7E5A}\x{7E5D}\x{7E5E}\x{7E66}\x{7E67}' .
+'\x{7E69}\x{7E6A}\x{7E6D}\x{7E70}\x{7E79}\x{7E7B}\x{7E7C}\x{7E7D}\x{7E7F}' .
+'\x{7E82}\x{7E83}\x{7E88}\x{7E89}\x{7E8C}\x{7E8E}\x{7E8F}\x{7E90}\x{7E92}' .
+'\x{7E93}\x{7E94}\x{7E96}\x{7E9B}\x{7E9C}\x{7F36}\x{7F38}\x{7F3A}\x{7F45}' .
+'\x{7F4C}\x{7F4D}\x{7F4E}\x{7F50}\x{7F51}\x{7F54}\x{7F55}\x{7F58}\x{7F5F}' .
+'\x{7F60}\x{7F67}\x{7F68}\x{7F69}\x{7F6A}\x{7F6B}\x{7F6E}\x{7F70}\x{7F72}' .
+'\x{7F75}\x{7F77}\x{7F78}\x{7F79}\x{7F82}\x{7F83}\x{7F85}\x{7F86}\x{7F87}' .
+'\x{7F88}\x{7F8A}\x{7F8C}\x{7F8E}\x{7F94}\x{7F9A}\x{7F9D}\x{7F9E}\x{7FA3}' .
+'\x{7FA4}\x{7FA8}\x{7FA9}\x{7FAE}\x{7FAF}\x{7FB2}\x{7FB6}\x{7FB8}\x{7FB9}' .
+'\x{7FBD}\x{7FC1}\x{7FC5}\x{7FC6}\x{7FCA}\x{7FCC}\x{7FD2}\x{7FD4}\x{7FD5}' .
+'\x{7FE0}\x{7FE1}\x{7FE6}\x{7FE9}\x{7FEB}\x{7FF0}\x{7FF3}\x{7FF9}\x{7FFB}' .
+'\x{7FFC}\x{8000}\x{8001}\x{8003}\x{8004}\x{8005}\x{8006}\x{800B}\x{800C}' .
+'\x{8010}\x{8012}\x{8015}\x{8017}\x{8018}\x{8019}\x{801C}\x{8021}\x{8028}' .
+'\x{8033}\x{8036}\x{803B}\x{803D}\x{803F}\x{8046}\x{804A}\x{8052}\x{8056}' .
+'\x{8058}\x{805A}\x{805E}\x{805F}\x{8061}\x{8062}\x{8068}\x{806F}\x{8070}' .
+'\x{8072}\x{8073}\x{8074}\x{8076}\x{8077}\x{8079}\x{807D}\x{807E}\x{807F}' .
+'\x{8084}\x{8085}\x{8086}\x{8087}\x{8089}\x{808B}\x{808C}\x{8093}\x{8096}' .
+'\x{8098}\x{809A}\x{809B}\x{809D}\x{80A1}\x{80A2}\x{80A5}\x{80A9}\x{80AA}' .
+'\x{80AC}\x{80AD}\x{80AF}\x{80B1}\x{80B2}\x{80B4}\x{80BA}\x{80C3}\x{80C4}' .
+'\x{80C6}\x{80CC}\x{80CE}\x{80D6}\x{80D9}\x{80DA}\x{80DB}\x{80DD}\x{80DE}' .
+'\x{80E1}\x{80E4}\x{80E5}\x{80EF}\x{80F1}\x{80F4}\x{80F8}\x{80FC}\x{80FD}' .
+'\x{8102}\x{8105}\x{8106}\x{8107}\x{8108}\x{8109}\x{810A}\x{811A}\x{811B}' .
+'\x{8123}\x{8129}\x{812F}\x{8131}\x{8133}\x{8139}\x{813E}\x{8146}\x{814B}' .
+'\x{814E}\x{8150}\x{8151}\x{8153}\x{8154}\x{8155}\x{815F}\x{8165}\x{8166}' .
+'\x{816B}\x{816E}\x{8170}\x{8171}\x{8174}\x{8178}\x{8179}\x{817A}\x{817F}' .
+'\x{8180}\x{8182}\x{8183}\x{8188}\x{818A}\x{818F}\x{8193}\x{8195}\x{819A}' .
+'\x{819C}\x{819D}\x{81A0}\x{81A3}\x{81A4}\x{81A8}\x{81A9}\x{81B0}\x{81B3}' .
+'\x{81B5}\x{81B8}\x{81BA}\x{81BD}\x{81BE}\x{81BF}\x{81C0}\x{81C2}\x{81C6}' .
+'\x{81C8}\x{81C9}\x{81CD}\x{81D1}\x{81D3}\x{81D8}\x{81D9}\x{81DA}\x{81DF}' .
+'\x{81E0}\x{81E3}\x{81E5}\x{81E7}\x{81E8}\x{81EA}\x{81ED}\x{81F3}\x{81F4}' .
+'\x{81FA}\x{81FB}\x{81FC}\x{81FE}\x{8201}\x{8202}\x{8205}\x{8207}\x{8208}' .
+'\x{8209}\x{820A}\x{820C}\x{820D}\x{820E}\x{8210}\x{8212}\x{8216}\x{8217}' .
+'\x{8218}\x{821B}\x{821C}\x{821E}\x{821F}\x{8229}\x{822A}\x{822B}\x{822C}' .
+'\x{822E}\x{8233}\x{8235}\x{8236}\x{8237}\x{8238}\x{8239}\x{8240}\x{8247}' .
+'\x{8258}\x{8259}\x{825A}\x{825D}\x{825F}\x{8262}\x{8264}\x{8266}\x{8268}' .
+'\x{826A}\x{826B}\x{826E}\x{826F}\x{8271}\x{8272}\x{8276}\x{8277}\x{8278}' .
+'\x{827E}\x{828B}\x{828D}\x{8292}\x{8299}\x{829D}\x{829F}\x{82A5}\x{82A6}' .
+'\x{82AB}\x{82AC}\x{82AD}\x{82AF}\x{82B1}\x{82B3}\x{82B8}\x{82B9}\x{82BB}' .
+'\x{82BD}\x{82C5}\x{82D1}\x{82D2}\x{82D3}\x{82D4}\x{82D7}\x{82D9}\x{82DB}' .
+'\x{82DC}\x{82DE}\x{82DF}\x{82E1}\x{82E3}\x{82E5}\x{82E6}\x{82E7}\x{82EB}' .
+'\x{82F1}\x{82F3}\x{82F4}\x{82F9}\x{82FA}\x{82FB}\x{8302}\x{8303}\x{8304}' .
+'\x{8305}\x{8306}\x{8309}\x{830E}\x{8316}\x{8317}\x{8318}\x{831C}\x{8323}' .
+'\x{8328}\x{832B}\x{832F}\x{8331}\x{8332}\x{8334}\x{8335}\x{8336}\x{8338}' .
+'\x{8339}\x{8340}\x{8345}\x{8349}\x{834A}\x{834F}\x{8350}\x{8352}\x{8358}' .
+'\x{8373}\x{8375}\x{8377}\x{837B}\x{837C}\x{8385}\x{8387}\x{8389}\x{838A}' .
+'\x{838E}\x{8393}\x{8396}\x{839A}\x{839E}\x{839F}\x{83A0}\x{83A2}\x{83A8}' .
+'\x{83AA}\x{83AB}\x{83B1}\x{83B5}\x{83BD}\x{83C1}\x{83C5}\x{83CA}\x{83CC}' .
+'\x{83CE}\x{83D3}\x{83D6}\x{83D8}\x{83DC}\x{83DF}\x{83E0}\x{83E9}\x{83EB}' .
+'\x{83EF}\x{83F0}\x{83F1}\x{83F2}\x{83F4}\x{83F7}\x{83FB}\x{83FD}\x{8403}' .
+'\x{8404}\x{8407}\x{840B}\x{840C}\x{840D}\x{840E}\x{8413}\x{8420}\x{8422}' .
+'\x{8429}\x{842A}\x{842C}\x{8431}\x{8435}\x{8438}\x{843C}\x{843D}\x{8446}' .
+'\x{8449}\x{844E}\x{8457}\x{845B}\x{8461}\x{8462}\x{8463}\x{8466}\x{8469}' .
+'\x{846B}\x{846C}\x{846D}\x{846E}\x{846F}\x{8471}\x{8475}\x{8477}\x{8479}' .
+'\x{847A}\x{8482}\x{8484}\x{848B}\x{8490}\x{8494}\x{8499}\x{849C}\x{849F}' .
+'\x{84A1}\x{84AD}\x{84B2}\x{84B8}\x{84B9}\x{84BB}\x{84BC}\x{84BF}\x{84C1}' .
+'\x{84C4}\x{84C6}\x{84C9}\x{84CA}\x{84CB}\x{84CD}\x{84D0}\x{84D1}\x{84D6}' .
+'\x{84D9}\x{84DA}\x{84EC}\x{84EE}\x{84F4}\x{84FC}\x{84FF}\x{8500}\x{8506}' .
+'\x{8511}\x{8513}\x{8514}\x{8515}\x{8517}\x{8518}\x{851A}\x{851F}\x{8521}' .
+'\x{8526}\x{852C}\x{852D}\x{8535}\x{853D}\x{8540}\x{8541}\x{8543}\x{8548}' .
+'\x{8549}\x{854A}\x{854B}\x{854E}\x{8555}\x{8557}\x{8558}\x{855A}\x{8563}' .
+'\x{8568}\x{8569}\x{856A}\x{856D}\x{8577}\x{857E}\x{8580}\x{8584}\x{8587}' .
+'\x{8588}\x{858A}\x{8590}\x{8591}\x{8594}\x{8597}\x{8599}\x{859B}\x{859C}' .
+'\x{85A4}\x{85A6}\x{85A8}\x{85A9}\x{85AA}\x{85AB}\x{85AC}\x{85AE}\x{85AF}' .
+'\x{85B9}\x{85BA}\x{85C1}\x{85C9}\x{85CD}\x{85CF}\x{85D0}\x{85D5}\x{85DC}' .
+'\x{85DD}\x{85E4}\x{85E5}\x{85E9}\x{85EA}\x{85F7}\x{85F9}\x{85FA}\x{85FB}' .
+'\x{85FE}\x{8602}\x{8606}\x{8607}\x{860A}\x{860B}\x{8613}\x{8616}\x{8617}' .
+'\x{861A}\x{8622}\x{862D}\x{862F}\x{8630}\x{863F}\x{864D}\x{864E}\x{8650}' .
+'\x{8654}\x{8655}\x{865A}\x{865C}\x{865E}\x{865F}\x{8667}\x{866B}\x{8671}' .
+'\x{8679}\x{867B}\x{868A}\x{868B}\x{868C}\x{8693}\x{8695}\x{86A3}\x{86A4}' .
+'\x{86A9}\x{86AA}\x{86AB}\x{86AF}\x{86B0}\x{86B6}\x{86C4}\x{86C6}\x{86C7}' .
+'\x{86C9}\x{86CB}\x{86CD}\x{86CE}\x{86D4}\x{86D9}\x{86DB}\x{86DE}\x{86DF}' .
+'\x{86E4}\x{86E9}\x{86EC}\x{86ED}\x{86EE}\x{86EF}\x{86F8}\x{86F9}\x{86FB}' .
+'\x{86FE}\x{8700}\x{8702}\x{8703}\x{8706}\x{8708}\x{8709}\x{870A}\x{870D}' .
+'\x{8711}\x{8712}\x{8718}\x{871A}\x{871C}\x{8725}\x{8729}\x{8734}\x{8737}' .
+'\x{873B}\x{873F}\x{8749}\x{874B}\x{874C}\x{874E}\x{8753}\x{8755}\x{8757}' .
+'\x{8759}\x{875F}\x{8760}\x{8763}\x{8766}\x{8768}\x{876A}\x{876E}\x{8774}' .
+'\x{8776}\x{8778}\x{877F}\x{8782}\x{878D}\x{879F}\x{87A2}\x{87AB}\x{87AF}' .
+'\x{87B3}\x{87BA}\x{87BB}\x{87BD}\x{87C0}\x{87C4}\x{87C6}\x{87C7}\x{87CB}' .
+'\x{87D0}\x{87D2}\x{87E0}\x{87EF}\x{87F2}\x{87F6}\x{87F7}\x{87F9}\x{87FB}' .
+'\x{87FE}\x{8805}\x{880D}\x{880E}\x{880F}\x{8811}\x{8815}\x{8816}\x{8821}' .
+'\x{8822}\x{8823}\x{8827}\x{8831}\x{8836}\x{8839}\x{883B}\x{8840}\x{8842}' .
+'\x{8844}\x{8846}\x{884C}\x{884D}\x{8852}\x{8853}\x{8857}\x{8859}\x{885B}' .
+'\x{885D}\x{885E}\x{8861}\x{8862}\x{8863}\x{8868}\x{886B}\x{8870}\x{8872}' .
+'\x{8875}\x{8877}\x{887D}\x{887E}\x{887F}\x{8881}\x{8882}\x{8888}\x{888B}' .
+'\x{888D}\x{8892}\x{8896}\x{8897}\x{8899}\x{889E}\x{88A2}\x{88A4}\x{88AB}' .
+'\x{88AE}\x{88B0}\x{88B1}\x{88B4}\x{88B5}\x{88B7}\x{88BF}\x{88C1}\x{88C2}' .
+'\x{88C3}\x{88C4}\x{88C5}\x{88CF}\x{88D4}\x{88D5}\x{88D8}\x{88D9}\x{88DC}' .
+'\x{88DD}\x{88DF}\x{88E1}\x{88E8}\x{88F2}\x{88F3}\x{88F4}\x{88F8}\x{88F9}' .
+'\x{88FC}\x{88FD}\x{88FE}\x{8902}\x{8904}\x{8907}\x{890A}\x{890C}\x{8910}' .
+'\x{8912}\x{8913}\x{891D}\x{891E}\x{8925}\x{892A}\x{892B}\x{8936}\x{8938}' .
+'\x{893B}\x{8941}\x{8943}\x{8944}\x{894C}\x{894D}\x{8956}\x{895E}\x{895F}' .
+'\x{8960}\x{8964}\x{8966}\x{896A}\x{896D}\x{896F}\x{8972}\x{8974}\x{8977}' .
+'\x{897E}\x{897F}\x{8981}\x{8983}\x{8986}\x{8987}\x{8988}\x{898A}\x{898B}' .
+'\x{898F}\x{8993}\x{8996}\x{8997}\x{8998}\x{899A}\x{89A1}\x{89A6}\x{89A7}' .
+'\x{89A9}\x{89AA}\x{89AC}\x{89AF}\x{89B2}\x{89B3}\x{89BA}\x{89BD}\x{89BF}' .
+'\x{89C0}\x{89D2}\x{89DA}\x{89DC}\x{89DD}\x{89E3}\x{89E6}\x{89E7}\x{89F4}' .
+'\x{89F8}\x{8A00}\x{8A02}\x{8A03}\x{8A08}\x{8A0A}\x{8A0C}\x{8A0E}\x{8A10}' .
+'\x{8A13}\x{8A16}\x{8A17}\x{8A18}\x{8A1B}\x{8A1D}\x{8A1F}\x{8A23}\x{8A25}' .
+'\x{8A2A}\x{8A2D}\x{8A31}\x{8A33}\x{8A34}\x{8A36}\x{8A3A}\x{8A3B}\x{8A3C}' .
+'\x{8A41}\x{8A46}\x{8A48}\x{8A50}\x{8A51}\x{8A52}\x{8A54}\x{8A55}\x{8A5B}' .
+'\x{8A5E}\x{8A60}\x{8A62}\x{8A63}\x{8A66}\x{8A69}\x{8A6B}\x{8A6C}\x{8A6D}' .
+'\x{8A6E}\x{8A70}\x{8A71}\x{8A72}\x{8A73}\x{8A7C}\x{8A82}\x{8A84}\x{8A85}' .
+'\x{8A87}\x{8A89}\x{8A8C}\x{8A8D}\x{8A91}\x{8A93}\x{8A95}\x{8A98}\x{8A9A}' .
+'\x{8A9E}\x{8AA0}\x{8AA1}\x{8AA3}\x{8AA4}\x{8AA5}\x{8AA6}\x{8AA8}\x{8AAC}' .
+'\x{8AAD}\x{8AB0}\x{8AB2}\x{8AB9}\x{8ABC}\x{8ABF}\x{8AC2}\x{8AC4}\x{8AC7}' .
+'\x{8ACB}\x{8ACC}\x{8ACD}\x{8ACF}\x{8AD2}\x{8AD6}\x{8ADA}\x{8ADB}\x{8ADC}' .
+'\x{8ADE}\x{8AE0}\x{8AE1}\x{8AE2}\x{8AE4}\x{8AE6}\x{8AE7}\x{8AEB}\x{8AED}' .
+'\x{8AEE}\x{8AF1}\x{8AF3}\x{8AF7}\x{8AF8}\x{8AFA}\x{8AFE}\x{8B00}\x{8B01}' .
+'\x{8B02}\x{8B04}\x{8B07}\x{8B0C}\x{8B0E}\x{8B10}\x{8B14}\x{8B16}\x{8B17}' .
+'\x{8B19}\x{8B1A}\x{8B1B}\x{8B1D}\x{8B20}\x{8B21}\x{8B26}\x{8B28}\x{8B2B}' .
+'\x{8B2C}\x{8B33}\x{8B39}\x{8B3E}\x{8B41}\x{8B49}\x{8B4C}\x{8B4E}\x{8B4F}' .
+'\x{8B56}\x{8B58}\x{8B5A}\x{8B5B}\x{8B5C}\x{8B5F}\x{8B66}\x{8B6B}\x{8B6C}' .
+'\x{8B6F}\x{8B70}\x{8B71}\x{8B72}\x{8B74}\x{8B77}\x{8B7D}\x{8B80}\x{8B83}' .
+'\x{8B8A}\x{8B8C}\x{8B8E}\x{8B90}\x{8B92}\x{8B93}\x{8B96}\x{8B99}\x{8B9A}' .
+'\x{8C37}\x{8C3A}\x{8C3F}\x{8C41}\x{8C46}\x{8C48}\x{8C4A}\x{8C4C}\x{8C4E}' .
+'\x{8C50}\x{8C55}\x{8C5A}\x{8C61}\x{8C62}\x{8C6A}\x{8C6B}\x{8C6C}\x{8C78}' .
+'\x{8C79}\x{8C7A}\x{8C7C}\x{8C82}\x{8C85}\x{8C89}\x{8C8A}\x{8C8C}\x{8C8D}' .
+'\x{8C8E}\x{8C94}\x{8C98}\x{8C9D}\x{8C9E}\x{8CA0}\x{8CA1}\x{8CA2}\x{8CA7}' .
+'\x{8CA8}\x{8CA9}\x{8CAA}\x{8CAB}\x{8CAC}\x{8CAD}\x{8CAE}\x{8CAF}\x{8CB0}' .
+'\x{8CB2}\x{8CB3}\x{8CB4}\x{8CB6}\x{8CB7}\x{8CB8}\x{8CBB}\x{8CBC}\x{8CBD}' .
+'\x{8CBF}\x{8CC0}\x{8CC1}\x{8CC2}\x{8CC3}\x{8CC4}\x{8CC7}\x{8CC8}\x{8CCA}' .
+'\x{8CCD}\x{8CCE}\x{8CD1}\x{8CD3}\x{8CDA}\x{8CDB}\x{8CDC}\x{8CDE}\x{8CE0}' .
+'\x{8CE2}\x{8CE3}\x{8CE4}\x{8CE6}\x{8CEA}\x{8CED}\x{8CFA}\x{8CFB}\x{8CFC}' .
+'\x{8CFD}\x{8D04}\x{8D05}\x{8D07}\x{8D08}\x{8D0A}\x{8D0B}\x{8D0D}\x{8D0F}' .
+'\x{8D10}\x{8D13}\x{8D14}\x{8D16}\x{8D64}\x{8D66}\x{8D67}\x{8D6B}\x{8D6D}' .
+'\x{8D70}\x{8D71}\x{8D73}\x{8D74}\x{8D77}\x{8D81}\x{8D85}\x{8D8A}\x{8D99}' .
+'\x{8DA3}\x{8DA8}\x{8DB3}\x{8DBA}\x{8DBE}\x{8DC2}\x{8DCB}\x{8DCC}\x{8DCF}' .
+'\x{8DD6}\x{8DDA}\x{8DDB}\x{8DDD}\x{8DDF}\x{8DE1}\x{8DE3}\x{8DE8}\x{8DEA}' .
+'\x{8DEB}\x{8DEF}\x{8DF3}\x{8DF5}\x{8DFC}\x{8DFF}\x{8E08}\x{8E09}\x{8E0A}' .
+'\x{8E0F}\x{8E10}\x{8E1D}\x{8E1E}\x{8E1F}\x{8E2A}\x{8E30}\x{8E34}\x{8E35}' .
+'\x{8E42}\x{8E44}\x{8E47}\x{8E48}\x{8E49}\x{8E4A}\x{8E4C}\x{8E50}\x{8E55}' .
+'\x{8E59}\x{8E5F}\x{8E60}\x{8E63}\x{8E64}\x{8E72}\x{8E74}\x{8E76}\x{8E7C}' .
+'\x{8E81}\x{8E84}\x{8E85}\x{8E87}\x{8E8A}\x{8E8B}\x{8E8D}\x{8E91}\x{8E93}' .
+'\x{8E94}\x{8E99}\x{8EA1}\x{8EAA}\x{8EAB}\x{8EAC}\x{8EAF}\x{8EB0}\x{8EB1}' .
+'\x{8EBE}\x{8EC5}\x{8EC6}\x{8EC8}\x{8ECA}\x{8ECB}\x{8ECC}\x{8ECD}\x{8ED2}' .
+'\x{8EDB}\x{8EDF}\x{8EE2}\x{8EE3}\x{8EEB}\x{8EF8}\x{8EFB}\x{8EFC}\x{8EFD}' .
+'\x{8EFE}\x{8F03}\x{8F05}\x{8F09}\x{8F0A}\x{8F0C}\x{8F12}\x{8F13}\x{8F14}' .
+'\x{8F15}\x{8F19}\x{8F1B}\x{8F1C}\x{8F1D}\x{8F1F}\x{8F26}\x{8F29}\x{8F2A}' .
+'\x{8F2F}\x{8F33}\x{8F38}\x{8F39}\x{8F3B}\x{8F3E}\x{8F3F}\x{8F42}\x{8F44}' .
+'\x{8F45}\x{8F46}\x{8F49}\x{8F4C}\x{8F4D}\x{8F4E}\x{8F57}\x{8F5C}\x{8F5F}' .
+'\x{8F61}\x{8F62}\x{8F63}\x{8F64}\x{8F9B}\x{8F9C}\x{8F9E}\x{8F9F}\x{8FA3}' .
+'\x{8FA7}\x{8FA8}\x{8FAD}\x{8FAE}\x{8FAF}\x{8FB0}\x{8FB1}\x{8FB2}\x{8FB7}' .
+'\x{8FBA}\x{8FBB}\x{8FBC}\x{8FBF}\x{8FC2}\x{8FC4}\x{8FC5}\x{8FCE}\x{8FD1}' .
+'\x{8FD4}\x{8FDA}\x{8FE2}\x{8FE5}\x{8FE6}\x{8FE9}\x{8FEA}\x{8FEB}\x{8FED}' .
+'\x{8FEF}\x{8FF0}\x{8FF4}\x{8FF7}\x{8FF8}\x{8FF9}\x{8FFA}\x{8FFD}\x{9000}' .
+'\x{9001}\x{9003}\x{9005}\x{9006}\x{900B}\x{900D}\x{900E}\x{900F}\x{9010}' .
+'\x{9011}\x{9013}\x{9014}\x{9015}\x{9016}\x{9017}\x{9019}\x{901A}\x{901D}' .
+'\x{901E}\x{901F}\x{9020}\x{9021}\x{9022}\x{9023}\x{9027}\x{902E}\x{9031}' .
+'\x{9032}\x{9035}\x{9036}\x{9038}\x{9039}\x{903C}\x{903E}\x{9041}\x{9042}' .
+'\x{9045}\x{9047}\x{9049}\x{904A}\x{904B}\x{904D}\x{904E}\x{904F}\x{9050}' .
+'\x{9051}\x{9052}\x{9053}\x{9054}\x{9055}\x{9056}\x{9058}\x{9059}\x{905C}' .
+'\x{905E}\x{9060}\x{9061}\x{9063}\x{9065}\x{9068}\x{9069}\x{906D}\x{906E}' .
+'\x{906F}\x{9072}\x{9075}\x{9076}\x{9077}\x{9078}\x{907A}\x{907C}\x{907D}' .
+'\x{907F}\x{9080}\x{9081}\x{9082}\x{9083}\x{9084}\x{9087}\x{9089}\x{908A}' .
+'\x{908F}\x{9091}\x{90A3}\x{90A6}\x{90A8}\x{90AA}\x{90AF}\x{90B1}\x{90B5}' .
+'\x{90B8}\x{90C1}\x{90CA}\x{90CE}\x{90DB}\x{90E1}\x{90E2}\x{90E4}\x{90E8}' .
+'\x{90ED}\x{90F5}\x{90F7}\x{90FD}\x{9102}\x{9112}\x{9119}\x{912D}\x{9130}' .
+'\x{9132}\x{9149}\x{914A}\x{914B}\x{914C}\x{914D}\x{914E}\x{9152}\x{9154}' .
+'\x{9156}\x{9158}\x{9162}\x{9163}\x{9165}\x{9169}\x{916A}\x{916C}\x{9172}' .
+'\x{9173}\x{9175}\x{9177}\x{9178}\x{9182}\x{9187}\x{9189}\x{918B}\x{918D}' .
+'\x{9190}\x{9192}\x{9197}\x{919C}\x{91A2}\x{91A4}\x{91AA}\x{91AB}\x{91AF}' .
+'\x{91B4}\x{91B5}\x{91B8}\x{91BA}\x{91C0}\x{91C1}\x{91C6}\x{91C7}\x{91C8}' .
+'\x{91C9}\x{91CB}\x{91CC}\x{91CD}\x{91CE}\x{91CF}\x{91D0}\x{91D1}\x{91D6}' .
+'\x{91D8}\x{91DB}\x{91DC}\x{91DD}\x{91DF}\x{91E1}\x{91E3}\x{91E6}\x{91E7}' .
+'\x{91F5}\x{91F6}\x{91FC}\x{91FF}\x{920D}\x{920E}\x{9211}\x{9214}\x{9215}' .
+'\x{921E}\x{9229}\x{922C}\x{9234}\x{9237}\x{923F}\x{9244}\x{9245}\x{9248}' .
+'\x{9249}\x{924B}\x{9250}\x{9257}\x{925A}\x{925B}\x{925E}\x{9262}\x{9264}' .
+'\x{9266}\x{9271}\x{927E}\x{9280}\x{9283}\x{9285}\x{9291}\x{9293}\x{9295}' .
+'\x{9296}\x{9298}\x{929A}\x{929B}\x{929C}\x{92AD}\x{92B7}\x{92B9}\x{92CF}' .
+'\x{92D2}\x{92E4}\x{92E9}\x{92EA}\x{92ED}\x{92F2}\x{92F3}\x{92F8}\x{92FA}' .
+'\x{92FC}\x{9306}\x{930F}\x{9310}\x{9318}\x{9319}\x{931A}\x{9320}\x{9322}' .
+'\x{9323}\x{9326}\x{9328}\x{932B}\x{932C}\x{932E}\x{932F}\x{9332}\x{9335}' .
+'\x{933A}\x{933B}\x{9344}\x{934B}\x{934D}\x{9354}\x{9356}\x{935B}\x{935C}' .
+'\x{9360}\x{936C}\x{936E}\x{9375}\x{937C}\x{937E}\x{938C}\x{9394}\x{9396}' .
+'\x{9397}\x{939A}\x{93A7}\x{93AC}\x{93AD}\x{93AE}\x{93B0}\x{93B9}\x{93C3}' .
+'\x{93C8}\x{93D0}\x{93D1}\x{93D6}\x{93D7}\x{93D8}\x{93DD}\x{93E1}\x{93E4}' .
+'\x{93E5}\x{93E8}\x{9403}\x{9407}\x{9410}\x{9413}\x{9414}\x{9418}\x{9419}' .
+'\x{941A}\x{9421}\x{942B}\x{9435}\x{9436}\x{9438}\x{943A}\x{9441}\x{9444}' .
+'\x{9451}\x{9452}\x{9453}\x{945A}\x{945B}\x{945E}\x{9460}\x{9462}\x{946A}' .
+'\x{9470}\x{9475}\x{9477}\x{947C}\x{947D}\x{947E}\x{947F}\x{9481}\x{9577}' .
+'\x{9580}\x{9582}\x{9583}\x{9587}\x{9589}\x{958A}\x{958B}\x{958F}\x{9591}' .
+'\x{9593}\x{9594}\x{9596}\x{9598}\x{9599}\x{95A0}\x{95A2}\x{95A3}\x{95A4}' .
+'\x{95A5}\x{95A7}\x{95A8}\x{95AD}\x{95B2}\x{95B9}\x{95BB}\x{95BC}\x{95BE}' .
+'\x{95C3}\x{95C7}\x{95CA}\x{95CC}\x{95CD}\x{95D4}\x{95D5}\x{95D6}\x{95D8}' .
+'\x{95DC}\x{95E1}\x{95E2}\x{95E5}\x{961C}\x{9621}\x{9628}\x{962A}\x{962E}' .
+'\x{962F}\x{9632}\x{963B}\x{963F}\x{9640}\x{9642}\x{9644}\x{964B}\x{964C}' .
+'\x{964D}\x{964F}\x{9650}\x{965B}\x{965C}\x{965D}\x{965E}\x{965F}\x{9662}' .
+'\x{9663}\x{9664}\x{9665}\x{9666}\x{966A}\x{966C}\x{9670}\x{9672}\x{9673}' .
+'\x{9675}\x{9676}\x{9677}\x{9678}\x{967A}\x{967D}\x{9685}\x{9686}\x{9688}' .
+'\x{968A}\x{968B}\x{968D}\x{968E}\x{968F}\x{9694}\x{9695}\x{9697}\x{9698}' .
+'\x{9699}\x{969B}\x{969C}\x{96A0}\x{96A3}\x{96A7}\x{96A8}\x{96AA}\x{96B0}' .
+'\x{96B1}\x{96B2}\x{96B4}\x{96B6}\x{96B7}\x{96B8}\x{96B9}\x{96BB}\x{96BC}' .
+'\x{96C0}\x{96C1}\x{96C4}\x{96C5}\x{96C6}\x{96C7}\x{96C9}\x{96CB}\x{96CC}' .
+'\x{96CD}\x{96CE}\x{96D1}\x{96D5}\x{96D6}\x{96D9}\x{96DB}\x{96DC}\x{96E2}' .
+'\x{96E3}\x{96E8}\x{96EA}\x{96EB}\x{96F0}\x{96F2}\x{96F6}\x{96F7}\x{96F9}' .
+'\x{96FB}\x{9700}\x{9704}\x{9706}\x{9707}\x{9708}\x{970A}\x{970D}\x{970E}' .
+'\x{970F}\x{9711}\x{9713}\x{9716}\x{9719}\x{971C}\x{971E}\x{9724}\x{9727}' .
+'\x{972A}\x{9730}\x{9732}\x{9738}\x{9739}\x{973D}\x{973E}\x{9742}\x{9744}' .
+'\x{9746}\x{9748}\x{9749}\x{9752}\x{9756}\x{9759}\x{975C}\x{975E}\x{9760}' .
+'\x{9761}\x{9762}\x{9764}\x{9766}\x{9768}\x{9769}\x{976B}\x{976D}\x{9771}' .
+'\x{9774}\x{9779}\x{977A}\x{977C}\x{9781}\x{9784}\x{9785}\x{9786}\x{978B}' .
+'\x{978D}\x{978F}\x{9790}\x{9798}\x{979C}\x{97A0}\x{97A3}\x{97A6}\x{97A8}' .
+'\x{97AB}\x{97AD}\x{97B3}\x{97B4}\x{97C3}\x{97C6}\x{97C8}\x{97CB}\x{97D3}' .
+'\x{97DC}\x{97ED}\x{97EE}\x{97F2}\x{97F3}\x{97F5}\x{97F6}\x{97FB}\x{97FF}' .
+'\x{9801}\x{9802}\x{9803}\x{9805}\x{9806}\x{9808}\x{980C}\x{980F}\x{9810}' .
+'\x{9811}\x{9812}\x{9813}\x{9817}\x{9818}\x{981A}\x{9821}\x{9824}\x{982C}' .
+'\x{982D}\x{9834}\x{9837}\x{9838}\x{983B}\x{983C}\x{983D}\x{9846}\x{984B}' .
+'\x{984C}\x{984D}\x{984E}\x{984F}\x{9854}\x{9855}\x{9858}\x{985B}\x{985E}' .
+'\x{9867}\x{986B}\x{986F}\x{9870}\x{9871}\x{9873}\x{9874}\x{98A8}\x{98AA}' .
+'\x{98AF}\x{98B1}\x{98B6}\x{98C3}\x{98C4}\x{98C6}\x{98DB}\x{98DC}\x{98DF}' .
+'\x{98E2}\x{98E9}\x{98EB}\x{98ED}\x{98EE}\x{98EF}\x{98F2}\x{98F4}\x{98FC}' .
+'\x{98FD}\x{98FE}\x{9903}\x{9905}\x{9909}\x{990A}\x{990C}\x{9910}\x{9912}' .
+'\x{9913}\x{9914}\x{9918}\x{991D}\x{991E}\x{9920}\x{9921}\x{9924}\x{9928}' .
+'\x{992C}\x{992E}\x{993D}\x{993E}\x{9942}\x{9945}\x{9949}\x{994B}\x{994C}' .
+'\x{9950}\x{9951}\x{9952}\x{9955}\x{9957}\x{9996}\x{9997}\x{9998}\x{9999}' .
+'\x{99A5}\x{99A8}\x{99AC}\x{99AD}\x{99AE}\x{99B3}\x{99B4}\x{99BC}\x{99C1}' .
+'\x{99C4}\x{99C5}\x{99C6}\x{99C8}\x{99D0}\x{99D1}\x{99D2}\x{99D5}\x{99D8}' .
+'\x{99DB}\x{99DD}\x{99DF}\x{99E2}\x{99ED}\x{99EE}\x{99F1}\x{99F2}\x{99F8}' .
+'\x{99FB}\x{99FF}\x{9A01}\x{9A05}\x{9A0E}\x{9A0F}\x{9A12}\x{9A13}\x{9A19}' .
+'\x{9A28}\x{9A2B}\x{9A30}\x{9A37}\x{9A3E}\x{9A40}\x{9A42}\x{9A43}\x{9A45}' .
+'\x{9A4D}\x{9A55}\x{9A57}\x{9A5A}\x{9A5B}\x{9A5F}\x{9A62}\x{9A64}\x{9A65}' .
+'\x{9A69}\x{9A6A}\x{9A6B}\x{9AA8}\x{9AAD}\x{9AB0}\x{9AB8}\x{9ABC}\x{9AC0}' .
+'\x{9AC4}\x{9ACF}\x{9AD1}\x{9AD3}\x{9AD4}\x{9AD8}\x{9ADE}\x{9ADF}\x{9AE2}' .
+'\x{9AE3}\x{9AE6}\x{9AEA}\x{9AEB}\x{9AED}\x{9AEE}\x{9AEF}\x{9AF1}\x{9AF4}' .
+'\x{9AF7}\x{9AFB}\x{9B06}\x{9B18}\x{9B1A}\x{9B1F}\x{9B22}\x{9B23}\x{9B25}' .
+'\x{9B27}\x{9B28}\x{9B29}\x{9B2A}\x{9B2E}\x{9B2F}\x{9B31}\x{9B32}\x{9B3B}' .
+'\x{9B3C}\x{9B41}\x{9B42}\x{9B43}\x{9B44}\x{9B45}\x{9B4D}\x{9B4E}\x{9B4F}' .
+'\x{9B51}\x{9B54}\x{9B58}\x{9B5A}\x{9B6F}\x{9B74}\x{9B83}\x{9B8E}\x{9B91}' .
+'\x{9B92}\x{9B93}\x{9B96}\x{9B97}\x{9B9F}\x{9BA0}\x{9BA8}\x{9BAA}\x{9BAB}' .
+'\x{9BAD}\x{9BAE}\x{9BB4}\x{9BB9}\x{9BC0}\x{9BC6}\x{9BC9}\x{9BCA}\x{9BCF}' .
+'\x{9BD1}\x{9BD2}\x{9BD4}\x{9BD6}\x{9BDB}\x{9BE1}\x{9BE2}\x{9BE3}\x{9BE4}' .
+'\x{9BE8}\x{9BF0}\x{9BF1}\x{9BF2}\x{9BF5}\x{9C04}\x{9C06}\x{9C08}\x{9C09}' .
+'\x{9C0A}\x{9C0C}\x{9C0D}\x{9C10}\x{9C12}\x{9C13}\x{9C14}\x{9C15}\x{9C1B}' .
+'\x{9C21}\x{9C24}\x{9C25}\x{9C2D}\x{9C2E}\x{9C2F}\x{9C30}\x{9C32}\x{9C39}' .
+'\x{9C3A}\x{9C3B}\x{9C3E}\x{9C46}\x{9C47}\x{9C48}\x{9C52}\x{9C57}\x{9C5A}' .
+'\x{9C60}\x{9C67}\x{9C76}\x{9C78}\x{9CE5}\x{9CE7}\x{9CE9}\x{9CEB}\x{9CEC}' .
+'\x{9CF0}\x{9CF3}\x{9CF4}\x{9CF6}\x{9D03}\x{9D06}\x{9D07}\x{9D08}\x{9D09}' .
+'\x{9D0E}\x{9D12}\x{9D15}\x{9D1B}\x{9D1F}\x{9D23}\x{9D26}\x{9D28}\x{9D2A}' .
+'\x{9D2B}\x{9D2C}\x{9D3B}\x{9D3E}\x{9D3F}\x{9D41}\x{9D44}\x{9D46}\x{9D48}' .
+'\x{9D50}\x{9D51}\x{9D59}\x{9D5C}\x{9D5D}\x{9D5E}\x{9D60}\x{9D61}\x{9D64}' .
+'\x{9D6C}\x{9D6F}\x{9D72}\x{9D7A}\x{9D87}\x{9D89}\x{9D8F}\x{9D9A}\x{9DA4}' .
+'\x{9DA9}\x{9DAB}\x{9DAF}\x{9DB2}\x{9DB4}\x{9DB8}\x{9DBA}\x{9DBB}\x{9DC1}' .
+'\x{9DC2}\x{9DC4}\x{9DC6}\x{9DCF}\x{9DD3}\x{9DD9}\x{9DE6}\x{9DED}\x{9DEF}' .
+'\x{9DF2}\x{9DF8}\x{9DF9}\x{9DFA}\x{9DFD}\x{9E1A}\x{9E1B}\x{9E1E}\x{9E75}' .
+'\x{9E78}\x{9E79}\x{9E7D}\x{9E7F}\x{9E81}\x{9E88}\x{9E8B}\x{9E8C}\x{9E91}' .
+'\x{9E92}\x{9E93}\x{9E95}\x{9E97}\x{9E9D}\x{9E9F}\x{9EA5}\x{9EA6}\x{9EA9}' .
+'\x{9EAA}\x{9EAD}\x{9EB8}\x{9EB9}\x{9EBA}\x{9EBB}\x{9EBC}\x{9EBE}\x{9EBF}' .
+'\x{9EC4}\x{9ECC}\x{9ECD}\x{9ECE}\x{9ECF}\x{9ED0}\x{9ED2}\x{9ED4}\x{9ED8}' .
+'\x{9ED9}\x{9EDB}\x{9EDC}\x{9EDD}\x{9EDE}\x{9EE0}\x{9EE5}\x{9EE8}\x{9EEF}' .
+'\x{9EF4}\x{9EF6}\x{9EF7}\x{9EF9}\x{9EFB}\x{9EFC}\x{9EFD}\x{9F07}\x{9F08}' .
+'\x{9F0E}\x{9F13}\x{9F15}\x{9F20}\x{9F21}\x{9F2C}\x{9F3B}\x{9F3E}\x{9F4A}' .
+'\x{9F4B}\x{9F4E}\x{9F4F}\x{9F52}\x{9F54}\x{9F5F}\x{9F60}\x{9F61}\x{9F62}' .
+'\x{9F63}\x{9F66}\x{9F67}\x{9F6A}\x{9F6C}\x{9F72}\x{9F76}\x{9F77}\x{9F8D}' .
+'\x{9F95}\x{9F9C}\x{9F9D}\x{9FA0}]{1,15}$/iu',
+    12 => '/^[\x{002d}0-9a-z\x{3447}\x{3473}\x{359E}\x{360E}\x{361A}\x{3918}\x{396E}\x{39CF}\x{39D0}' .
+'\x{39DF}\x{3A73}\x{3B4E}\x{3C6E}\x{3CE0}\x{4056}\x{415F}\x{4337}\x{43AC}' .
+'\x{43B1}\x{43DD}\x{44D6}\x{464C}\x{4661}\x{4723}\x{4729}\x{477C}\x{478D}' .
+'\x{4947}\x{497A}\x{497D}\x{4982}\x{4983}\x{4985}\x{4986}\x{499B}\x{499F}' .
+'\x{49B6}\x{49B7}\x{4C77}\x{4C9F}\x{4CA0}\x{4CA1}\x{4CA2}\x{4CA3}\x{4D13}' .
+'\x{4D14}\x{4D15}\x{4D16}\x{4D17}\x{4D18}\x{4D19}\x{4DAE}\x{4E00}\x{4E01}' .
+'\x{4E02}\x{4E03}\x{4E04}\x{4E05}\x{4E06}\x{4E07}\x{4E08}\x{4E09}\x{4E0A}' .
+'\x{4E0B}\x{4E0C}\x{4E0D}\x{4E0E}\x{4E0F}\x{4E10}\x{4E11}\x{4E13}\x{4E14}' .
+'\x{4E15}\x{4E16}\x{4E17}\x{4E18}\x{4E19}\x{4E1A}\x{4E1B}\x{4E1C}\x{4E1D}' .
+'\x{4E1E}\x{4E1F}\x{4E20}\x{4E21}\x{4E22}\x{4E23}\x{4E24}\x{4E25}\x{4E26}' .
+'\x{4E27}\x{4E28}\x{4E2A}\x{4E2B}\x{4E2C}\x{4E2D}\x{4E2E}\x{4E2F}\x{4E30}' .
+'\x{4E31}\x{4E32}\x{4E33}\x{4E34}\x{4E35}\x{4E36}\x{4E37}\x{4E38}\x{4E39}' .
+'\x{4E3A}\x{4E3B}\x{4E3C}\x{4E3D}\x{4E3E}\x{4E3F}\x{4E40}\x{4E41}\x{4E42}' .
+'\x{4E43}\x{4E44}\x{4E45}\x{4E46}\x{4E47}\x{4E48}\x{4E49}\x{4E4A}\x{4E4B}' .
+'\x{4E4C}\x{4E4D}\x{4E4E}\x{4E4F}\x{4E50}\x{4E51}\x{4E52}\x{4E53}\x{4E54}' .
+'\x{4E56}\x{4E57}\x{4E58}\x{4E59}\x{4E5A}\x{4E5B}\x{4E5C}\x{4E5D}\x{4E5E}' .
+'\x{4E5F}\x{4E60}\x{4E61}\x{4E62}\x{4E63}\x{4E64}\x{4E65}\x{4E66}\x{4E67}' .
+'\x{4E69}\x{4E6A}\x{4E6B}\x{4E6C}\x{4E6D}\x{4E6E}\x{4E6F}\x{4E70}\x{4E71}' .
+'\x{4E72}\x{4E73}\x{4E74}\x{4E75}\x{4E76}\x{4E77}\x{4E78}\x{4E7A}\x{4E7B}' .
+'\x{4E7C}\x{4E7D}\x{4E7E}\x{4E7F}\x{4E80}\x{4E81}\x{4E82}\x{4E83}\x{4E84}' .
+'\x{4E85}\x{4E86}\x{4E87}\x{4E88}\x{4E89}\x{4E8B}\x{4E8C}\x{4E8D}\x{4E8E}' .
+'\x{4E8F}\x{4E90}\x{4E91}\x{4E92}\x{4E93}\x{4E94}\x{4E95}\x{4E97}\x{4E98}' .
+'\x{4E99}\x{4E9A}\x{4E9B}\x{4E9C}\x{4E9D}\x{4E9E}\x{4E9F}\x{4EA0}\x{4EA1}' .
+'\x{4EA2}\x{4EA4}\x{4EA5}\x{4EA6}\x{4EA7}\x{4EA8}\x{4EA9}\x{4EAA}\x{4EAB}' .
+'\x{4EAC}\x{4EAD}\x{4EAE}\x{4EAF}\x{4EB0}\x{4EB1}\x{4EB2}\x{4EB3}\x{4EB4}' .
+'\x{4EB5}\x{4EB6}\x{4EB7}\x{4EB8}\x{4EB9}\x{4EBA}\x{4EBB}\x{4EBD}\x{4EBE}' .
+'\x{4EBF}\x{4EC0}\x{4EC1}\x{4EC2}\x{4EC3}\x{4EC4}\x{4EC5}\x{4EC6}\x{4EC7}' .
+'\x{4EC8}\x{4EC9}\x{4ECA}\x{4ECB}\x{4ECD}\x{4ECE}\x{4ECF}\x{4ED0}\x{4ED1}' .
+'\x{4ED2}\x{4ED3}\x{4ED4}\x{4ED5}\x{4ED6}\x{4ED7}\x{4ED8}\x{4ED9}\x{4EDA}' .
+'\x{4EDB}\x{4EDC}\x{4EDD}\x{4EDE}\x{4EDF}\x{4EE0}\x{4EE1}\x{4EE2}\x{4EE3}' .
+'\x{4EE4}\x{4EE5}\x{4EE6}\x{4EE8}\x{4EE9}\x{4EEA}\x{4EEB}\x{4EEC}\x{4EEF}' .
+'\x{4EF0}\x{4EF1}\x{4EF2}\x{4EF3}\x{4EF4}\x{4EF5}\x{4EF6}\x{4EF7}\x{4EFB}' .
+'\x{4EFD}\x{4EFF}\x{4F00}\x{4F01}\x{4F02}\x{4F03}\x{4F04}\x{4F05}\x{4F06}' .
+'\x{4F08}\x{4F09}\x{4F0A}\x{4F0B}\x{4F0C}\x{4F0D}\x{4F0E}\x{4F0F}\x{4F10}' .
+'\x{4F11}\x{4F12}\x{4F13}\x{4F14}\x{4F15}\x{4F17}\x{4F18}\x{4F19}\x{4F1A}' .
+'\x{4F1B}\x{4F1C}\x{4F1D}\x{4F1E}\x{4F1F}\x{4F20}\x{4F21}\x{4F22}\x{4F23}' .
+'\x{4F24}\x{4F25}\x{4F26}\x{4F27}\x{4F29}\x{4F2A}\x{4F2B}\x{4F2C}\x{4F2D}' .
+'\x{4F2E}\x{4F2F}\x{4F30}\x{4F32}\x{4F33}\x{4F34}\x{4F36}\x{4F38}\x{4F39}' .
+'\x{4F3A}\x{4F3B}\x{4F3C}\x{4F3D}\x{4F3E}\x{4F3F}\x{4F41}\x{4F42}\x{4F43}' .
+'\x{4F45}\x{4F46}\x{4F47}\x{4F48}\x{4F49}\x{4F4A}\x{4F4B}\x{4F4C}\x{4F4D}' .
+'\x{4F4E}\x{4F4F}\x{4F50}\x{4F51}\x{4F52}\x{4F53}\x{4F54}\x{4F55}\x{4F56}' .
+'\x{4F57}\x{4F58}\x{4F59}\x{4F5A}\x{4F5B}\x{4F5C}\x{4F5D}\x{4F5E}\x{4F5F}' .
+'\x{4F60}\x{4F61}\x{4F62}\x{4F63}\x{4F64}\x{4F65}\x{4F66}\x{4F67}\x{4F68}' .
+'\x{4F69}\x{4F6A}\x{4F6B}\x{4F6C}\x{4F6D}\x{4F6E}\x{4F6F}\x{4F70}\x{4F72}' .
+'\x{4F73}\x{4F74}\x{4F75}\x{4F76}\x{4F77}\x{4F78}\x{4F79}\x{4F7A}\x{4F7B}' .
+'\x{4F7C}\x{4F7D}\x{4F7E}\x{4F7F}\x{4F80}\x{4F81}\x{4F82}\x{4F83}\x{4F84}' .
+'\x{4F85}\x{4F86}\x{4F87}\x{4F88}\x{4F89}\x{4F8A}\x{4F8B}\x{4F8D}\x{4F8F}' .
+'\x{4F90}\x{4F91}\x{4F92}\x{4F93}\x{4F94}\x{4F95}\x{4F96}\x{4F97}\x{4F98}' .
+'\x{4F99}\x{4F9A}\x{4F9B}\x{4F9C}\x{4F9D}\x{4F9E}\x{4F9F}\x{4FA0}\x{4FA1}' .
+'\x{4FA3}\x{4FA4}\x{4FA5}\x{4FA6}\x{4FA7}\x{4FA8}\x{4FA9}\x{4FAA}\x{4FAB}' .
+'\x{4FAC}\x{4FAE}\x{4FAF}\x{4FB0}\x{4FB1}\x{4FB2}\x{4FB3}\x{4FB4}\x{4FB5}' .
+'\x{4FB6}\x{4FB7}\x{4FB8}\x{4FB9}\x{4FBA}\x{4FBB}\x{4FBC}\x{4FBE}\x{4FBF}' .
+'\x{4FC0}\x{4FC1}\x{4FC2}\x{4FC3}\x{4FC4}\x{4FC5}\x{4FC7}\x{4FC9}\x{4FCA}' .
+'\x{4FCB}\x{4FCD}\x{4FCE}\x{4FCF}\x{4FD0}\x{4FD1}\x{4FD2}\x{4FD3}\x{4FD4}' .
+'\x{4FD5}\x{4FD6}\x{4FD7}\x{4FD8}\x{4FD9}\x{4FDA}\x{4FDB}\x{4FDC}\x{4FDD}' .
+'\x{4FDE}\x{4FDF}\x{4FE0}\x{4FE1}\x{4FE3}\x{4FE4}\x{4FE5}\x{4FE6}\x{4FE7}' .
+'\x{4FE8}\x{4FE9}\x{4FEA}\x{4FEB}\x{4FEC}\x{4FED}\x{4FEE}\x{4FEF}\x{4FF0}' .
+'\x{4FF1}\x{4FF2}\x{4FF3}\x{4FF4}\x{4FF5}\x{4FF6}\x{4FF7}\x{4FF8}\x{4FF9}' .
+'\x{4FFA}\x{4FFB}\x{4FFE}\x{4FFF}\x{5000}\x{5001}\x{5002}\x{5003}\x{5004}' .
+'\x{5005}\x{5006}\x{5007}\x{5008}\x{5009}\x{500A}\x{500B}\x{500C}\x{500D}' .
+'\x{500E}\x{500F}\x{5011}\x{5012}\x{5013}\x{5014}\x{5015}\x{5016}\x{5017}' .
+'\x{5018}\x{5019}\x{501A}\x{501B}\x{501C}\x{501D}\x{501E}\x{501F}\x{5020}' .
+'\x{5021}\x{5022}\x{5023}\x{5024}\x{5025}\x{5026}\x{5027}\x{5028}\x{5029}' .
+'\x{502A}\x{502B}\x{502C}\x{502D}\x{502E}\x{502F}\x{5030}\x{5031}\x{5032}' .
+'\x{5033}\x{5035}\x{5036}\x{5037}\x{5039}\x{503A}\x{503B}\x{503C}\x{503E}' .
+'\x{503F}\x{5040}\x{5041}\x{5043}\x{5044}\x{5045}\x{5046}\x{5047}\x{5048}' .
+'\x{5049}\x{504A}\x{504B}\x{504C}\x{504D}\x{504E}\x{504F}\x{5051}\x{5053}' .
+'\x{5054}\x{5055}\x{5056}\x{5057}\x{5059}\x{505A}\x{505B}\x{505C}\x{505D}' .
+'\x{505E}\x{505F}\x{5060}\x{5061}\x{5062}\x{5063}\x{5064}\x{5065}\x{5066}' .
+'\x{5067}\x{5068}\x{5069}\x{506A}\x{506B}\x{506C}\x{506D}\x{506E}\x{506F}' .
+'\x{5070}\x{5071}\x{5072}\x{5073}\x{5074}\x{5075}\x{5076}\x{5077}\x{5078}' .
+'\x{5079}\x{507A}\x{507B}\x{507D}\x{507E}\x{507F}\x{5080}\x{5082}\x{5083}' .
+'\x{5084}\x{5085}\x{5086}\x{5087}\x{5088}\x{5089}\x{508A}\x{508B}\x{508C}' .
+'\x{508D}\x{508E}\x{508F}\x{5090}\x{5091}\x{5092}\x{5094}\x{5095}\x{5096}' .
+'\x{5098}\x{5099}\x{509A}\x{509B}\x{509C}\x{509D}\x{509E}\x{50A2}\x{50A3}' .
+'\x{50A4}\x{50A5}\x{50A6}\x{50A7}\x{50A8}\x{50A9}\x{50AA}\x{50AB}\x{50AC}' .
+'\x{50AD}\x{50AE}\x{50AF}\x{50B0}\x{50B1}\x{50B2}\x{50B3}\x{50B4}\x{50B5}' .
+'\x{50B6}\x{50B7}\x{50B8}\x{50BA}\x{50BB}\x{50BC}\x{50BD}\x{50BE}\x{50BF}' .
+'\x{50C0}\x{50C1}\x{50C2}\x{50C4}\x{50C5}\x{50C6}\x{50C7}\x{50C8}\x{50C9}' .
+'\x{50CA}\x{50CB}\x{50CC}\x{50CD}\x{50CE}\x{50CF}\x{50D0}\x{50D1}\x{50D2}' .
+'\x{50D3}\x{50D4}\x{50D5}\x{50D6}\x{50D7}\x{50D9}\x{50DA}\x{50DB}\x{50DC}' .
+'\x{50DD}\x{50DE}\x{50E0}\x{50E3}\x{50E4}\x{50E5}\x{50E6}\x{50E7}\x{50E8}' .
+'\x{50E9}\x{50EA}\x{50EC}\x{50ED}\x{50EE}\x{50EF}\x{50F0}\x{50F1}\x{50F2}' .
+'\x{50F3}\x{50F5}\x{50F6}\x{50F8}\x{50F9}\x{50FA}\x{50FB}\x{50FC}\x{50FD}' .
+'\x{50FE}\x{50FF}\x{5100}\x{5101}\x{5102}\x{5103}\x{5104}\x{5105}\x{5106}' .
+'\x{5107}\x{5108}\x{5109}\x{510A}\x{510B}\x{510C}\x{510D}\x{510E}\x{510F}' .
+'\x{5110}\x{5111}\x{5112}\x{5113}\x{5114}\x{5115}\x{5116}\x{5117}\x{5118}' .
+'\x{5119}\x{511A}\x{511C}\x{511D}\x{511E}\x{511F}\x{5120}\x{5121}\x{5122}' .
+'\x{5123}\x{5124}\x{5125}\x{5126}\x{5127}\x{5129}\x{512A}\x{512C}\x{512D}' .
+'\x{512E}\x{512F}\x{5130}\x{5131}\x{5132}\x{5133}\x{5134}\x{5135}\x{5136}' .
+'\x{5137}\x{5138}\x{5139}\x{513A}\x{513B}\x{513C}\x{513D}\x{513E}\x{513F}' .
+'\x{5140}\x{5141}\x{5143}\x{5144}\x{5145}\x{5146}\x{5147}\x{5148}\x{5149}' .
+'\x{514B}\x{514C}\x{514D}\x{514E}\x{5150}\x{5151}\x{5152}\x{5154}\x{5155}' .
+'\x{5156}\x{5157}\x{5159}\x{515A}\x{515B}\x{515C}\x{515D}\x{515E}\x{515F}' .
+'\x{5161}\x{5162}\x{5163}\x{5165}\x{5166}\x{5167}\x{5168}\x{5169}\x{516A}' .
+'\x{516B}\x{516C}\x{516D}\x{516E}\x{516F}\x{5170}\x{5171}\x{5173}\x{5174}' .
+'\x{5175}\x{5176}\x{5177}\x{5178}\x{5179}\x{517A}\x{517B}\x{517C}\x{517D}' .
+'\x{517F}\x{5180}\x{5181}\x{5182}\x{5185}\x{5186}\x{5187}\x{5188}\x{5189}' .
+'\x{518A}\x{518B}\x{518C}\x{518D}\x{518F}\x{5190}\x{5191}\x{5192}\x{5193}' .
+'\x{5194}\x{5195}\x{5196}\x{5197}\x{5198}\x{5199}\x{519A}\x{519B}\x{519C}' .
+'\x{519D}\x{519E}\x{519F}\x{51A0}\x{51A2}\x{51A4}\x{51A5}\x{51A6}\x{51A7}' .
+'\x{51A8}\x{51AA}\x{51AB}\x{51AC}\x{51AE}\x{51AF}\x{51B0}\x{51B1}\x{51B2}' .
+'\x{51B3}\x{51B5}\x{51B6}\x{51B7}\x{51B9}\x{51BB}\x{51BC}\x{51BD}\x{51BE}' .
+'\x{51BF}\x{51C0}\x{51C1}\x{51C3}\x{51C4}\x{51C5}\x{51C6}\x{51C7}\x{51C8}' .
+'\x{51C9}\x{51CA}\x{51CB}\x{51CC}\x{51CD}\x{51CE}\x{51CF}\x{51D0}\x{51D1}' .
+'\x{51D4}\x{51D5}\x{51D6}\x{51D7}\x{51D8}\x{51D9}\x{51DA}\x{51DB}\x{51DC}' .
+'\x{51DD}\x{51DE}\x{51E0}\x{51E1}\x{51E2}\x{51E3}\x{51E4}\x{51E5}\x{51E7}' .
+'\x{51E8}\x{51E9}\x{51EA}\x{51EB}\x{51ED}\x{51EF}\x{51F0}\x{51F1}\x{51F3}' .
+'\x{51F4}\x{51F5}\x{51F6}\x{51F7}\x{51F8}\x{51F9}\x{51FA}\x{51FB}\x{51FC}' .
+'\x{51FD}\x{51FE}\x{51FF}\x{5200}\x{5201}\x{5202}\x{5203}\x{5204}\x{5205}' .
+'\x{5206}\x{5207}\x{5208}\x{5209}\x{520A}\x{520B}\x{520C}\x{520D}\x{520E}' .
+'\x{520F}\x{5210}\x{5211}\x{5212}\x{5213}\x{5214}\x{5215}\x{5216}\x{5217}' .
+'\x{5218}\x{5219}\x{521A}\x{521B}\x{521C}\x{521D}\x{521E}\x{521F}\x{5220}' .
+'\x{5221}\x{5222}\x{5223}\x{5224}\x{5225}\x{5226}\x{5228}\x{5229}\x{522A}' .
+'\x{522B}\x{522C}\x{522D}\x{522E}\x{522F}\x{5230}\x{5231}\x{5232}\x{5233}' .
+'\x{5234}\x{5235}\x{5236}\x{5237}\x{5238}\x{5239}\x{523A}\x{523B}\x{523C}' .
+'\x{523D}\x{523E}\x{523F}\x{5240}\x{5241}\x{5242}\x{5243}\x{5244}\x{5245}' .
+'\x{5246}\x{5247}\x{5248}\x{5249}\x{524A}\x{524B}\x{524C}\x{524D}\x{524E}' .
+'\x{5250}\x{5251}\x{5252}\x{5254}\x{5255}\x{5256}\x{5257}\x{5258}\x{5259}' .
+'\x{525A}\x{525B}\x{525C}\x{525D}\x{525E}\x{525F}\x{5260}\x{5261}\x{5262}' .
+'\x{5263}\x{5264}\x{5265}\x{5267}\x{5268}\x{5269}\x{526A}\x{526B}\x{526C}' .
+'\x{526D}\x{526E}\x{526F}\x{5270}\x{5272}\x{5273}\x{5274}\x{5275}\x{5276}' .
+'\x{5277}\x{5278}\x{527A}\x{527B}\x{527C}\x{527D}\x{527E}\x{527F}\x{5280}' .
+'\x{5281}\x{5282}\x{5283}\x{5284}\x{5286}\x{5287}\x{5288}\x{5289}\x{528A}' .
+'\x{528B}\x{528C}\x{528D}\x{528F}\x{5290}\x{5291}\x{5292}\x{5293}\x{5294}' .
+'\x{5295}\x{5296}\x{5297}\x{5298}\x{5299}\x{529A}\x{529B}\x{529C}\x{529D}' .
+'\x{529E}\x{529F}\x{52A0}\x{52A1}\x{52A2}\x{52A3}\x{52A5}\x{52A6}\x{52A7}' .
+'\x{52A8}\x{52A9}\x{52AA}\x{52AB}\x{52AC}\x{52AD}\x{52AE}\x{52AF}\x{52B0}' .
+'\x{52B1}\x{52B2}\x{52B3}\x{52B4}\x{52B5}\x{52B6}\x{52B7}\x{52B8}\x{52B9}' .
+'\x{52BA}\x{52BB}\x{52BC}\x{52BD}\x{52BE}\x{52BF}\x{52C0}\x{52C1}\x{52C2}' .
+'\x{52C3}\x{52C6}\x{52C7}\x{52C9}\x{52CA}\x{52CB}\x{52CD}\x{52CF}\x{52D0}' .
+'\x{52D2}\x{52D3}\x{52D5}\x{52D6}\x{52D7}\x{52D8}\x{52D9}\x{52DA}\x{52DB}' .
+'\x{52DC}\x{52DD}\x{52DE}\x{52DF}\x{52E0}\x{52E2}\x{52E3}\x{52E4}\x{52E6}' .
+'\x{52E7}\x{52E8}\x{52E9}\x{52EA}\x{52EB}\x{52EC}\x{52ED}\x{52EF}\x{52F0}' .
+'\x{52F1}\x{52F2}\x{52F3}\x{52F4}\x{52F5}\x{52F6}\x{52F7}\x{52F8}\x{52F9}' .
+'\x{52FA}\x{52FB}\x{52FC}\x{52FD}\x{52FE}\x{52FF}\x{5300}\x{5301}\x{5302}' .
+'\x{5305}\x{5306}\x{5307}\x{5308}\x{5309}\x{530A}\x{530B}\x{530C}\x{530D}' .
+'\x{530E}\x{530F}\x{5310}\x{5311}\x{5312}\x{5313}\x{5314}\x{5315}\x{5316}' .
+'\x{5317}\x{5319}\x{531A}\x{531C}\x{531D}\x{531F}\x{5320}\x{5321}\x{5322}' .
+'\x{5323}\x{5324}\x{5325}\x{5326}\x{5328}\x{532A}\x{532B}\x{532C}\x{532D}' .
+'\x{532E}\x{532F}\x{5330}\x{5331}\x{5333}\x{5334}\x{5337}\x{5339}\x{533A}' .
+'\x{533B}\x{533C}\x{533D}\x{533E}\x{533F}\x{5340}\x{5341}\x{5343}\x{5344}' .
+'\x{5345}\x{5346}\x{5347}\x{5348}\x{5349}\x{534A}\x{534B}\x{534C}\x{534D}' .
+'\x{534E}\x{534F}\x{5350}\x{5351}\x{5352}\x{5353}\x{5354}\x{5355}\x{5356}' .
+'\x{5357}\x{5358}\x{5359}\x{535A}\x{535C}\x{535E}\x{535F}\x{5360}\x{5361}' .
+'\x{5362}\x{5363}\x{5364}\x{5365}\x{5366}\x{5367}\x{5369}\x{536B}\x{536C}' .
+'\x{536E}\x{536F}\x{5370}\x{5371}\x{5372}\x{5373}\x{5374}\x{5375}\x{5376}' .
+'\x{5377}\x{5378}\x{5379}\x{537A}\x{537B}\x{537C}\x{537D}\x{537E}\x{537F}' .
+'\x{5381}\x{5382}\x{5383}\x{5384}\x{5385}\x{5386}\x{5387}\x{5388}\x{5389}' .
+'\x{538A}\x{538B}\x{538C}\x{538D}\x{538E}\x{538F}\x{5390}\x{5391}\x{5392}' .
+'\x{5393}\x{5394}\x{5395}\x{5396}\x{5397}\x{5398}\x{5399}\x{539A}\x{539B}' .
+'\x{539C}\x{539D}\x{539E}\x{539F}\x{53A0}\x{53A2}\x{53A3}\x{53A4}\x{53A5}' .
+'\x{53A6}\x{53A7}\x{53A8}\x{53A9}\x{53AC}\x{53AD}\x{53AE}\x{53B0}\x{53B1}' .
+'\x{53B2}\x{53B3}\x{53B4}\x{53B5}\x{53B6}\x{53B7}\x{53B8}\x{53B9}\x{53BB}' .
+'\x{53BC}\x{53BD}\x{53BE}\x{53BF}\x{53C0}\x{53C1}\x{53C2}\x{53C3}\x{53C4}' .
+'\x{53C6}\x{53C7}\x{53C8}\x{53C9}\x{53CA}\x{53CB}\x{53CC}\x{53CD}\x{53CE}' .
+'\x{53D0}\x{53D1}\x{53D2}\x{53D3}\x{53D4}\x{53D5}\x{53D6}\x{53D7}\x{53D8}' .
+'\x{53D9}\x{53DB}\x{53DC}\x{53DF}\x{53E0}\x{53E1}\x{53E2}\x{53E3}\x{53E4}' .
+'\x{53E5}\x{53E6}\x{53E8}\x{53E9}\x{53EA}\x{53EB}\x{53EC}\x{53ED}\x{53EE}' .
+'\x{53EF}\x{53F0}\x{53F1}\x{53F2}\x{53F3}\x{53F4}\x{53F5}\x{53F6}\x{53F7}' .
+'\x{53F8}\x{53F9}\x{53FA}\x{53FB}\x{53FC}\x{53FD}\x{53FE}\x{5401}\x{5402}' .
+'\x{5403}\x{5404}\x{5405}\x{5406}\x{5407}\x{5408}\x{5409}\x{540A}\x{540B}' .
+'\x{540C}\x{540D}\x{540E}\x{540F}\x{5410}\x{5411}\x{5412}\x{5413}\x{5414}' .
+'\x{5415}\x{5416}\x{5417}\x{5418}\x{5419}\x{541B}\x{541C}\x{541D}\x{541E}' .
+'\x{541F}\x{5420}\x{5421}\x{5423}\x{5424}\x{5425}\x{5426}\x{5427}\x{5428}' .
+'\x{5429}\x{542A}\x{542B}\x{542C}\x{542D}\x{542E}\x{542F}\x{5430}\x{5431}' .
+'\x{5432}\x{5433}\x{5434}\x{5435}\x{5436}\x{5437}\x{5438}\x{5439}\x{543A}' .
+'\x{543B}\x{543C}\x{543D}\x{543E}\x{543F}\x{5440}\x{5441}\x{5442}\x{5443}' .
+'\x{5444}\x{5445}\x{5446}\x{5447}\x{5448}\x{5449}\x{544A}\x{544B}\x{544D}' .
+'\x{544E}\x{544F}\x{5450}\x{5451}\x{5452}\x{5453}\x{5454}\x{5455}\x{5456}' .
+'\x{5457}\x{5458}\x{5459}\x{545A}\x{545B}\x{545C}\x{545E}\x{545F}\x{5460}' .
+'\x{5461}\x{5462}\x{5463}\x{5464}\x{5465}\x{5466}\x{5467}\x{5468}\x{546A}' .
+'\x{546B}\x{546C}\x{546D}\x{546E}\x{546F}\x{5470}\x{5471}\x{5472}\x{5473}' .
+'\x{5474}\x{5475}\x{5476}\x{5477}\x{5478}\x{5479}\x{547A}\x{547B}\x{547C}' .
+'\x{547D}\x{547E}\x{547F}\x{5480}\x{5481}\x{5482}\x{5483}\x{5484}\x{5485}' .
+'\x{5486}\x{5487}\x{5488}\x{5489}\x{548B}\x{548C}\x{548D}\x{548E}\x{548F}' .
+'\x{5490}\x{5491}\x{5492}\x{5493}\x{5494}\x{5495}\x{5496}\x{5497}\x{5498}' .
+'\x{5499}\x{549A}\x{549B}\x{549C}\x{549D}\x{549E}\x{549F}\x{54A0}\x{54A1}' .
+'\x{54A2}\x{54A3}\x{54A4}\x{54A5}\x{54A6}\x{54A7}\x{54A8}\x{54A9}\x{54AA}' .
+'\x{54AB}\x{54AC}\x{54AD}\x{54AE}\x{54AF}\x{54B0}\x{54B1}\x{54B2}\x{54B3}' .
+'\x{54B4}\x{54B6}\x{54B7}\x{54B8}\x{54B9}\x{54BA}\x{54BB}\x{54BC}\x{54BD}' .
+'\x{54BE}\x{54BF}\x{54C0}\x{54C1}\x{54C2}\x{54C3}\x{54C4}\x{54C5}\x{54C6}' .
+'\x{54C7}\x{54C8}\x{54C9}\x{54CA}\x{54CB}\x{54CC}\x{54CD}\x{54CE}\x{54CF}' .
+'\x{54D0}\x{54D1}\x{54D2}\x{54D3}\x{54D4}\x{54D5}\x{54D6}\x{54D7}\x{54D8}' .
+'\x{54D9}\x{54DA}\x{54DB}\x{54DC}\x{54DD}\x{54DE}\x{54DF}\x{54E0}\x{54E1}' .
+'\x{54E2}\x{54E3}\x{54E4}\x{54E5}\x{54E6}\x{54E7}\x{54E8}\x{54E9}\x{54EA}' .
+'\x{54EB}\x{54EC}\x{54ED}\x{54EE}\x{54EF}\x{54F0}\x{54F1}\x{54F2}\x{54F3}' .
+'\x{54F4}\x{54F5}\x{54F7}\x{54F8}\x{54F9}\x{54FA}\x{54FB}\x{54FC}\x{54FD}' .
+'\x{54FE}\x{54FF}\x{5500}\x{5501}\x{5502}\x{5503}\x{5504}\x{5505}\x{5506}' .
+'\x{5507}\x{5508}\x{5509}\x{550A}\x{550B}\x{550C}\x{550D}\x{550E}\x{550F}' .
+'\x{5510}\x{5511}\x{5512}\x{5513}\x{5514}\x{5516}\x{5517}\x{551A}\x{551B}' .
+'\x{551C}\x{551D}\x{551E}\x{551F}\x{5520}\x{5521}\x{5522}\x{5523}\x{5524}' .
+'\x{5525}\x{5526}\x{5527}\x{5528}\x{5529}\x{552A}\x{552B}\x{552C}\x{552D}' .
+'\x{552E}\x{552F}\x{5530}\x{5531}\x{5532}\x{5533}\x{5534}\x{5535}\x{5536}' .
+'\x{5537}\x{5538}\x{5539}\x{553A}\x{553B}\x{553C}\x{553D}\x{553E}\x{553F}' .
+'\x{5540}\x{5541}\x{5542}\x{5543}\x{5544}\x{5545}\x{5546}\x{5548}\x{5549}' .
+'\x{554A}\x{554B}\x{554C}\x{554D}\x{554E}\x{554F}\x{5550}\x{5551}\x{5552}' .
+'\x{5553}\x{5554}\x{5555}\x{5556}\x{5557}\x{5558}\x{5559}\x{555A}\x{555B}' .
+'\x{555C}\x{555D}\x{555E}\x{555F}\x{5561}\x{5562}\x{5563}\x{5564}\x{5565}' .
+'\x{5566}\x{5567}\x{5568}\x{5569}\x{556A}\x{556B}\x{556C}\x{556D}\x{556E}' .
+'\x{556F}\x{5570}\x{5571}\x{5572}\x{5573}\x{5574}\x{5575}\x{5576}\x{5577}' .
+'\x{5578}\x{5579}\x{557B}\x{557C}\x{557D}\x{557E}\x{557F}\x{5580}\x{5581}' .
+'\x{5582}\x{5583}\x{5584}\x{5585}\x{5586}\x{5587}\x{5588}\x{5589}\x{558A}' .
+'\x{558B}\x{558C}\x{558D}\x{558E}\x{558F}\x{5590}\x{5591}\x{5592}\x{5593}' .
+'\x{5594}\x{5595}\x{5596}\x{5597}\x{5598}\x{5599}\x{559A}\x{559B}\x{559C}' .
+'\x{559D}\x{559E}\x{559F}\x{55A0}\x{55A1}\x{55A2}\x{55A3}\x{55A4}\x{55A5}' .
+'\x{55A6}\x{55A7}\x{55A8}\x{55A9}\x{55AA}\x{55AB}\x{55AC}\x{55AD}\x{55AE}' .
+'\x{55AF}\x{55B0}\x{55B1}\x{55B2}\x{55B3}\x{55B4}\x{55B5}\x{55B6}\x{55B7}' .
+'\x{55B8}\x{55B9}\x{55BA}\x{55BB}\x{55BC}\x{55BD}\x{55BE}\x{55BF}\x{55C0}' .
+'\x{55C1}\x{55C2}\x{55C3}\x{55C4}\x{55C5}\x{55C6}\x{55C7}\x{55C8}\x{55C9}' .
+'\x{55CA}\x{55CB}\x{55CC}\x{55CD}\x{55CE}\x{55CF}\x{55D0}\x{55D1}\x{55D2}' .
+'\x{55D3}\x{55D4}\x{55D5}\x{55D6}\x{55D7}\x{55D8}\x{55D9}\x{55DA}\x{55DB}' .
+'\x{55DC}\x{55DD}\x{55DE}\x{55DF}\x{55E1}\x{55E2}\x{55E3}\x{55E4}\x{55E5}' .
+'\x{55E6}\x{55E7}\x{55E8}\x{55E9}\x{55EA}\x{55EB}\x{55EC}\x{55ED}\x{55EE}' .
+'\x{55EF}\x{55F0}\x{55F1}\x{55F2}\x{55F3}\x{55F4}\x{55F5}\x{55F6}\x{55F7}' .
+'\x{55F9}\x{55FA}\x{55FB}\x{55FC}\x{55FD}\x{55FE}\x{55FF}\x{5600}\x{5601}' .
+'\x{5602}\x{5603}\x{5604}\x{5606}\x{5607}\x{5608}\x{5609}\x{560C}\x{560D}' .
+'\x{560E}\x{560F}\x{5610}\x{5611}\x{5612}\x{5613}\x{5614}\x{5615}\x{5616}' .
+'\x{5617}\x{5618}\x{5619}\x{561A}\x{561B}\x{561C}\x{561D}\x{561E}\x{561F}' .
+'\x{5621}\x{5622}\x{5623}\x{5624}\x{5625}\x{5626}\x{5627}\x{5628}\x{5629}' .
+'\x{562A}\x{562C}\x{562D}\x{562E}\x{562F}\x{5630}\x{5631}\x{5632}\x{5633}' .
+'\x{5634}\x{5635}\x{5636}\x{5638}\x{5639}\x{563A}\x{563B}\x{563D}\x{563E}' .
+'\x{563F}\x{5640}\x{5641}\x{5642}\x{5643}\x{5645}\x{5646}\x{5647}\x{5648}' .
+'\x{5649}\x{564A}\x{564C}\x{564D}\x{564E}\x{564F}\x{5650}\x{5652}\x{5653}' .
+'\x{5654}\x{5655}\x{5657}\x{5658}\x{5659}\x{565A}\x{565B}\x{565C}\x{565D}' .
+'\x{565E}\x{5660}\x{5662}\x{5663}\x{5664}\x{5665}\x{5666}\x{5667}\x{5668}' .
+'\x{5669}\x{566A}\x{566B}\x{566C}\x{566D}\x{566E}\x{566F}\x{5670}\x{5671}' .
+'\x{5672}\x{5673}\x{5674}\x{5676}\x{5677}\x{5678}\x{5679}\x{567A}\x{567B}' .
+'\x{567C}\x{567E}\x{567F}\x{5680}\x{5681}\x{5682}\x{5683}\x{5684}\x{5685}' .
+'\x{5686}\x{5687}\x{568A}\x{568C}\x{568D}\x{568E}\x{568F}\x{5690}\x{5691}' .
+'\x{5692}\x{5693}\x{5694}\x{5695}\x{5697}\x{5698}\x{5699}\x{569A}\x{569B}' .
+'\x{569C}\x{569D}\x{569F}\x{56A0}\x{56A1}\x{56A3}\x{56A4}\x{56A5}\x{56A6}' .
+'\x{56A7}\x{56A8}\x{56A9}\x{56AA}\x{56AB}\x{56AC}\x{56AD}\x{56AE}\x{56AF}' .
+'\x{56B0}\x{56B1}\x{56B2}\x{56B3}\x{56B4}\x{56B5}\x{56B6}\x{56B7}\x{56B8}' .
+'\x{56B9}\x{56BB}\x{56BC}\x{56BD}\x{56BE}\x{56BF}\x{56C0}\x{56C1}\x{56C2}' .
+'\x{56C3}\x{56C4}\x{56C5}\x{56C6}\x{56C7}\x{56C8}\x{56C9}\x{56CA}\x{56CB}' .
+'\x{56CC}\x{56CD}\x{56CE}\x{56D0}\x{56D1}\x{56D2}\x{56D3}\x{56D4}\x{56D5}' .
+'\x{56D6}\x{56D7}\x{56D8}\x{56DA}\x{56DB}\x{56DC}\x{56DD}\x{56DE}\x{56DF}' .
+'\x{56E0}\x{56E1}\x{56E2}\x{56E3}\x{56E4}\x{56E5}\x{56E7}\x{56E8}\x{56E9}' .
+'\x{56EA}\x{56EB}\x{56EC}\x{56ED}\x{56EE}\x{56EF}\x{56F0}\x{56F1}\x{56F2}' .
+'\x{56F3}\x{56F4}\x{56F5}\x{56F7}\x{56F9}\x{56FA}\x{56FD}\x{56FE}\x{56FF}' .
+'\x{5700}\x{5701}\x{5702}\x{5703}\x{5704}\x{5706}\x{5707}\x{5708}\x{5709}' .
+'\x{570A}\x{570B}\x{570C}\x{570D}\x{570E}\x{570F}\x{5710}\x{5712}\x{5713}' .
+'\x{5714}\x{5715}\x{5716}\x{5718}\x{5719}\x{571A}\x{571B}\x{571C}\x{571D}' .
+'\x{571E}\x{571F}\x{5720}\x{5722}\x{5723}\x{5725}\x{5726}\x{5727}\x{5728}' .
+'\x{5729}\x{572A}\x{572B}\x{572C}\x{572D}\x{572E}\x{572F}\x{5730}\x{5731}' .
+'\x{5732}\x{5733}\x{5734}\x{5735}\x{5736}\x{5737}\x{5738}\x{5739}\x{573A}' .
+'\x{573B}\x{573C}\x{573E}\x{573F}\x{5740}\x{5741}\x{5742}\x{5744}\x{5745}' .
+'\x{5746}\x{5747}\x{5749}\x{574A}\x{574B}\x{574C}\x{574D}\x{574E}\x{574F}' .
+'\x{5750}\x{5751}\x{5752}\x{5753}\x{5754}\x{5757}\x{5759}\x{575A}\x{575B}' .
+'\x{575C}\x{575D}\x{575E}\x{575F}\x{5760}\x{5761}\x{5762}\x{5764}\x{5765}' .
+'\x{5766}\x{5767}\x{5768}\x{5769}\x{576A}\x{576B}\x{576C}\x{576D}\x{576F}' .
+'\x{5770}\x{5771}\x{5772}\x{5773}\x{5774}\x{5775}\x{5776}\x{5777}\x{5779}' .
+'\x{577A}\x{577B}\x{577C}\x{577D}\x{577E}\x{577F}\x{5780}\x{5782}\x{5783}' .
+'\x{5784}\x{5785}\x{5786}\x{5788}\x{5789}\x{578A}\x{578B}\x{578C}\x{578D}' .
+'\x{578E}\x{578F}\x{5790}\x{5791}\x{5792}\x{5793}\x{5794}\x{5795}\x{5797}' .
+'\x{5798}\x{5799}\x{579A}\x{579B}\x{579C}\x{579D}\x{579E}\x{579F}\x{57A0}' .
+'\x{57A1}\x{57A2}\x{57A3}\x{57A4}\x{57A5}\x{57A6}\x{57A7}\x{57A9}\x{57AA}' .
+'\x{57AB}\x{57AC}\x{57AD}\x{57AE}\x{57AF}\x{57B0}\x{57B1}\x{57B2}\x{57B3}' .
+'\x{57B4}\x{57B5}\x{57B6}\x{57B7}\x{57B8}\x{57B9}\x{57BA}\x{57BB}\x{57BC}' .
+'\x{57BD}\x{57BE}\x{57BF}\x{57C0}\x{57C1}\x{57C2}\x{57C3}\x{57C4}\x{57C5}' .
+'\x{57C6}\x{57C7}\x{57C8}\x{57C9}\x{57CB}\x{57CC}\x{57CD}\x{57CE}\x{57CF}' .
+'\x{57D0}\x{57D2}\x{57D3}\x{57D4}\x{57D5}\x{57D6}\x{57D8}\x{57D9}\x{57DA}' .
+'\x{57DC}\x{57DD}\x{57DF}\x{57E0}\x{57E1}\x{57E2}\x{57E3}\x{57E4}\x{57E5}' .
+'\x{57E6}\x{57E7}\x{57E8}\x{57E9}\x{57EA}\x{57EB}\x{57EC}\x{57ED}\x{57EE}' .
+'\x{57EF}\x{57F0}\x{57F1}\x{57F2}\x{57F3}\x{57F4}\x{57F5}\x{57F6}\x{57F7}' .
+'\x{57F8}\x{57F9}\x{57FA}\x{57FB}\x{57FC}\x{57FD}\x{57FE}\x{57FF}\x{5800}' .
+'\x{5801}\x{5802}\x{5803}\x{5804}\x{5805}\x{5806}\x{5807}\x{5808}\x{5809}' .
+'\x{580A}\x{580B}\x{580C}\x{580D}\x{580E}\x{580F}\x{5810}\x{5811}\x{5812}' .
+'\x{5813}\x{5814}\x{5815}\x{5816}\x{5819}\x{581A}\x{581B}\x{581C}\x{581D}' .
+'\x{581E}\x{581F}\x{5820}\x{5821}\x{5822}\x{5823}\x{5824}\x{5825}\x{5826}' .
+'\x{5827}\x{5828}\x{5829}\x{582A}\x{582B}\x{582C}\x{582D}\x{582E}\x{582F}' .
+'\x{5830}\x{5831}\x{5832}\x{5833}\x{5834}\x{5835}\x{5836}\x{5837}\x{5838}' .
+'\x{5839}\x{583A}\x{583B}\x{583C}\x{583D}\x{583E}\x{583F}\x{5840}\x{5842}' .
+'\x{5843}\x{5844}\x{5845}\x{5846}\x{5847}\x{5848}\x{5849}\x{584A}\x{584B}' .
+'\x{584C}\x{584D}\x{584E}\x{584F}\x{5851}\x{5852}\x{5853}\x{5854}\x{5855}' .
+'\x{5857}\x{5858}\x{5859}\x{585A}\x{585B}\x{585C}\x{585D}\x{585E}\x{585F}' .
+'\x{5861}\x{5862}\x{5863}\x{5864}\x{5865}\x{5868}\x{5869}\x{586A}\x{586B}' .
+'\x{586C}\x{586D}\x{586E}\x{586F}\x{5870}\x{5871}\x{5872}\x{5873}\x{5874}' .
+'\x{5875}\x{5876}\x{5878}\x{5879}\x{587A}\x{587B}\x{587C}\x{587D}\x{587E}' .
+'\x{587F}\x{5880}\x{5881}\x{5882}\x{5883}\x{5884}\x{5885}\x{5886}\x{5887}' .
+'\x{5888}\x{5889}\x{588A}\x{588B}\x{588C}\x{588D}\x{588E}\x{588F}\x{5890}' .
+'\x{5891}\x{5892}\x{5893}\x{5894}\x{5896}\x{5897}\x{5898}\x{5899}\x{589A}' .
+'\x{589B}\x{589C}\x{589D}\x{589E}\x{589F}\x{58A0}\x{58A1}\x{58A2}\x{58A3}' .
+'\x{58A4}\x{58A5}\x{58A6}\x{58A7}\x{58A8}\x{58A9}\x{58AB}\x{58AC}\x{58AD}' .
+'\x{58AE}\x{58AF}\x{58B0}\x{58B1}\x{58B2}\x{58B3}\x{58B4}\x{58B7}\x{58B8}' .
+'\x{58B9}\x{58BA}\x{58BB}\x{58BC}\x{58BD}\x{58BE}\x{58BF}\x{58C1}\x{58C2}' .
+'\x{58C5}\x{58C6}\x{58C7}\x{58C8}\x{58C9}\x{58CA}\x{58CB}\x{58CE}\x{58CF}' .
+'\x{58D1}\x{58D2}\x{58D3}\x{58D4}\x{58D5}\x{58D6}\x{58D7}\x{58D8}\x{58D9}' .
+'\x{58DA}\x{58DB}\x{58DD}\x{58DE}\x{58DF}\x{58E0}\x{58E2}\x{58E3}\x{58E4}' .
+'\x{58E5}\x{58E7}\x{58E8}\x{58E9}\x{58EA}\x{58EB}\x{58EC}\x{58ED}\x{58EE}' .
+'\x{58EF}\x{58F0}\x{58F1}\x{58F2}\x{58F3}\x{58F4}\x{58F6}\x{58F7}\x{58F8}' .
+'\x{58F9}\x{58FA}\x{58FB}\x{58FC}\x{58FD}\x{58FE}\x{58FF}\x{5900}\x{5902}' .
+'\x{5903}\x{5904}\x{5906}\x{5907}\x{5909}\x{590A}\x{590B}\x{590C}\x{590D}' .
+'\x{590E}\x{590F}\x{5910}\x{5912}\x{5914}\x{5915}\x{5916}\x{5917}\x{5918}' .
+'\x{5919}\x{591A}\x{591B}\x{591C}\x{591D}\x{591E}\x{591F}\x{5920}\x{5921}' .
+'\x{5922}\x{5924}\x{5925}\x{5926}\x{5927}\x{5928}\x{5929}\x{592A}\x{592B}' .
+'\x{592C}\x{592D}\x{592E}\x{592F}\x{5930}\x{5931}\x{5932}\x{5934}\x{5935}' .
+'\x{5937}\x{5938}\x{5939}\x{593A}\x{593B}\x{593C}\x{593D}\x{593E}\x{593F}' .
+'\x{5940}\x{5941}\x{5942}\x{5943}\x{5944}\x{5945}\x{5946}\x{5947}\x{5948}' .
+'\x{5949}\x{594A}\x{594B}\x{594C}\x{594D}\x{594E}\x{594F}\x{5950}\x{5951}' .
+'\x{5952}\x{5953}\x{5954}\x{5955}\x{5956}\x{5957}\x{5958}\x{595A}\x{595C}' .
+'\x{595D}\x{595E}\x{595F}\x{5960}\x{5961}\x{5962}\x{5963}\x{5964}\x{5965}' .
+'\x{5966}\x{5967}\x{5968}\x{5969}\x{596A}\x{596B}\x{596C}\x{596D}\x{596E}' .
+'\x{596F}\x{5970}\x{5971}\x{5972}\x{5973}\x{5974}\x{5975}\x{5976}\x{5977}' .
+'\x{5978}\x{5979}\x{597A}\x{597B}\x{597C}\x{597D}\x{597E}\x{597F}\x{5980}' .
+'\x{5981}\x{5982}\x{5983}\x{5984}\x{5985}\x{5986}\x{5987}\x{5988}\x{5989}' .
+'\x{598A}\x{598B}\x{598C}\x{598D}\x{598E}\x{598F}\x{5990}\x{5991}\x{5992}' .
+'\x{5993}\x{5994}\x{5995}\x{5996}\x{5997}\x{5998}\x{5999}\x{599A}\x{599C}' .
+'\x{599D}\x{599E}\x{599F}\x{59A0}\x{59A1}\x{59A2}\x{59A3}\x{59A4}\x{59A5}' .
+'\x{59A6}\x{59A7}\x{59A8}\x{59A9}\x{59AA}\x{59AB}\x{59AC}\x{59AD}\x{59AE}' .
+'\x{59AF}\x{59B0}\x{59B1}\x{59B2}\x{59B3}\x{59B4}\x{59B5}\x{59B6}\x{59B8}' .
+'\x{59B9}\x{59BA}\x{59BB}\x{59BC}\x{59BD}\x{59BE}\x{59BF}\x{59C0}\x{59C1}' .
+'\x{59C2}\x{59C3}\x{59C4}\x{59C5}\x{59C6}\x{59C7}\x{59C8}\x{59C9}\x{59CA}' .
+'\x{59CB}\x{59CC}\x{59CD}\x{59CE}\x{59CF}\x{59D0}\x{59D1}\x{59D2}\x{59D3}' .
+'\x{59D4}\x{59D5}\x{59D6}\x{59D7}\x{59D8}\x{59D9}\x{59DA}\x{59DB}\x{59DC}' .
+'\x{59DD}\x{59DE}\x{59DF}\x{59E0}\x{59E1}\x{59E2}\x{59E3}\x{59E4}\x{59E5}' .
+'\x{59E6}\x{59E8}\x{59E9}\x{59EA}\x{59EB}\x{59EC}\x{59ED}\x{59EE}\x{59EF}' .
+'\x{59F0}\x{59F1}\x{59F2}\x{59F3}\x{59F4}\x{59F5}\x{59F6}\x{59F7}\x{59F8}' .
+'\x{59F9}\x{59FA}\x{59FB}\x{59FC}\x{59FD}\x{59FE}\x{59FF}\x{5A00}\x{5A01}' .
+'\x{5A02}\x{5A03}\x{5A04}\x{5A05}\x{5A06}\x{5A07}\x{5A08}\x{5A09}\x{5A0A}' .
+'\x{5A0B}\x{5A0C}\x{5A0D}\x{5A0E}\x{5A0F}\x{5A10}\x{5A11}\x{5A12}\x{5A13}' .
+'\x{5A14}\x{5A15}\x{5A16}\x{5A17}\x{5A18}\x{5A19}\x{5A1A}\x{5A1B}\x{5A1C}' .
+'\x{5A1D}\x{5A1E}\x{5A1F}\x{5A20}\x{5A21}\x{5A22}\x{5A23}\x{5A25}\x{5A27}' .
+'\x{5A28}\x{5A29}\x{5A2A}\x{5A2B}\x{5A2D}\x{5A2E}\x{5A2F}\x{5A31}\x{5A32}' .
+'\x{5A33}\x{5A34}\x{5A35}\x{5A36}\x{5A37}\x{5A38}\x{5A39}\x{5A3A}\x{5A3B}' .
+'\x{5A3C}\x{5A3D}\x{5A3E}\x{5A3F}\x{5A40}\x{5A41}\x{5A42}\x{5A43}\x{5A44}' .
+'\x{5A45}\x{5A46}\x{5A47}\x{5A48}\x{5A49}\x{5A4A}\x{5A4B}\x{5A4C}\x{5A4D}' .
+'\x{5A4E}\x{5A4F}\x{5A50}\x{5A51}\x{5A52}\x{5A53}\x{5A55}\x{5A56}\x{5A57}' .
+'\x{5A58}\x{5A5A}\x{5A5B}\x{5A5C}\x{5A5D}\x{5A5E}\x{5A5F}\x{5A60}\x{5A61}' .
+'\x{5A62}\x{5A63}\x{5A64}\x{5A65}\x{5A66}\x{5A67}\x{5A68}\x{5A69}\x{5A6A}' .
+'\x{5A6B}\x{5A6C}\x{5A6D}\x{5A6E}\x{5A70}\x{5A72}\x{5A73}\x{5A74}\x{5A75}' .
+'\x{5A76}\x{5A77}\x{5A78}\x{5A79}\x{5A7A}\x{5A7B}\x{5A7C}\x{5A7D}\x{5A7E}' .
+'\x{5A7F}\x{5A80}\x{5A81}\x{5A82}\x{5A83}\x{5A84}\x{5A85}\x{5A86}\x{5A88}' .
+'\x{5A89}\x{5A8A}\x{5A8B}\x{5A8C}\x{5A8E}\x{5A8F}\x{5A90}\x{5A91}\x{5A92}' .
+'\x{5A93}\x{5A94}\x{5A95}\x{5A96}\x{5A97}\x{5A98}\x{5A99}\x{5A9A}\x{5A9B}' .
+'\x{5A9C}\x{5A9D}\x{5A9E}\x{5A9F}\x{5AA0}\x{5AA1}\x{5AA2}\x{5AA3}\x{5AA4}' .
+'\x{5AA5}\x{5AA6}\x{5AA7}\x{5AA8}\x{5AA9}\x{5AAA}\x{5AAC}\x{5AAD}\x{5AAE}' .
+'\x{5AAF}\x{5AB0}\x{5AB1}\x{5AB2}\x{5AB3}\x{5AB4}\x{5AB5}\x{5AB6}\x{5AB7}' .
+'\x{5AB8}\x{5AB9}\x{5ABA}\x{5ABB}\x{5ABC}\x{5ABD}\x{5ABE}\x{5ABF}\x{5AC0}' .
+'\x{5AC1}\x{5AC2}\x{5AC3}\x{5AC4}\x{5AC5}\x{5AC6}\x{5AC7}\x{5AC8}\x{5AC9}' .
+'\x{5ACA}\x{5ACB}\x{5ACC}\x{5ACD}\x{5ACE}\x{5ACF}\x{5AD1}\x{5AD2}\x{5AD4}' .
+'\x{5AD5}\x{5AD6}\x{5AD7}\x{5AD8}\x{5AD9}\x{5ADA}\x{5ADB}\x{5ADC}\x{5ADD}' .
+'\x{5ADE}\x{5ADF}\x{5AE0}\x{5AE1}\x{5AE2}\x{5AE3}\x{5AE4}\x{5AE5}\x{5AE6}' .
+'\x{5AE7}\x{5AE8}\x{5AE9}\x{5AEA}\x{5AEB}\x{5AEC}\x{5AED}\x{5AEE}\x{5AF1}' .
+'\x{5AF2}\x{5AF3}\x{5AF4}\x{5AF5}\x{5AF6}\x{5AF7}\x{5AF8}\x{5AF9}\x{5AFA}' .
+'\x{5AFB}\x{5AFC}\x{5AFD}\x{5AFE}\x{5AFF}\x{5B00}\x{5B01}\x{5B02}\x{5B03}' .
+'\x{5B04}\x{5B05}\x{5B06}\x{5B07}\x{5B08}\x{5B09}\x{5B0B}\x{5B0C}\x{5B0E}' .
+'\x{5B0F}\x{5B10}\x{5B11}\x{5B12}\x{5B13}\x{5B14}\x{5B15}\x{5B16}\x{5B17}' .
+'\x{5B18}\x{5B19}\x{5B1A}\x{5B1B}\x{5B1C}\x{5B1D}\x{5B1E}\x{5B1F}\x{5B20}' .
+'\x{5B21}\x{5B22}\x{5B23}\x{5B24}\x{5B25}\x{5B26}\x{5B27}\x{5B28}\x{5B29}' .
+'\x{5B2A}\x{5B2B}\x{5B2C}\x{5B2D}\x{5B2E}\x{5B2F}\x{5B30}\x{5B31}\x{5B32}' .
+'\x{5B33}\x{5B34}\x{5B35}\x{5B36}\x{5B37}\x{5B38}\x{5B3A}\x{5B3B}\x{5B3C}' .
+'\x{5B3D}\x{5B3E}\x{5B3F}\x{5B40}\x{5B41}\x{5B42}\x{5B43}\x{5B44}\x{5B45}' .
+'\x{5B47}\x{5B48}\x{5B49}\x{5B4A}\x{5B4B}\x{5B4C}\x{5B4D}\x{5B4E}\x{5B50}' .
+'\x{5B51}\x{5B53}\x{5B54}\x{5B55}\x{5B56}\x{5B57}\x{5B58}\x{5B59}\x{5B5A}' .
+'\x{5B5B}\x{5B5C}\x{5B5D}\x{5B5E}\x{5B5F}\x{5B62}\x{5B63}\x{5B64}\x{5B65}' .
+'\x{5B66}\x{5B67}\x{5B68}\x{5B69}\x{5B6A}\x{5B6B}\x{5B6C}\x{5B6D}\x{5B6E}' .
+'\x{5B70}\x{5B71}\x{5B72}\x{5B73}\x{5B74}\x{5B75}\x{5B76}\x{5B77}\x{5B78}' .
+'\x{5B7A}\x{5B7B}\x{5B7C}\x{5B7D}\x{5B7F}\x{5B80}\x{5B81}\x{5B82}\x{5B83}' .
+'\x{5B84}\x{5B85}\x{5B87}\x{5B88}\x{5B89}\x{5B8A}\x{5B8B}\x{5B8C}\x{5B8D}' .
+'\x{5B8E}\x{5B8F}\x{5B91}\x{5B92}\x{5B93}\x{5B94}\x{5B95}\x{5B96}\x{5B97}' .
+'\x{5B98}\x{5B99}\x{5B9A}\x{5B9B}\x{5B9C}\x{5B9D}\x{5B9E}\x{5B9F}\x{5BA0}' .
+'\x{5BA1}\x{5BA2}\x{5BA3}\x{5BA4}\x{5BA5}\x{5BA6}\x{5BA7}\x{5BA8}\x{5BAA}' .
+'\x{5BAB}\x{5BAC}\x{5BAD}\x{5BAE}\x{5BAF}\x{5BB0}\x{5BB1}\x{5BB3}\x{5BB4}' .
+'\x{5BB5}\x{5BB6}\x{5BB8}\x{5BB9}\x{5BBA}\x{5BBB}\x{5BBD}\x{5BBE}\x{5BBF}' .
+'\x{5BC0}\x{5BC1}\x{5BC2}\x{5BC3}\x{5BC4}\x{5BC5}\x{5BC6}\x{5BC7}\x{5BCA}' .
+'\x{5BCB}\x{5BCC}\x{5BCD}\x{5BCE}\x{5BCF}\x{5BD0}\x{5BD1}\x{5BD2}\x{5BD3}' .
+'\x{5BD4}\x{5BD5}\x{5BD6}\x{5BD8}\x{5BD9}\x{5BDB}\x{5BDC}\x{5BDD}\x{5BDE}' .
+'\x{5BDF}\x{5BE0}\x{5BE1}\x{5BE2}\x{5BE3}\x{5BE4}\x{5BE5}\x{5BE6}\x{5BE7}' .
+'\x{5BE8}\x{5BE9}\x{5BEA}\x{5BEB}\x{5BEC}\x{5BED}\x{5BEE}\x{5BEF}\x{5BF0}' .
+'\x{5BF1}\x{5BF2}\x{5BF3}\x{5BF4}\x{5BF5}\x{5BF6}\x{5BF7}\x{5BF8}\x{5BF9}' .
+'\x{5BFA}\x{5BFB}\x{5BFC}\x{5BFD}\x{5BFF}\x{5C01}\x{5C03}\x{5C04}\x{5C05}' .
+'\x{5C06}\x{5C07}\x{5C08}\x{5C09}\x{5C0A}\x{5C0B}\x{5C0C}\x{5C0D}\x{5C0E}' .
+'\x{5C0F}\x{5C10}\x{5C11}\x{5C12}\x{5C13}\x{5C14}\x{5C15}\x{5C16}\x{5C17}' .
+'\x{5C18}\x{5C19}\x{5C1A}\x{5C1C}\x{5C1D}\x{5C1E}\x{5C1F}\x{5C20}\x{5C21}' .
+'\x{5C22}\x{5C24}\x{5C25}\x{5C27}\x{5C28}\x{5C2A}\x{5C2B}\x{5C2C}\x{5C2D}' .
+'\x{5C2E}\x{5C2F}\x{5C30}\x{5C31}\x{5C32}\x{5C33}\x{5C34}\x{5C35}\x{5C37}' .
+'\x{5C38}\x{5C39}\x{5C3A}\x{5C3B}\x{5C3C}\x{5C3D}\x{5C3E}\x{5C3F}\x{5C40}' .
+'\x{5C41}\x{5C42}\x{5C43}\x{5C44}\x{5C45}\x{5C46}\x{5C47}\x{5C48}\x{5C49}' .
+'\x{5C4A}\x{5C4B}\x{5C4C}\x{5C4D}\x{5C4E}\x{5C4F}\x{5C50}\x{5C51}\x{5C52}' .
+'\x{5C53}\x{5C54}\x{5C55}\x{5C56}\x{5C57}\x{5C58}\x{5C59}\x{5C5B}\x{5C5C}' .
+'\x{5C5D}\x{5C5E}\x{5C5F}\x{5C60}\x{5C61}\x{5C62}\x{5C63}\x{5C64}\x{5C65}' .
+'\x{5C66}\x{5C67}\x{5C68}\x{5C69}\x{5C6A}\x{5C6B}\x{5C6C}\x{5C6D}\x{5C6E}' .
+'\x{5C6F}\x{5C70}\x{5C71}\x{5C72}\x{5C73}\x{5C74}\x{5C75}\x{5C76}\x{5C77}' .
+'\x{5C78}\x{5C79}\x{5C7A}\x{5C7B}\x{5C7C}\x{5C7D}\x{5C7E}\x{5C7F}\x{5C80}' .
+'\x{5C81}\x{5C82}\x{5C83}\x{5C84}\x{5C86}\x{5C87}\x{5C88}\x{5C89}\x{5C8A}' .
+'\x{5C8B}\x{5C8C}\x{5C8D}\x{5C8E}\x{5C8F}\x{5C90}\x{5C91}\x{5C92}\x{5C93}' .
+'\x{5C94}\x{5C95}\x{5C96}\x{5C97}\x{5C98}\x{5C99}\x{5C9A}\x{5C9B}\x{5C9C}' .
+'\x{5C9D}\x{5C9E}\x{5C9F}\x{5CA0}\x{5CA1}\x{5CA2}\x{5CA3}\x{5CA4}\x{5CA5}' .
+'\x{5CA6}\x{5CA7}\x{5CA8}\x{5CA9}\x{5CAA}\x{5CAB}\x{5CAC}\x{5CAD}\x{5CAE}' .
+'\x{5CAF}\x{5CB0}\x{5CB1}\x{5CB2}\x{5CB3}\x{5CB5}\x{5CB6}\x{5CB7}\x{5CB8}' .
+'\x{5CBA}\x{5CBB}\x{5CBC}\x{5CBD}\x{5CBE}\x{5CBF}\x{5CC1}\x{5CC2}\x{5CC3}' .
+'\x{5CC4}\x{5CC5}\x{5CC6}\x{5CC7}\x{5CC8}\x{5CC9}\x{5CCA}\x{5CCB}\x{5CCC}' .
+'\x{5CCD}\x{5CCE}\x{5CCF}\x{5CD0}\x{5CD1}\x{5CD2}\x{5CD3}\x{5CD4}\x{5CD6}' .
+'\x{5CD7}\x{5CD8}\x{5CD9}\x{5CDA}\x{5CDB}\x{5CDC}\x{5CDE}\x{5CDF}\x{5CE0}' .
+'\x{5CE1}\x{5CE2}\x{5CE3}\x{5CE4}\x{5CE5}\x{5CE6}\x{5CE7}\x{5CE8}\x{5CE9}' .
+'\x{5CEA}\x{5CEB}\x{5CEC}\x{5CED}\x{5CEE}\x{5CEF}\x{5CF0}\x{5CF1}\x{5CF2}' .
+'\x{5CF3}\x{5CF4}\x{5CF6}\x{5CF7}\x{5CF8}\x{5CF9}\x{5CFA}\x{5CFB}\x{5CFC}' .
+'\x{5CFD}\x{5CFE}\x{5CFF}\x{5D00}\x{5D01}\x{5D02}\x{5D03}\x{5D04}\x{5D05}' .
+'\x{5D06}\x{5D07}\x{5D08}\x{5D09}\x{5D0A}\x{5D0B}\x{5D0C}\x{5D0D}\x{5D0E}' .
+'\x{5D0F}\x{5D10}\x{5D11}\x{5D12}\x{5D13}\x{5D14}\x{5D15}\x{5D16}\x{5D17}' .
+'\x{5D18}\x{5D19}\x{5D1A}\x{5D1B}\x{5D1C}\x{5D1D}\x{5D1E}\x{5D1F}\x{5D20}' .
+'\x{5D21}\x{5D22}\x{5D23}\x{5D24}\x{5D25}\x{5D26}\x{5D27}\x{5D28}\x{5D29}' .
+'\x{5D2A}\x{5D2C}\x{5D2D}\x{5D2E}\x{5D30}\x{5D31}\x{5D32}\x{5D33}\x{5D34}' .
+'\x{5D35}\x{5D36}\x{5D37}\x{5D38}\x{5D39}\x{5D3A}\x{5D3C}\x{5D3D}\x{5D3E}' .
+'\x{5D3F}\x{5D40}\x{5D41}\x{5D42}\x{5D43}\x{5D44}\x{5D45}\x{5D46}\x{5D47}' .
+'\x{5D48}\x{5D49}\x{5D4A}\x{5D4B}\x{5D4C}\x{5D4D}\x{5D4E}\x{5D4F}\x{5D50}' .
+'\x{5D51}\x{5D52}\x{5D54}\x{5D55}\x{5D56}\x{5D58}\x{5D59}\x{5D5A}\x{5D5B}' .
+'\x{5D5D}\x{5D5E}\x{5D5F}\x{5D61}\x{5D62}\x{5D63}\x{5D64}\x{5D65}\x{5D66}' .
+'\x{5D67}\x{5D68}\x{5D69}\x{5D6A}\x{5D6B}\x{5D6C}\x{5D6D}\x{5D6E}\x{5D6F}' .
+'\x{5D70}\x{5D71}\x{5D72}\x{5D73}\x{5D74}\x{5D75}\x{5D76}\x{5D77}\x{5D78}' .
+'\x{5D79}\x{5D7A}\x{5D7B}\x{5D7C}\x{5D7D}\x{5D7E}\x{5D7F}\x{5D80}\x{5D81}' .
+'\x{5D82}\x{5D84}\x{5D85}\x{5D86}\x{5D87}\x{5D88}\x{5D89}\x{5D8A}\x{5D8B}' .
+'\x{5D8C}\x{5D8D}\x{5D8E}\x{5D8F}\x{5D90}\x{5D91}\x{5D92}\x{5D93}\x{5D94}' .
+'\x{5D95}\x{5D97}\x{5D98}\x{5D99}\x{5D9A}\x{5D9B}\x{5D9C}\x{5D9D}\x{5D9E}' .
+'\x{5D9F}\x{5DA0}\x{5DA1}\x{5DA2}\x{5DA5}\x{5DA6}\x{5DA7}\x{5DA8}\x{5DA9}' .
+'\x{5DAA}\x{5DAC}\x{5DAD}\x{5DAE}\x{5DAF}\x{5DB0}\x{5DB1}\x{5DB2}\x{5DB4}' .
+'\x{5DB5}\x{5DB6}\x{5DB7}\x{5DB8}\x{5DBA}\x{5DBB}\x{5DBC}\x{5DBD}\x{5DBE}' .
+'\x{5DBF}\x{5DC0}\x{5DC1}\x{5DC2}\x{5DC3}\x{5DC5}\x{5DC6}\x{5DC7}\x{5DC8}' .
+'\x{5DC9}\x{5DCA}\x{5DCB}\x{5DCC}\x{5DCD}\x{5DCE}\x{5DCF}\x{5DD0}\x{5DD1}' .
+'\x{5DD2}\x{5DD3}\x{5DD4}\x{5DD5}\x{5DD6}\x{5DD8}\x{5DD9}\x{5DDB}\x{5DDD}' .
+'\x{5DDE}\x{5DDF}\x{5DE0}\x{5DE1}\x{5DE2}\x{5DE3}\x{5DE4}\x{5DE5}\x{5DE6}' .
+'\x{5DE7}\x{5DE8}\x{5DE9}\x{5DEA}\x{5DEB}\x{5DEC}\x{5DED}\x{5DEE}\x{5DEF}' .
+'\x{5DF0}\x{5DF1}\x{5DF2}\x{5DF3}\x{5DF4}\x{5DF5}\x{5DF7}\x{5DF8}\x{5DF9}' .
+'\x{5DFA}\x{5DFB}\x{5DFC}\x{5DFD}\x{5DFE}\x{5DFF}\x{5E00}\x{5E01}\x{5E02}' .
+'\x{5E03}\x{5E04}\x{5E05}\x{5E06}\x{5E07}\x{5E08}\x{5E09}\x{5E0A}\x{5E0B}' .
+'\x{5E0C}\x{5E0D}\x{5E0E}\x{5E0F}\x{5E10}\x{5E11}\x{5E13}\x{5E14}\x{5E15}' .
+'\x{5E16}\x{5E17}\x{5E18}\x{5E19}\x{5E1A}\x{5E1B}\x{5E1C}\x{5E1D}\x{5E1E}' .
+'\x{5E1F}\x{5E20}\x{5E21}\x{5E22}\x{5E23}\x{5E24}\x{5E25}\x{5E26}\x{5E27}' .
+'\x{5E28}\x{5E29}\x{5E2A}\x{5E2B}\x{5E2C}\x{5E2D}\x{5E2E}\x{5E2F}\x{5E30}' .
+'\x{5E31}\x{5E32}\x{5E33}\x{5E34}\x{5E35}\x{5E36}\x{5E37}\x{5E38}\x{5E39}' .
+'\x{5E3A}\x{5E3B}\x{5E3C}\x{5E3D}\x{5E3E}\x{5E40}\x{5E41}\x{5E42}\x{5E43}' .
+'\x{5E44}\x{5E45}\x{5E46}\x{5E47}\x{5E49}\x{5E4A}\x{5E4B}\x{5E4C}\x{5E4D}' .
+'\x{5E4E}\x{5E4F}\x{5E50}\x{5E52}\x{5E53}\x{5E54}\x{5E55}\x{5E56}\x{5E57}' .
+'\x{5E58}\x{5E59}\x{5E5A}\x{5E5B}\x{5E5C}\x{5E5D}\x{5E5E}\x{5E5F}\x{5E60}' .
+'\x{5E61}\x{5E62}\x{5E63}\x{5E64}\x{5E65}\x{5E66}\x{5E67}\x{5E68}\x{5E69}' .
+'\x{5E6A}\x{5E6B}\x{5E6C}\x{5E6D}\x{5E6E}\x{5E6F}\x{5E70}\x{5E71}\x{5E72}' .
+'\x{5E73}\x{5E74}\x{5E75}\x{5E76}\x{5E77}\x{5E78}\x{5E79}\x{5E7A}\x{5E7B}' .
+'\x{5E7C}\x{5E7D}\x{5E7E}\x{5E7F}\x{5E80}\x{5E81}\x{5E82}\x{5E83}\x{5E84}' .
+'\x{5E85}\x{5E86}\x{5E87}\x{5E88}\x{5E89}\x{5E8A}\x{5E8B}\x{5E8C}\x{5E8D}' .
+'\x{5E8E}\x{5E8F}\x{5E90}\x{5E91}\x{5E93}\x{5E94}\x{5E95}\x{5E96}\x{5E97}' .
+'\x{5E98}\x{5E99}\x{5E9A}\x{5E9B}\x{5E9C}\x{5E9D}\x{5E9E}\x{5E9F}\x{5EA0}' .
+'\x{5EA1}\x{5EA2}\x{5EA3}\x{5EA4}\x{5EA5}\x{5EA6}\x{5EA7}\x{5EA8}\x{5EA9}' .
+'\x{5EAA}\x{5EAB}\x{5EAC}\x{5EAD}\x{5EAE}\x{5EAF}\x{5EB0}\x{5EB1}\x{5EB2}' .
+'\x{5EB3}\x{5EB4}\x{5EB5}\x{5EB6}\x{5EB7}\x{5EB8}\x{5EB9}\x{5EBB}\x{5EBC}' .
+'\x{5EBD}\x{5EBE}\x{5EBF}\x{5EC1}\x{5EC2}\x{5EC3}\x{5EC4}\x{5EC5}\x{5EC6}' .
+'\x{5EC7}\x{5EC8}\x{5EC9}\x{5ECA}\x{5ECB}\x{5ECC}\x{5ECD}\x{5ECE}\x{5ECF}' .
+'\x{5ED0}\x{5ED1}\x{5ED2}\x{5ED3}\x{5ED4}\x{5ED5}\x{5ED6}\x{5ED7}\x{5ED8}' .
+'\x{5ED9}\x{5EDA}\x{5EDB}\x{5EDC}\x{5EDD}\x{5EDE}\x{5EDF}\x{5EE0}\x{5EE1}' .
+'\x{5EE2}\x{5EE3}\x{5EE4}\x{5EE5}\x{5EE6}\x{5EE7}\x{5EE8}\x{5EE9}\x{5EEA}' .
+'\x{5EEC}\x{5EED}\x{5EEE}\x{5EEF}\x{5EF0}\x{5EF1}\x{5EF2}\x{5EF3}\x{5EF4}' .
+'\x{5EF5}\x{5EF6}\x{5EF7}\x{5EF8}\x{5EFA}\x{5EFB}\x{5EFC}\x{5EFD}\x{5EFE}' .
+'\x{5EFF}\x{5F00}\x{5F01}\x{5F02}\x{5F03}\x{5F04}\x{5F05}\x{5F06}\x{5F07}' .
+'\x{5F08}\x{5F0A}\x{5F0B}\x{5F0C}\x{5F0D}\x{5F0F}\x{5F11}\x{5F12}\x{5F13}' .
+'\x{5F14}\x{5F15}\x{5F16}\x{5F17}\x{5F18}\x{5F19}\x{5F1A}\x{5F1B}\x{5F1C}' .
+'\x{5F1D}\x{5F1E}\x{5F1F}\x{5F20}\x{5F21}\x{5F22}\x{5F23}\x{5F24}\x{5F25}' .
+'\x{5F26}\x{5F27}\x{5F28}\x{5F29}\x{5F2A}\x{5F2B}\x{5F2C}\x{5F2D}\x{5F2E}' .
+'\x{5F2F}\x{5F30}\x{5F31}\x{5F32}\x{5F33}\x{5F34}\x{5F35}\x{5F36}\x{5F37}' .
+'\x{5F38}\x{5F39}\x{5F3A}\x{5F3C}\x{5F3E}\x{5F3F}\x{5F40}\x{5F41}\x{5F42}' .
+'\x{5F43}\x{5F44}\x{5F45}\x{5F46}\x{5F47}\x{5F48}\x{5F49}\x{5F4A}\x{5F4B}' .
+'\x{5F4C}\x{5F4D}\x{5F4E}\x{5F4F}\x{5F50}\x{5F51}\x{5F52}\x{5F53}\x{5F54}' .
+'\x{5F55}\x{5F56}\x{5F57}\x{5F58}\x{5F59}\x{5F5A}\x{5F5B}\x{5F5C}\x{5F5D}' .
+'\x{5F5E}\x{5F5F}\x{5F60}\x{5F61}\x{5F62}\x{5F63}\x{5F64}\x{5F65}\x{5F66}' .
+'\x{5F67}\x{5F68}\x{5F69}\x{5F6A}\x{5F6B}\x{5F6C}\x{5F6D}\x{5F6E}\x{5F6F}' .
+'\x{5F70}\x{5F71}\x{5F72}\x{5F73}\x{5F74}\x{5F75}\x{5F76}\x{5F77}\x{5F78}' .
+'\x{5F79}\x{5F7A}\x{5F7B}\x{5F7C}\x{5F7D}\x{5F7E}\x{5F7F}\x{5F80}\x{5F81}' .
+'\x{5F82}\x{5F83}\x{5F84}\x{5F85}\x{5F86}\x{5F87}\x{5F88}\x{5F89}\x{5F8A}' .
+'\x{5F8B}\x{5F8C}\x{5F8D}\x{5F8E}\x{5F90}\x{5F91}\x{5F92}\x{5F93}\x{5F94}' .
+'\x{5F95}\x{5F96}\x{5F97}\x{5F98}\x{5F99}\x{5F9B}\x{5F9C}\x{5F9D}\x{5F9E}' .
+'\x{5F9F}\x{5FA0}\x{5FA1}\x{5FA2}\x{5FA5}\x{5FA6}\x{5FA7}\x{5FA8}\x{5FA9}' .
+'\x{5FAA}\x{5FAB}\x{5FAC}\x{5FAD}\x{5FAE}\x{5FAF}\x{5FB1}\x{5FB2}\x{5FB3}' .
+'\x{5FB4}\x{5FB5}\x{5FB6}\x{5FB7}\x{5FB8}\x{5FB9}\x{5FBA}\x{5FBB}\x{5FBC}' .
+'\x{5FBD}\x{5FBE}\x{5FBF}\x{5FC0}\x{5FC1}\x{5FC3}\x{5FC4}\x{5FC5}\x{5FC6}' .
+'\x{5FC7}\x{5FC8}\x{5FC9}\x{5FCA}\x{5FCB}\x{5FCC}\x{5FCD}\x{5FCF}\x{5FD0}' .
+'\x{5FD1}\x{5FD2}\x{5FD3}\x{5FD4}\x{5FD5}\x{5FD6}\x{5FD7}\x{5FD8}\x{5FD9}' .
+'\x{5FDA}\x{5FDC}\x{5FDD}\x{5FDE}\x{5FE0}\x{5FE1}\x{5FE3}\x{5FE4}\x{5FE5}' .
+'\x{5FE6}\x{5FE7}\x{5FE8}\x{5FE9}\x{5FEA}\x{5FEB}\x{5FED}\x{5FEE}\x{5FEF}' .
+'\x{5FF0}\x{5FF1}\x{5FF2}\x{5FF3}\x{5FF4}\x{5FF5}\x{5FF6}\x{5FF7}\x{5FF8}' .
+'\x{5FF9}\x{5FFA}\x{5FFB}\x{5FFD}\x{5FFE}\x{5FFF}\x{6000}\x{6001}\x{6002}' .
+'\x{6003}\x{6004}\x{6005}\x{6006}\x{6007}\x{6008}\x{6009}\x{600A}\x{600B}' .
+'\x{600C}\x{600D}\x{600E}\x{600F}\x{6010}\x{6011}\x{6012}\x{6013}\x{6014}' .
+'\x{6015}\x{6016}\x{6017}\x{6018}\x{6019}\x{601A}\x{601B}\x{601C}\x{601D}' .
+'\x{601E}\x{601F}\x{6020}\x{6021}\x{6022}\x{6024}\x{6025}\x{6026}\x{6027}' .
+'\x{6028}\x{6029}\x{602A}\x{602B}\x{602C}\x{602D}\x{602E}\x{602F}\x{6030}' .
+'\x{6031}\x{6032}\x{6033}\x{6034}\x{6035}\x{6036}\x{6037}\x{6038}\x{6039}' .
+'\x{603A}\x{603B}\x{603C}\x{603D}\x{603E}\x{603F}\x{6040}\x{6041}\x{6042}' .
+'\x{6043}\x{6044}\x{6045}\x{6046}\x{6047}\x{6048}\x{6049}\x{604A}\x{604B}' .
+'\x{604C}\x{604D}\x{604E}\x{604F}\x{6050}\x{6051}\x{6052}\x{6053}\x{6054}' .
+'\x{6055}\x{6057}\x{6058}\x{6059}\x{605A}\x{605B}\x{605C}\x{605D}\x{605E}' .
+'\x{605F}\x{6062}\x{6063}\x{6064}\x{6065}\x{6066}\x{6067}\x{6068}\x{6069}' .
+'\x{606A}\x{606B}\x{606C}\x{606D}\x{606E}\x{606F}\x{6070}\x{6072}\x{6073}' .
+'\x{6075}\x{6076}\x{6077}\x{6078}\x{6079}\x{607A}\x{607B}\x{607C}\x{607D}' .
+'\x{607E}\x{607F}\x{6080}\x{6081}\x{6082}\x{6083}\x{6084}\x{6085}\x{6086}' .
+'\x{6087}\x{6088}\x{6089}\x{608A}\x{608B}\x{608C}\x{608D}\x{608E}\x{608F}' .
+'\x{6090}\x{6092}\x{6094}\x{6095}\x{6096}\x{6097}\x{6098}\x{6099}\x{609A}' .
+'\x{609B}\x{609C}\x{609D}\x{609E}\x{609F}\x{60A0}\x{60A1}\x{60A2}\x{60A3}' .
+'\x{60A4}\x{60A6}\x{60A7}\x{60A8}\x{60AA}\x{60AB}\x{60AC}\x{60AD}\x{60AE}' .
+'\x{60AF}\x{60B0}\x{60B1}\x{60B2}\x{60B3}\x{60B4}\x{60B5}\x{60B6}\x{60B7}' .
+'\x{60B8}\x{60B9}\x{60BA}\x{60BB}\x{60BC}\x{60BD}\x{60BE}\x{60BF}\x{60C0}' .
+'\x{60C1}\x{60C2}\x{60C3}\x{60C4}\x{60C5}\x{60C6}\x{60C7}\x{60C8}\x{60C9}' .
+'\x{60CA}\x{60CB}\x{60CC}\x{60CD}\x{60CE}\x{60CF}\x{60D0}\x{60D1}\x{60D3}' .
+'\x{60D4}\x{60D5}\x{60D7}\x{60D8}\x{60D9}\x{60DA}\x{60DB}\x{60DC}\x{60DD}' .
+'\x{60DF}\x{60E0}\x{60E1}\x{60E2}\x{60E4}\x{60E6}\x{60E7}\x{60E8}\x{60E9}' .
+'\x{60EA}\x{60EB}\x{60EC}\x{60ED}\x{60EE}\x{60EF}\x{60F0}\x{60F1}\x{60F2}' .
+'\x{60F3}\x{60F4}\x{60F5}\x{60F6}\x{60F7}\x{60F8}\x{60F9}\x{60FA}\x{60FB}' .
+'\x{60FC}\x{60FE}\x{60FF}\x{6100}\x{6101}\x{6103}\x{6104}\x{6105}\x{6106}' .
+'\x{6108}\x{6109}\x{610A}\x{610B}\x{610C}\x{610D}\x{610E}\x{610F}\x{6110}' .
+'\x{6112}\x{6113}\x{6114}\x{6115}\x{6116}\x{6117}\x{6118}\x{6119}\x{611A}' .
+'\x{611B}\x{611C}\x{611D}\x{611F}\x{6120}\x{6122}\x{6123}\x{6124}\x{6125}' .
+'\x{6126}\x{6127}\x{6128}\x{6129}\x{612A}\x{612B}\x{612C}\x{612D}\x{612E}' .
+'\x{612F}\x{6130}\x{6132}\x{6134}\x{6136}\x{6137}\x{613A}\x{613B}\x{613C}' .
+'\x{613D}\x{613E}\x{613F}\x{6140}\x{6141}\x{6142}\x{6143}\x{6144}\x{6145}' .
+'\x{6146}\x{6147}\x{6148}\x{6149}\x{614A}\x{614B}\x{614C}\x{614D}\x{614E}' .
+'\x{614F}\x{6150}\x{6151}\x{6152}\x{6153}\x{6154}\x{6155}\x{6156}\x{6157}' .
+'\x{6158}\x{6159}\x{615A}\x{615B}\x{615C}\x{615D}\x{615E}\x{615F}\x{6161}' .
+'\x{6162}\x{6163}\x{6164}\x{6165}\x{6166}\x{6167}\x{6168}\x{6169}\x{616A}' .
+'\x{616B}\x{616C}\x{616D}\x{616E}\x{6170}\x{6171}\x{6172}\x{6173}\x{6174}' .
+'\x{6175}\x{6176}\x{6177}\x{6178}\x{6179}\x{617A}\x{617C}\x{617E}\x{6180}' .
+'\x{6181}\x{6182}\x{6183}\x{6184}\x{6185}\x{6187}\x{6188}\x{6189}\x{618A}' .
+'\x{618B}\x{618C}\x{618D}\x{618E}\x{618F}\x{6190}\x{6191}\x{6192}\x{6193}' .
+'\x{6194}\x{6195}\x{6196}\x{6198}\x{6199}\x{619A}\x{619B}\x{619D}\x{619E}' .
+'\x{619F}\x{61A0}\x{61A1}\x{61A2}\x{61A3}\x{61A4}\x{61A5}\x{61A6}\x{61A7}' .
+'\x{61A8}\x{61A9}\x{61AA}\x{61AB}\x{61AC}\x{61AD}\x{61AE}\x{61AF}\x{61B0}' .
+'\x{61B1}\x{61B2}\x{61B3}\x{61B4}\x{61B5}\x{61B6}\x{61B7}\x{61B8}\x{61BA}' .
+'\x{61BC}\x{61BD}\x{61BE}\x{61BF}\x{61C0}\x{61C1}\x{61C2}\x{61C3}\x{61C4}' .
+'\x{61C5}\x{61C6}\x{61C7}\x{61C8}\x{61C9}\x{61CA}\x{61CB}\x{61CC}\x{61CD}' .
+'\x{61CE}\x{61CF}\x{61D0}\x{61D1}\x{61D2}\x{61D4}\x{61D6}\x{61D7}\x{61D8}' .
+'\x{61D9}\x{61DA}\x{61DB}\x{61DC}\x{61DD}\x{61DE}\x{61DF}\x{61E0}\x{61E1}' .
+'\x{61E2}\x{61E3}\x{61E4}\x{61E5}\x{61E6}\x{61E7}\x{61E8}\x{61E9}\x{61EA}' .
+'\x{61EB}\x{61ED}\x{61EE}\x{61F0}\x{61F1}\x{61F2}\x{61F3}\x{61F5}\x{61F6}' .
+'\x{61F7}\x{61F8}\x{61F9}\x{61FA}\x{61FB}\x{61FC}\x{61FD}\x{61FE}\x{61FF}' .
+'\x{6200}\x{6201}\x{6202}\x{6203}\x{6204}\x{6206}\x{6207}\x{6208}\x{6209}' .
+'\x{620A}\x{620B}\x{620C}\x{620D}\x{620E}\x{620F}\x{6210}\x{6211}\x{6212}' .
+'\x{6213}\x{6214}\x{6215}\x{6216}\x{6217}\x{6218}\x{6219}\x{621A}\x{621B}' .
+'\x{621C}\x{621D}\x{621E}\x{621F}\x{6220}\x{6221}\x{6222}\x{6223}\x{6224}' .
+'\x{6225}\x{6226}\x{6227}\x{6228}\x{6229}\x{622A}\x{622B}\x{622C}\x{622D}' .
+'\x{622E}\x{622F}\x{6230}\x{6231}\x{6232}\x{6233}\x{6234}\x{6236}\x{6237}' .
+'\x{6238}\x{623A}\x{623B}\x{623C}\x{623D}\x{623E}\x{623F}\x{6240}\x{6241}' .
+'\x{6242}\x{6243}\x{6244}\x{6245}\x{6246}\x{6247}\x{6248}\x{6249}\x{624A}' .
+'\x{624B}\x{624C}\x{624D}\x{624E}\x{624F}\x{6250}\x{6251}\x{6252}\x{6253}' .
+'\x{6254}\x{6255}\x{6256}\x{6258}\x{6259}\x{625A}\x{625B}\x{625C}\x{625D}' .
+'\x{625E}\x{625F}\x{6260}\x{6261}\x{6262}\x{6263}\x{6264}\x{6265}\x{6266}' .
+'\x{6267}\x{6268}\x{6269}\x{626A}\x{626B}\x{626C}\x{626D}\x{626E}\x{626F}' .
+'\x{6270}\x{6271}\x{6272}\x{6273}\x{6274}\x{6275}\x{6276}\x{6277}\x{6278}' .
+'\x{6279}\x{627A}\x{627B}\x{627C}\x{627D}\x{627E}\x{627F}\x{6280}\x{6281}' .
+'\x{6283}\x{6284}\x{6285}\x{6286}\x{6287}\x{6288}\x{6289}\x{628A}\x{628B}' .
+'\x{628C}\x{628E}\x{628F}\x{6290}\x{6291}\x{6292}\x{6293}\x{6294}\x{6295}' .
+'\x{6296}\x{6297}\x{6298}\x{6299}\x{629A}\x{629B}\x{629C}\x{629E}\x{629F}' .
+'\x{62A0}\x{62A1}\x{62A2}\x{62A3}\x{62A4}\x{62A5}\x{62A7}\x{62A8}\x{62A9}' .
+'\x{62AA}\x{62AB}\x{62AC}\x{62AD}\x{62AE}\x{62AF}\x{62B0}\x{62B1}\x{62B2}' .
+'\x{62B3}\x{62B4}\x{62B5}\x{62B6}\x{62B7}\x{62B8}\x{62B9}\x{62BA}\x{62BB}' .
+'\x{62BC}\x{62BD}\x{62BE}\x{62BF}\x{62C0}\x{62C1}\x{62C2}\x{62C3}\x{62C4}' .
+'\x{62C5}\x{62C6}\x{62C7}\x{62C8}\x{62C9}\x{62CA}\x{62CB}\x{62CC}\x{62CD}' .
+'\x{62CE}\x{62CF}\x{62D0}\x{62D1}\x{62D2}\x{62D3}\x{62D4}\x{62D5}\x{62D6}' .
+'\x{62D7}\x{62D8}\x{62D9}\x{62DA}\x{62DB}\x{62DC}\x{62DD}\x{62DF}\x{62E0}' .
+'\x{62E1}\x{62E2}\x{62E3}\x{62E4}\x{62E5}\x{62E6}\x{62E7}\x{62E8}\x{62E9}' .
+'\x{62EB}\x{62EC}\x{62ED}\x{62EE}\x{62EF}\x{62F0}\x{62F1}\x{62F2}\x{62F3}' .
+'\x{62F4}\x{62F5}\x{62F6}\x{62F7}\x{62F8}\x{62F9}\x{62FA}\x{62FB}\x{62FC}' .
+'\x{62FD}\x{62FE}\x{62FF}\x{6300}\x{6301}\x{6302}\x{6303}\x{6304}\x{6305}' .
+'\x{6306}\x{6307}\x{6308}\x{6309}\x{630B}\x{630C}\x{630D}\x{630E}\x{630F}' .
+'\x{6310}\x{6311}\x{6312}\x{6313}\x{6314}\x{6315}\x{6316}\x{6318}\x{6319}' .
+'\x{631A}\x{631B}\x{631C}\x{631D}\x{631E}\x{631F}\x{6320}\x{6321}\x{6322}' .
+'\x{6323}\x{6324}\x{6325}\x{6326}\x{6327}\x{6328}\x{6329}\x{632A}\x{632B}' .
+'\x{632C}\x{632D}\x{632E}\x{632F}\x{6330}\x{6332}\x{6333}\x{6334}\x{6336}' .
+'\x{6338}\x{6339}\x{633A}\x{633B}\x{633C}\x{633D}\x{633E}\x{6340}\x{6341}' .
+'\x{6342}\x{6343}\x{6344}\x{6345}\x{6346}\x{6347}\x{6348}\x{6349}\x{634A}' .
+'\x{634B}\x{634C}\x{634D}\x{634E}\x{634F}\x{6350}\x{6351}\x{6352}\x{6353}' .
+'\x{6354}\x{6355}\x{6356}\x{6357}\x{6358}\x{6359}\x{635A}\x{635C}\x{635D}' .
+'\x{635E}\x{635F}\x{6360}\x{6361}\x{6362}\x{6363}\x{6364}\x{6365}\x{6366}' .
+'\x{6367}\x{6368}\x{6369}\x{636A}\x{636B}\x{636C}\x{636D}\x{636E}\x{636F}' .
+'\x{6370}\x{6371}\x{6372}\x{6373}\x{6374}\x{6375}\x{6376}\x{6377}\x{6378}' .
+'\x{6379}\x{637A}\x{637B}\x{637C}\x{637D}\x{637E}\x{6380}\x{6381}\x{6382}' .
+'\x{6383}\x{6384}\x{6385}\x{6386}\x{6387}\x{6388}\x{6389}\x{638A}\x{638C}' .
+'\x{638D}\x{638E}\x{638F}\x{6390}\x{6391}\x{6392}\x{6394}\x{6395}\x{6396}' .
+'\x{6397}\x{6398}\x{6399}\x{639A}\x{639B}\x{639C}\x{639D}\x{639E}\x{639F}' .
+'\x{63A0}\x{63A1}\x{63A2}\x{63A3}\x{63A4}\x{63A5}\x{63A6}\x{63A7}\x{63A8}' .
+'\x{63A9}\x{63AA}\x{63AB}\x{63AC}\x{63AD}\x{63AE}\x{63AF}\x{63B0}\x{63B1}' .
+'\x{63B2}\x{63B3}\x{63B4}\x{63B5}\x{63B6}\x{63B7}\x{63B8}\x{63B9}\x{63BA}' .
+'\x{63BC}\x{63BD}\x{63BE}\x{63BF}\x{63C0}\x{63C1}\x{63C2}\x{63C3}\x{63C4}' .
+'\x{63C5}\x{63C6}\x{63C7}\x{63C8}\x{63C9}\x{63CA}\x{63CB}\x{63CC}\x{63CD}' .
+'\x{63CE}\x{63CF}\x{63D0}\x{63D2}\x{63D3}\x{63D4}\x{63D5}\x{63D6}\x{63D7}' .
+'\x{63D8}\x{63D9}\x{63DA}\x{63DB}\x{63DC}\x{63DD}\x{63DE}\x{63DF}\x{63E0}' .
+'\x{63E1}\x{63E2}\x{63E3}\x{63E4}\x{63E5}\x{63E6}\x{63E7}\x{63E8}\x{63E9}' .
+'\x{63EA}\x{63EB}\x{63EC}\x{63ED}\x{63EE}\x{63EF}\x{63F0}\x{63F1}\x{63F2}' .
+'\x{63F3}\x{63F4}\x{63F5}\x{63F6}\x{63F7}\x{63F8}\x{63F9}\x{63FA}\x{63FB}' .
+'\x{63FC}\x{63FD}\x{63FE}\x{63FF}\x{6400}\x{6401}\x{6402}\x{6403}\x{6404}' .
+'\x{6405}\x{6406}\x{6408}\x{6409}\x{640A}\x{640B}\x{640C}\x{640D}\x{640E}' .
+'\x{640F}\x{6410}\x{6411}\x{6412}\x{6413}\x{6414}\x{6415}\x{6416}\x{6417}' .
+'\x{6418}\x{6419}\x{641A}\x{641B}\x{641C}\x{641D}\x{641E}\x{641F}\x{6420}' .
+'\x{6421}\x{6422}\x{6423}\x{6424}\x{6425}\x{6426}\x{6427}\x{6428}\x{6429}' .
+'\x{642A}\x{642B}\x{642C}\x{642D}\x{642E}\x{642F}\x{6430}\x{6431}\x{6432}' .
+'\x{6433}\x{6434}\x{6435}\x{6436}\x{6437}\x{6438}\x{6439}\x{643A}\x{643D}' .
+'\x{643E}\x{643F}\x{6440}\x{6441}\x{6443}\x{6444}\x{6445}\x{6446}\x{6447}' .
+'\x{6448}\x{644A}\x{644B}\x{644C}\x{644D}\x{644E}\x{644F}\x{6450}\x{6451}' .
+'\x{6452}\x{6453}\x{6454}\x{6455}\x{6456}\x{6457}\x{6458}\x{6459}\x{645B}' .
+'\x{645C}\x{645D}\x{645E}\x{645F}\x{6460}\x{6461}\x{6462}\x{6463}\x{6464}' .
+'\x{6465}\x{6466}\x{6467}\x{6468}\x{6469}\x{646A}\x{646B}\x{646C}\x{646D}' .
+'\x{646E}\x{646F}\x{6470}\x{6471}\x{6472}\x{6473}\x{6474}\x{6475}\x{6476}' .
+'\x{6477}\x{6478}\x{6479}\x{647A}\x{647B}\x{647C}\x{647D}\x{647F}\x{6480}' .
+'\x{6481}\x{6482}\x{6483}\x{6484}\x{6485}\x{6487}\x{6488}\x{6489}\x{648A}' .
+'\x{648B}\x{648C}\x{648D}\x{648E}\x{648F}\x{6490}\x{6491}\x{6492}\x{6493}' .
+'\x{6494}\x{6495}\x{6496}\x{6497}\x{6498}\x{6499}\x{649A}\x{649B}\x{649C}' .
+'\x{649D}\x{649E}\x{649F}\x{64A0}\x{64A2}\x{64A3}\x{64A4}\x{64A5}\x{64A6}' .
+'\x{64A7}\x{64A8}\x{64A9}\x{64AA}\x{64AB}\x{64AC}\x{64AD}\x{64AE}\x{64B0}' .
+'\x{64B1}\x{64B2}\x{64B3}\x{64B4}\x{64B5}\x{64B7}\x{64B8}\x{64B9}\x{64BA}' .
+'\x{64BB}\x{64BC}\x{64BD}\x{64BE}\x{64BF}\x{64C0}\x{64C1}\x{64C2}\x{64C3}' .
+'\x{64C4}\x{64C5}\x{64C6}\x{64C7}\x{64C9}\x{64CA}\x{64CB}\x{64CC}\x{64CD}' .
+'\x{64CE}\x{64CF}\x{64D0}\x{64D1}\x{64D2}\x{64D3}\x{64D4}\x{64D6}\x{64D7}' .
+'\x{64D8}\x{64D9}\x{64DA}\x{64DB}\x{64DC}\x{64DD}\x{64DE}\x{64DF}\x{64E0}' .
+'\x{64E2}\x{64E3}\x{64E4}\x{64E6}\x{64E7}\x{64E8}\x{64E9}\x{64EA}\x{64EB}' .
+'\x{64EC}\x{64ED}\x{64EF}\x{64F0}\x{64F1}\x{64F2}\x{64F3}\x{64F4}\x{64F6}' .
+'\x{64F7}\x{64F8}\x{64FA}\x{64FB}\x{64FC}\x{64FD}\x{64FE}\x{64FF}\x{6500}' .
+'\x{6501}\x{6503}\x{6504}\x{6505}\x{6506}\x{6507}\x{6508}\x{6509}\x{650B}' .
+'\x{650C}\x{650D}\x{650E}\x{650F}\x{6510}\x{6511}\x{6512}\x{6513}\x{6514}' .
+'\x{6515}\x{6516}\x{6517}\x{6518}\x{6519}\x{651A}\x{651B}\x{651C}\x{651D}' .
+'\x{651E}\x{6520}\x{6521}\x{6522}\x{6523}\x{6524}\x{6525}\x{6526}\x{6527}' .
+'\x{6529}\x{652A}\x{652B}\x{652C}\x{652D}\x{652E}\x{652F}\x{6530}\x{6531}' .
+'\x{6532}\x{6533}\x{6534}\x{6535}\x{6536}\x{6537}\x{6538}\x{6539}\x{653A}' .
+'\x{653B}\x{653C}\x{653D}\x{653E}\x{653F}\x{6541}\x{6543}\x{6544}\x{6545}' .
+'\x{6546}\x{6547}\x{6548}\x{6549}\x{654A}\x{654B}\x{654C}\x{654D}\x{654E}' .
+'\x{654F}\x{6550}\x{6551}\x{6552}\x{6553}\x{6554}\x{6555}\x{6556}\x{6557}' .
+'\x{6558}\x{6559}\x{655B}\x{655C}\x{655D}\x{655E}\x{6560}\x{6561}\x{6562}' .
+'\x{6563}\x{6564}\x{6565}\x{6566}\x{6567}\x{6568}\x{6569}\x{656A}\x{656B}' .
+'\x{656C}\x{656E}\x{656F}\x{6570}\x{6571}\x{6572}\x{6573}\x{6574}\x{6575}' .
+'\x{6576}\x{6577}\x{6578}\x{6579}\x{657A}\x{657B}\x{657C}\x{657E}\x{657F}' .
+'\x{6580}\x{6581}\x{6582}\x{6583}\x{6584}\x{6585}\x{6586}\x{6587}\x{6588}' .
+'\x{6589}\x{658B}\x{658C}\x{658D}\x{658E}\x{658F}\x{6590}\x{6591}\x{6592}' .
+'\x{6593}\x{6594}\x{6595}\x{6596}\x{6597}\x{6598}\x{6599}\x{659B}\x{659C}' .
+'\x{659D}\x{659E}\x{659F}\x{65A0}\x{65A1}\x{65A2}\x{65A3}\x{65A4}\x{65A5}' .
+'\x{65A6}\x{65A7}\x{65A8}\x{65A9}\x{65AA}\x{65AB}\x{65AC}\x{65AD}\x{65AE}' .
+'\x{65AF}\x{65B0}\x{65B1}\x{65B2}\x{65B3}\x{65B4}\x{65B6}\x{65B7}\x{65B8}' .
+'\x{65B9}\x{65BA}\x{65BB}\x{65BC}\x{65BD}\x{65BF}\x{65C0}\x{65C1}\x{65C2}' .
+'\x{65C3}\x{65C4}\x{65C5}\x{65C6}\x{65C7}\x{65CA}\x{65CB}\x{65CC}\x{65CD}' .
+'\x{65CE}\x{65CF}\x{65D0}\x{65D2}\x{65D3}\x{65D4}\x{65D5}\x{65D6}\x{65D7}' .
+'\x{65DA}\x{65DB}\x{65DD}\x{65DE}\x{65DF}\x{65E0}\x{65E1}\x{65E2}\x{65E3}' .
+'\x{65E5}\x{65E6}\x{65E7}\x{65E8}\x{65E9}\x{65EB}\x{65EC}\x{65ED}\x{65EE}' .
+'\x{65EF}\x{65F0}\x{65F1}\x{65F2}\x{65F3}\x{65F4}\x{65F5}\x{65F6}\x{65F7}' .
+'\x{65F8}\x{65FA}\x{65FB}\x{65FC}\x{65FD}\x{6600}\x{6601}\x{6602}\x{6603}' .
+'\x{6604}\x{6605}\x{6606}\x{6607}\x{6608}\x{6609}\x{660A}\x{660B}\x{660C}' .
+'\x{660D}\x{660E}\x{660F}\x{6610}\x{6611}\x{6612}\x{6613}\x{6614}\x{6615}' .
+'\x{6616}\x{6618}\x{6619}\x{661A}\x{661B}\x{661C}\x{661D}\x{661F}\x{6620}' .
+'\x{6621}\x{6622}\x{6623}\x{6624}\x{6625}\x{6626}\x{6627}\x{6628}\x{6629}' .
+'\x{662A}\x{662B}\x{662D}\x{662E}\x{662F}\x{6630}\x{6631}\x{6632}\x{6633}' .
+'\x{6634}\x{6635}\x{6636}\x{6639}\x{663A}\x{663C}\x{663D}\x{663E}\x{6640}' .
+'\x{6641}\x{6642}\x{6643}\x{6644}\x{6645}\x{6646}\x{6647}\x{6649}\x{664A}' .
+'\x{664B}\x{664C}\x{664E}\x{664F}\x{6650}\x{6651}\x{6652}\x{6653}\x{6654}' .
+'\x{6655}\x{6656}\x{6657}\x{6658}\x{6659}\x{665A}\x{665B}\x{665C}\x{665D}' .
+'\x{665E}\x{665F}\x{6661}\x{6662}\x{6664}\x{6665}\x{6666}\x{6668}\x{6669}' .
+'\x{666A}\x{666B}\x{666C}\x{666D}\x{666E}\x{666F}\x{6670}\x{6671}\x{6672}' .
+'\x{6673}\x{6674}\x{6675}\x{6676}\x{6677}\x{6678}\x{6679}\x{667A}\x{667B}' .
+'\x{667C}\x{667D}\x{667E}\x{667F}\x{6680}\x{6681}\x{6682}\x{6683}\x{6684}' .
+'\x{6685}\x{6686}\x{6687}\x{6688}\x{6689}\x{668A}\x{668B}\x{668C}\x{668D}' .
+'\x{668E}\x{668F}\x{6690}\x{6691}\x{6693}\x{6694}\x{6695}\x{6696}\x{6697}' .
+'\x{6698}\x{6699}\x{669A}\x{669B}\x{669D}\x{669F}\x{66A0}\x{66A1}\x{66A2}' .
+'\x{66A3}\x{66A4}\x{66A5}\x{66A6}\x{66A7}\x{66A8}\x{66A9}\x{66AA}\x{66AB}' .
+'\x{66AE}\x{66AF}\x{66B0}\x{66B1}\x{66B2}\x{66B3}\x{66B4}\x{66B5}\x{66B6}' .
+'\x{66B7}\x{66B8}\x{66B9}\x{66BA}\x{66BB}\x{66BC}\x{66BD}\x{66BE}\x{66BF}' .
+'\x{66C0}\x{66C1}\x{66C2}\x{66C3}\x{66C4}\x{66C5}\x{66C6}\x{66C7}\x{66C8}' .
+'\x{66C9}\x{66CA}\x{66CB}\x{66CC}\x{66CD}\x{66CE}\x{66CF}\x{66D1}\x{66D2}' .
+'\x{66D4}\x{66D5}\x{66D6}\x{66D8}\x{66D9}\x{66DA}\x{66DB}\x{66DC}\x{66DD}' .
+'\x{66DE}\x{66E0}\x{66E1}\x{66E2}\x{66E3}\x{66E4}\x{66E5}\x{66E6}\x{66E7}' .
+'\x{66E8}\x{66E9}\x{66EA}\x{66EB}\x{66EC}\x{66ED}\x{66EE}\x{66F0}\x{66F1}' .
+'\x{66F2}\x{66F3}\x{66F4}\x{66F5}\x{66F6}\x{66F7}\x{66F8}\x{66F9}\x{66FA}' .
+'\x{66FB}\x{66FC}\x{66FE}\x{66FF}\x{6700}\x{6701}\x{6703}\x{6704}\x{6705}' .
+'\x{6706}\x{6708}\x{6709}\x{670A}\x{670B}\x{670C}\x{670D}\x{670E}\x{670F}' .
+'\x{6710}\x{6711}\x{6712}\x{6713}\x{6714}\x{6715}\x{6716}\x{6717}\x{6718}' .
+'\x{671A}\x{671B}\x{671C}\x{671D}\x{671E}\x{671F}\x{6720}\x{6721}\x{6722}' .
+'\x{6723}\x{6725}\x{6726}\x{6727}\x{6728}\x{672A}\x{672B}\x{672C}\x{672D}' .
+'\x{672E}\x{672F}\x{6730}\x{6731}\x{6732}\x{6733}\x{6734}\x{6735}\x{6736}' .
+'\x{6737}\x{6738}\x{6739}\x{673A}\x{673B}\x{673C}\x{673D}\x{673E}\x{673F}' .
+'\x{6740}\x{6741}\x{6742}\x{6743}\x{6744}\x{6745}\x{6746}\x{6747}\x{6748}' .
+'\x{6749}\x{674A}\x{674B}\x{674C}\x{674D}\x{674E}\x{674F}\x{6750}\x{6751}' .
+'\x{6752}\x{6753}\x{6754}\x{6755}\x{6756}\x{6757}\x{6758}\x{6759}\x{675A}' .
+'\x{675B}\x{675C}\x{675D}\x{675E}\x{675F}\x{6760}\x{6761}\x{6762}\x{6763}' .
+'\x{6764}\x{6765}\x{6766}\x{6768}\x{6769}\x{676A}\x{676B}\x{676C}\x{676D}' .
+'\x{676E}\x{676F}\x{6770}\x{6771}\x{6772}\x{6773}\x{6774}\x{6775}\x{6776}' .
+'\x{6777}\x{6778}\x{6779}\x{677A}\x{677B}\x{677C}\x{677D}\x{677E}\x{677F}' .
+'\x{6780}\x{6781}\x{6782}\x{6783}\x{6784}\x{6785}\x{6786}\x{6787}\x{6789}' .
+'\x{678A}\x{678B}\x{678C}\x{678D}\x{678E}\x{678F}\x{6790}\x{6791}\x{6792}' .
+'\x{6793}\x{6794}\x{6795}\x{6797}\x{6798}\x{6799}\x{679A}\x{679B}\x{679C}' .
+'\x{679D}\x{679E}\x{679F}\x{67A0}\x{67A1}\x{67A2}\x{67A3}\x{67A4}\x{67A5}' .
+'\x{67A6}\x{67A7}\x{67A8}\x{67AA}\x{67AB}\x{67AC}\x{67AD}\x{67AE}\x{67AF}' .
+'\x{67B0}\x{67B1}\x{67B2}\x{67B3}\x{67B4}\x{67B5}\x{67B6}\x{67B7}\x{67B8}' .
+'\x{67B9}\x{67BA}\x{67BB}\x{67BC}\x{67BE}\x{67C0}\x{67C1}\x{67C2}\x{67C3}' .
+'\x{67C4}\x{67C5}\x{67C6}\x{67C7}\x{67C8}\x{67C9}\x{67CA}\x{67CB}\x{67CC}' .
+'\x{67CD}\x{67CE}\x{67CF}\x{67D0}\x{67D1}\x{67D2}\x{67D3}\x{67D4}\x{67D6}' .
+'\x{67D8}\x{67D9}\x{67DA}\x{67DB}\x{67DC}\x{67DD}\x{67DE}\x{67DF}\x{67E0}' .
+'\x{67E1}\x{67E2}\x{67E3}\x{67E4}\x{67E5}\x{67E6}\x{67E7}\x{67E8}\x{67E9}' .
+'\x{67EA}\x{67EB}\x{67EC}\x{67ED}\x{67EE}\x{67EF}\x{67F0}\x{67F1}\x{67F2}' .
+'\x{67F3}\x{67F4}\x{67F5}\x{67F6}\x{67F7}\x{67F8}\x{67FA}\x{67FB}\x{67FC}' .
+'\x{67FD}\x{67FE}\x{67FF}\x{6800}\x{6802}\x{6803}\x{6804}\x{6805}\x{6806}' .
+'\x{6807}\x{6808}\x{6809}\x{680A}\x{680B}\x{680C}\x{680D}\x{680E}\x{680F}' .
+'\x{6810}\x{6811}\x{6812}\x{6813}\x{6814}\x{6816}\x{6817}\x{6818}\x{6819}' .
+'\x{681A}\x{681B}\x{681C}\x{681D}\x{681F}\x{6820}\x{6821}\x{6822}\x{6823}' .
+'\x{6824}\x{6825}\x{6826}\x{6828}\x{6829}\x{682A}\x{682B}\x{682C}\x{682D}' .
+'\x{682E}\x{682F}\x{6831}\x{6832}\x{6833}\x{6834}\x{6835}\x{6836}\x{6837}' .
+'\x{6838}\x{6839}\x{683A}\x{683B}\x{683C}\x{683D}\x{683E}\x{683F}\x{6840}' .
+'\x{6841}\x{6842}\x{6843}\x{6844}\x{6845}\x{6846}\x{6847}\x{6848}\x{6849}' .
+'\x{684A}\x{684B}\x{684C}\x{684D}\x{684E}\x{684F}\x{6850}\x{6851}\x{6852}' .
+'\x{6853}\x{6854}\x{6855}\x{6856}\x{6857}\x{685B}\x{685D}\x{6860}\x{6861}' .
+'\x{6862}\x{6863}\x{6864}\x{6865}\x{6866}\x{6867}\x{6868}\x{6869}\x{686A}' .
+'\x{686B}\x{686C}\x{686D}\x{686E}\x{686F}\x{6870}\x{6871}\x{6872}\x{6873}' .
+'\x{6874}\x{6875}\x{6876}\x{6877}\x{6878}\x{6879}\x{687B}\x{687C}\x{687D}' .
+'\x{687E}\x{687F}\x{6880}\x{6881}\x{6882}\x{6883}\x{6884}\x{6885}\x{6886}' .
+'\x{6887}\x{6888}\x{6889}\x{688A}\x{688B}\x{688C}\x{688D}\x{688E}\x{688F}' .
+'\x{6890}\x{6891}\x{6892}\x{6893}\x{6894}\x{6896}\x{6897}\x{6898}\x{689A}' .
+'\x{689B}\x{689C}\x{689D}\x{689E}\x{689F}\x{68A0}\x{68A1}\x{68A2}\x{68A3}' .
+'\x{68A4}\x{68A6}\x{68A7}\x{68A8}\x{68A9}\x{68AA}\x{68AB}\x{68AC}\x{68AD}' .
+'\x{68AE}\x{68AF}\x{68B0}\x{68B1}\x{68B2}\x{68B3}\x{68B4}\x{68B5}\x{68B6}' .
+'\x{68B7}\x{68B9}\x{68BB}\x{68BC}\x{68BD}\x{68BE}\x{68BF}\x{68C0}\x{68C1}' .
+'\x{68C2}\x{68C4}\x{68C6}\x{68C7}\x{68C8}\x{68C9}\x{68CA}\x{68CB}\x{68CC}' .
+'\x{68CD}\x{68CE}\x{68CF}\x{68D0}\x{68D1}\x{68D2}\x{68D3}\x{68D4}\x{68D5}' .
+'\x{68D6}\x{68D7}\x{68D8}\x{68DA}\x{68DB}\x{68DC}\x{68DD}\x{68DE}\x{68DF}' .
+'\x{68E0}\x{68E1}\x{68E3}\x{68E4}\x{68E6}\x{68E7}\x{68E8}\x{68E9}\x{68EA}' .
+'\x{68EB}\x{68EC}\x{68ED}\x{68EE}\x{68EF}\x{68F0}\x{68F1}\x{68F2}\x{68F3}' .
+'\x{68F4}\x{68F5}\x{68F6}\x{68F7}\x{68F8}\x{68F9}\x{68FA}\x{68FB}\x{68FC}' .
+'\x{68FD}\x{68FE}\x{68FF}\x{6901}\x{6902}\x{6903}\x{6904}\x{6905}\x{6906}' .
+'\x{6907}\x{6908}\x{690A}\x{690B}\x{690C}\x{690D}\x{690E}\x{690F}\x{6910}' .
+'\x{6911}\x{6912}\x{6913}\x{6914}\x{6915}\x{6916}\x{6917}\x{6918}\x{6919}' .
+'\x{691A}\x{691B}\x{691C}\x{691D}\x{691E}\x{691F}\x{6920}\x{6921}\x{6922}' .
+'\x{6923}\x{6924}\x{6925}\x{6926}\x{6927}\x{6928}\x{6929}\x{692A}\x{692B}' .
+'\x{692C}\x{692D}\x{692E}\x{692F}\x{6930}\x{6931}\x{6932}\x{6933}\x{6934}' .
+'\x{6935}\x{6936}\x{6937}\x{6938}\x{6939}\x{693A}\x{693B}\x{693C}\x{693D}' .
+'\x{693F}\x{6940}\x{6941}\x{6942}\x{6943}\x{6944}\x{6945}\x{6946}\x{6947}' .
+'\x{6948}\x{6949}\x{694A}\x{694B}\x{694C}\x{694E}\x{694F}\x{6950}\x{6951}' .
+'\x{6952}\x{6953}\x{6954}\x{6955}\x{6956}\x{6957}\x{6958}\x{6959}\x{695A}' .
+'\x{695B}\x{695C}\x{695D}\x{695E}\x{695F}\x{6960}\x{6961}\x{6962}\x{6963}' .
+'\x{6964}\x{6965}\x{6966}\x{6967}\x{6968}\x{6969}\x{696A}\x{696B}\x{696C}' .
+'\x{696D}\x{696E}\x{696F}\x{6970}\x{6971}\x{6972}\x{6973}\x{6974}\x{6975}' .
+'\x{6976}\x{6977}\x{6978}\x{6979}\x{697A}\x{697B}\x{697C}\x{697D}\x{697E}' .
+'\x{697F}\x{6980}\x{6981}\x{6982}\x{6983}\x{6984}\x{6985}\x{6986}\x{6987}' .
+'\x{6988}\x{6989}\x{698A}\x{698B}\x{698C}\x{698D}\x{698E}\x{698F}\x{6990}' .
+'\x{6991}\x{6992}\x{6993}\x{6994}\x{6995}\x{6996}\x{6997}\x{6998}\x{6999}' .
+'\x{699A}\x{699B}\x{699C}\x{699D}\x{699E}\x{69A0}\x{69A1}\x{69A3}\x{69A4}' .
+'\x{69A5}\x{69A6}\x{69A7}\x{69A8}\x{69A9}\x{69AA}\x{69AB}\x{69AC}\x{69AD}' .
+'\x{69AE}\x{69AF}\x{69B0}\x{69B1}\x{69B2}\x{69B3}\x{69B4}\x{69B5}\x{69B6}' .
+'\x{69B7}\x{69B8}\x{69B9}\x{69BA}\x{69BB}\x{69BC}\x{69BD}\x{69BE}\x{69BF}' .
+'\x{69C1}\x{69C2}\x{69C3}\x{69C4}\x{69C5}\x{69C6}\x{69C7}\x{69C8}\x{69C9}' .
+'\x{69CA}\x{69CB}\x{69CC}\x{69CD}\x{69CE}\x{69CF}\x{69D0}\x{69D3}\x{69D4}' .
+'\x{69D8}\x{69D9}\x{69DA}\x{69DB}\x{69DC}\x{69DD}\x{69DE}\x{69DF}\x{69E0}' .
+'\x{69E1}\x{69E2}\x{69E3}\x{69E4}\x{69E5}\x{69E6}\x{69E7}\x{69E8}\x{69E9}' .
+'\x{69EA}\x{69EB}\x{69EC}\x{69ED}\x{69EE}\x{69EF}\x{69F0}\x{69F1}\x{69F2}' .
+'\x{69F3}\x{69F4}\x{69F5}\x{69F6}\x{69F7}\x{69F8}\x{69FA}\x{69FB}\x{69FC}' .
+'\x{69FD}\x{69FE}\x{69FF}\x{6A00}\x{6A01}\x{6A02}\x{6A04}\x{6A05}\x{6A06}' .
+'\x{6A07}\x{6A08}\x{6A09}\x{6A0A}\x{6A0B}\x{6A0D}\x{6A0E}\x{6A0F}\x{6A10}' .
+'\x{6A11}\x{6A12}\x{6A13}\x{6A14}\x{6A15}\x{6A16}\x{6A17}\x{6A18}\x{6A19}' .
+'\x{6A1A}\x{6A1B}\x{6A1D}\x{6A1E}\x{6A1F}\x{6A20}\x{6A21}\x{6A22}\x{6A23}' .
+'\x{6A25}\x{6A26}\x{6A27}\x{6A28}\x{6A29}\x{6A2A}\x{6A2B}\x{6A2C}\x{6A2D}' .
+'\x{6A2E}\x{6A2F}\x{6A30}\x{6A31}\x{6A32}\x{6A33}\x{6A34}\x{6A35}\x{6A36}' .
+'\x{6A38}\x{6A39}\x{6A3A}\x{6A3B}\x{6A3C}\x{6A3D}\x{6A3E}\x{6A3F}\x{6A40}' .
+'\x{6A41}\x{6A42}\x{6A43}\x{6A44}\x{6A45}\x{6A46}\x{6A47}\x{6A48}\x{6A49}' .
+'\x{6A4B}\x{6A4C}\x{6A4D}\x{6A4E}\x{6A4F}\x{6A50}\x{6A51}\x{6A52}\x{6A54}' .
+'\x{6A55}\x{6A56}\x{6A57}\x{6A58}\x{6A59}\x{6A5A}\x{6A5B}\x{6A5D}\x{6A5E}' .
+'\x{6A5F}\x{6A60}\x{6A61}\x{6A62}\x{6A63}\x{6A64}\x{6A65}\x{6A66}\x{6A67}' .
+'\x{6A68}\x{6A69}\x{6A6A}\x{6A6B}\x{6A6C}\x{6A6D}\x{6A6F}\x{6A71}\x{6A72}' .
+'\x{6A73}\x{6A74}\x{6A75}\x{6A76}\x{6A77}\x{6A78}\x{6A79}\x{6A7A}\x{6A7B}' .
+'\x{6A7C}\x{6A7D}\x{6A7E}\x{6A7F}\x{6A80}\x{6A81}\x{6A82}\x{6A83}\x{6A84}' .
+'\x{6A85}\x{6A87}\x{6A88}\x{6A89}\x{6A8B}\x{6A8C}\x{6A8D}\x{6A8E}\x{6A90}' .
+'\x{6A91}\x{6A92}\x{6A93}\x{6A94}\x{6A95}\x{6A96}\x{6A97}\x{6A98}\x{6A9A}' .
+'\x{6A9B}\x{6A9C}\x{6A9E}\x{6A9F}\x{6AA0}\x{6AA1}\x{6AA2}\x{6AA3}\x{6AA4}' .
+'\x{6AA5}\x{6AA6}\x{6AA7}\x{6AA8}\x{6AA9}\x{6AAB}\x{6AAC}\x{6AAD}\x{6AAE}' .
+'\x{6AAF}\x{6AB0}\x{6AB2}\x{6AB3}\x{6AB4}\x{6AB5}\x{6AB6}\x{6AB7}\x{6AB8}' .
+'\x{6AB9}\x{6ABA}\x{6ABB}\x{6ABC}\x{6ABD}\x{6ABF}\x{6AC1}\x{6AC2}\x{6AC3}' .
+'\x{6AC5}\x{6AC6}\x{6AC7}\x{6ACA}\x{6ACB}\x{6ACC}\x{6ACD}\x{6ACE}\x{6ACF}' .
+'\x{6AD0}\x{6AD1}\x{6AD2}\x{6AD3}\x{6AD4}\x{6AD5}\x{6AD6}\x{6AD7}\x{6AD9}' .
+'\x{6ADA}\x{6ADB}\x{6ADC}\x{6ADD}\x{6ADE}\x{6ADF}\x{6AE0}\x{6AE1}\x{6AE2}' .
+'\x{6AE3}\x{6AE4}\x{6AE5}\x{6AE6}\x{6AE7}\x{6AE8}\x{6AEA}\x{6AEB}\x{6AEC}' .
+'\x{6AED}\x{6AEE}\x{6AEF}\x{6AF0}\x{6AF1}\x{6AF2}\x{6AF3}\x{6AF4}\x{6AF5}' .
+'\x{6AF6}\x{6AF7}\x{6AF8}\x{6AF9}\x{6AFA}\x{6AFB}\x{6AFC}\x{6AFD}\x{6AFE}' .
+'\x{6AFF}\x{6B00}\x{6B01}\x{6B02}\x{6B03}\x{6B04}\x{6B05}\x{6B06}\x{6B07}' .
+'\x{6B08}\x{6B09}\x{6B0A}\x{6B0B}\x{6B0C}\x{6B0D}\x{6B0F}\x{6B10}\x{6B11}' .
+'\x{6B12}\x{6B13}\x{6B14}\x{6B15}\x{6B16}\x{6B17}\x{6B18}\x{6B19}\x{6B1A}' .
+'\x{6B1C}\x{6B1D}\x{6B1E}\x{6B1F}\x{6B20}\x{6B21}\x{6B22}\x{6B23}\x{6B24}' .
+'\x{6B25}\x{6B26}\x{6B27}\x{6B28}\x{6B29}\x{6B2A}\x{6B2B}\x{6B2C}\x{6B2D}' .
+'\x{6B2F}\x{6B30}\x{6B31}\x{6B32}\x{6B33}\x{6B34}\x{6B36}\x{6B37}\x{6B38}' .
+'\x{6B39}\x{6B3A}\x{6B3B}\x{6B3C}\x{6B3D}\x{6B3E}\x{6B3F}\x{6B41}\x{6B42}' .
+'\x{6B43}\x{6B44}\x{6B45}\x{6B46}\x{6B47}\x{6B48}\x{6B49}\x{6B4A}\x{6B4B}' .
+'\x{6B4C}\x{6B4D}\x{6B4E}\x{6B4F}\x{6B50}\x{6B51}\x{6B52}\x{6B53}\x{6B54}' .
+'\x{6B55}\x{6B56}\x{6B59}\x{6B5A}\x{6B5B}\x{6B5C}\x{6B5E}\x{6B5F}\x{6B60}' .
+'\x{6B61}\x{6B62}\x{6B63}\x{6B64}\x{6B65}\x{6B66}\x{6B67}\x{6B69}\x{6B6A}' .
+'\x{6B6B}\x{6B6D}\x{6B6F}\x{6B70}\x{6B72}\x{6B73}\x{6B74}\x{6B76}\x{6B77}' .
+'\x{6B78}\x{6B79}\x{6B7A}\x{6B7B}\x{6B7C}\x{6B7E}\x{6B7F}\x{6B80}\x{6B81}' .
+'\x{6B82}\x{6B83}\x{6B84}\x{6B85}\x{6B86}\x{6B87}\x{6B88}\x{6B89}\x{6B8A}' .
+'\x{6B8B}\x{6B8C}\x{6B8D}\x{6B8E}\x{6B8F}\x{6B90}\x{6B91}\x{6B92}\x{6B93}' .
+'\x{6B94}\x{6B95}\x{6B96}\x{6B97}\x{6B98}\x{6B99}\x{6B9A}\x{6B9B}\x{6B9C}' .
+'\x{6B9D}\x{6B9E}\x{6B9F}\x{6BA0}\x{6BA1}\x{6BA2}\x{6BA3}\x{6BA4}\x{6BA5}' .
+'\x{6BA6}\x{6BA7}\x{6BA8}\x{6BA9}\x{6BAA}\x{6BAB}\x{6BAC}\x{6BAD}\x{6BAE}' .
+'\x{6BAF}\x{6BB0}\x{6BB2}\x{6BB3}\x{6BB4}\x{6BB5}\x{6BB6}\x{6BB7}\x{6BB9}' .
+'\x{6BBA}\x{6BBB}\x{6BBC}\x{6BBD}\x{6BBE}\x{6BBF}\x{6BC0}\x{6BC1}\x{6BC2}' .
+'\x{6BC3}\x{6BC4}\x{6BC5}\x{6BC6}\x{6BC7}\x{6BC8}\x{6BC9}\x{6BCA}\x{6BCB}' .
+'\x{6BCC}\x{6BCD}\x{6BCE}\x{6BCF}\x{6BD0}\x{6BD1}\x{6BD2}\x{6BD3}\x{6BD4}' .
+'\x{6BD5}\x{6BD6}\x{6BD7}\x{6BD8}\x{6BD9}\x{6BDA}\x{6BDB}\x{6BDC}\x{6BDD}' .
+'\x{6BDE}\x{6BDF}\x{6BE0}\x{6BE1}\x{6BE2}\x{6BE3}\x{6BE4}\x{6BE5}\x{6BE6}' .
+'\x{6BE7}\x{6BE8}\x{6BEA}\x{6BEB}\x{6BEC}\x{6BED}\x{6BEE}\x{6BEF}\x{6BF0}' .
+'\x{6BF2}\x{6BF3}\x{6BF5}\x{6BF6}\x{6BF7}\x{6BF8}\x{6BF9}\x{6BFB}\x{6BFC}' .
+'\x{6BFD}\x{6BFE}\x{6BFF}\x{6C00}\x{6C01}\x{6C02}\x{6C03}\x{6C04}\x{6C05}' .
+'\x{6C06}\x{6C07}\x{6C08}\x{6C09}\x{6C0B}\x{6C0C}\x{6C0D}\x{6C0E}\x{6C0F}' .
+'\x{6C10}\x{6C11}\x{6C12}\x{6C13}\x{6C14}\x{6C15}\x{6C16}\x{6C18}\x{6C19}' .
+'\x{6C1A}\x{6C1B}\x{6C1D}\x{6C1E}\x{6C1F}\x{6C20}\x{6C21}\x{6C22}\x{6C23}' .
+'\x{6C24}\x{6C25}\x{6C26}\x{6C27}\x{6C28}\x{6C29}\x{6C2A}\x{6C2B}\x{6C2C}' .
+'\x{6C2E}\x{6C2F}\x{6C30}\x{6C31}\x{6C32}\x{6C33}\x{6C34}\x{6C35}\x{6C36}' .
+'\x{6C37}\x{6C38}\x{6C3A}\x{6C3B}\x{6C3D}\x{6C3E}\x{6C3F}\x{6C40}\x{6C41}' .
+'\x{6C42}\x{6C43}\x{6C44}\x{6C46}\x{6C47}\x{6C48}\x{6C49}\x{6C4A}\x{6C4B}' .
+'\x{6C4C}\x{6C4D}\x{6C4E}\x{6C4F}\x{6C50}\x{6C51}\x{6C52}\x{6C53}\x{6C54}' .
+'\x{6C55}\x{6C56}\x{6C57}\x{6C58}\x{6C59}\x{6C5A}\x{6C5B}\x{6C5C}\x{6C5D}' .
+'\x{6C5E}\x{6C5F}\x{6C60}\x{6C61}\x{6C62}\x{6C63}\x{6C64}\x{6C65}\x{6C66}' .
+'\x{6C67}\x{6C68}\x{6C69}\x{6C6A}\x{6C6B}\x{6C6D}\x{6C6F}\x{6C70}\x{6C71}' .
+'\x{6C72}\x{6C73}\x{6C74}\x{6C75}\x{6C76}\x{6C77}\x{6C78}\x{6C79}\x{6C7A}' .
+'\x{6C7B}\x{6C7C}\x{6C7D}\x{6C7E}\x{6C7F}\x{6C80}\x{6C81}\x{6C82}\x{6C83}' .
+'\x{6C84}\x{6C85}\x{6C86}\x{6C87}\x{6C88}\x{6C89}\x{6C8A}\x{6C8B}\x{6C8C}' .
+'\x{6C8D}\x{6C8E}\x{6C8F}\x{6C90}\x{6C91}\x{6C92}\x{6C93}\x{6C94}\x{6C95}' .
+'\x{6C96}\x{6C97}\x{6C98}\x{6C99}\x{6C9A}\x{6C9B}\x{6C9C}\x{6C9D}\x{6C9E}' .
+'\x{6C9F}\x{6CA1}\x{6CA2}\x{6CA3}\x{6CA4}\x{6CA5}\x{6CA6}\x{6CA7}\x{6CA8}' .
+'\x{6CA9}\x{6CAA}\x{6CAB}\x{6CAC}\x{6CAD}\x{6CAE}\x{6CAF}\x{6CB0}\x{6CB1}' .
+'\x{6CB2}\x{6CB3}\x{6CB4}\x{6CB5}\x{6CB6}\x{6CB7}\x{6CB8}\x{6CB9}\x{6CBA}' .
+'\x{6CBB}\x{6CBC}\x{6CBD}\x{6CBE}\x{6CBF}\x{6CC0}\x{6CC1}\x{6CC2}\x{6CC3}' .
+'\x{6CC4}\x{6CC5}\x{6CC6}\x{6CC7}\x{6CC8}\x{6CC9}\x{6CCA}\x{6CCB}\x{6CCC}' .
+'\x{6CCD}\x{6CCE}\x{6CCF}\x{6CD0}\x{6CD1}\x{6CD2}\x{6CD3}\x{6CD4}\x{6CD5}' .
+'\x{6CD6}\x{6CD7}\x{6CD9}\x{6CDA}\x{6CDB}\x{6CDC}\x{6CDD}\x{6CDE}\x{6CDF}' .
+'\x{6CE0}\x{6CE1}\x{6CE2}\x{6CE3}\x{6CE4}\x{6CE5}\x{6CE6}\x{6CE7}\x{6CE8}' .
+'\x{6CE9}\x{6CEA}\x{6CEB}\x{6CEC}\x{6CED}\x{6CEE}\x{6CEF}\x{6CF0}\x{6CF1}' .
+'\x{6CF2}\x{6CF3}\x{6CF5}\x{6CF6}\x{6CF7}\x{6CF8}\x{6CF9}\x{6CFA}\x{6CFB}' .
+'\x{6CFC}\x{6CFD}\x{6CFE}\x{6CFF}\x{6D00}\x{6D01}\x{6D03}\x{6D04}\x{6D05}' .
+'\x{6D06}\x{6D07}\x{6D08}\x{6D09}\x{6D0A}\x{6D0B}\x{6D0C}\x{6D0D}\x{6D0E}' .
+'\x{6D0F}\x{6D10}\x{6D11}\x{6D12}\x{6D13}\x{6D14}\x{6D15}\x{6D16}\x{6D17}' .
+'\x{6D18}\x{6D19}\x{6D1A}\x{6D1B}\x{6D1D}\x{6D1E}\x{6D1F}\x{6D20}\x{6D21}' .
+'\x{6D22}\x{6D23}\x{6D25}\x{6D26}\x{6D27}\x{6D28}\x{6D29}\x{6D2A}\x{6D2B}' .
+'\x{6D2C}\x{6D2D}\x{6D2E}\x{6D2F}\x{6D30}\x{6D31}\x{6D32}\x{6D33}\x{6D34}' .
+'\x{6D35}\x{6D36}\x{6D37}\x{6D38}\x{6D39}\x{6D3A}\x{6D3B}\x{6D3C}\x{6D3D}' .
+'\x{6D3E}\x{6D3F}\x{6D40}\x{6D41}\x{6D42}\x{6D43}\x{6D44}\x{6D45}\x{6D46}' .
+'\x{6D47}\x{6D48}\x{6D49}\x{6D4A}\x{6D4B}\x{6D4C}\x{6D4D}\x{6D4E}\x{6D4F}' .
+'\x{6D50}\x{6D51}\x{6D52}\x{6D53}\x{6D54}\x{6D55}\x{6D56}\x{6D57}\x{6D58}' .
+'\x{6D59}\x{6D5A}\x{6D5B}\x{6D5C}\x{6D5D}\x{6D5E}\x{6D5F}\x{6D60}\x{6D61}' .
+'\x{6D62}\x{6D63}\x{6D64}\x{6D65}\x{6D66}\x{6D67}\x{6D68}\x{6D69}\x{6D6A}' .
+'\x{6D6B}\x{6D6C}\x{6D6D}\x{6D6E}\x{6D6F}\x{6D70}\x{6D72}\x{6D73}\x{6D74}' .
+'\x{6D75}\x{6D76}\x{6D77}\x{6D78}\x{6D79}\x{6D7A}\x{6D7B}\x{6D7C}\x{6D7D}' .
+'\x{6D7E}\x{6D7F}\x{6D80}\x{6D82}\x{6D83}\x{6D84}\x{6D85}\x{6D86}\x{6D87}' .
+'\x{6D88}\x{6D89}\x{6D8A}\x{6D8B}\x{6D8C}\x{6D8D}\x{6D8E}\x{6D8F}\x{6D90}' .
+'\x{6D91}\x{6D92}\x{6D93}\x{6D94}\x{6D95}\x{6D97}\x{6D98}\x{6D99}\x{6D9A}' .
+'\x{6D9B}\x{6D9D}\x{6D9E}\x{6D9F}\x{6DA0}\x{6DA1}\x{6DA2}\x{6DA3}\x{6DA4}' .
+'\x{6DA5}\x{6DA6}\x{6DA7}\x{6DA8}\x{6DA9}\x{6DAA}\x{6DAB}\x{6DAC}\x{6DAD}' .
+'\x{6DAE}\x{6DAF}\x{6DB2}\x{6DB3}\x{6DB4}\x{6DB5}\x{6DB7}\x{6DB8}\x{6DB9}' .
+'\x{6DBA}\x{6DBB}\x{6DBC}\x{6DBD}\x{6DBE}\x{6DBF}\x{6DC0}\x{6DC1}\x{6DC2}' .
+'\x{6DC3}\x{6DC4}\x{6DC5}\x{6DC6}\x{6DC7}\x{6DC8}\x{6DC9}\x{6DCA}\x{6DCB}' .
+'\x{6DCC}\x{6DCD}\x{6DCE}\x{6DCF}\x{6DD0}\x{6DD1}\x{6DD2}\x{6DD3}\x{6DD4}' .
+'\x{6DD5}\x{6DD6}\x{6DD7}\x{6DD8}\x{6DD9}\x{6DDA}\x{6DDB}\x{6DDC}\x{6DDD}' .
+'\x{6DDE}\x{6DDF}\x{6DE0}\x{6DE1}\x{6DE2}\x{6DE3}\x{6DE4}\x{6DE5}\x{6DE6}' .
+'\x{6DE7}\x{6DE8}\x{6DE9}\x{6DEA}\x{6DEB}\x{6DEC}\x{6DED}\x{6DEE}\x{6DEF}' .
+'\x{6DF0}\x{6DF1}\x{6DF2}\x{6DF3}\x{6DF4}\x{6DF5}\x{6DF6}\x{6DF7}\x{6DF8}' .
+'\x{6DF9}\x{6DFA}\x{6DFB}\x{6DFC}\x{6DFD}\x{6E00}\x{6E03}\x{6E04}\x{6E05}' .
+'\x{6E07}\x{6E08}\x{6E09}\x{6E0A}\x{6E0B}\x{6E0C}\x{6E0D}\x{6E0E}\x{6E0F}' .
+'\x{6E10}\x{6E11}\x{6E14}\x{6E15}\x{6E16}\x{6E17}\x{6E19}\x{6E1A}\x{6E1B}' .
+'\x{6E1C}\x{6E1D}\x{6E1E}\x{6E1F}\x{6E20}\x{6E21}\x{6E22}\x{6E23}\x{6E24}' .
+'\x{6E25}\x{6E26}\x{6E27}\x{6E28}\x{6E29}\x{6E2B}\x{6E2C}\x{6E2D}\x{6E2E}' .
+'\x{6E2F}\x{6E30}\x{6E31}\x{6E32}\x{6E33}\x{6E34}\x{6E35}\x{6E36}\x{6E37}' .
+'\x{6E38}\x{6E39}\x{6E3A}\x{6E3B}\x{6E3C}\x{6E3D}\x{6E3E}\x{6E3F}\x{6E40}' .
+'\x{6E41}\x{6E42}\x{6E43}\x{6E44}\x{6E45}\x{6E46}\x{6E47}\x{6E48}\x{6E49}' .
+'\x{6E4A}\x{6E4B}\x{6E4D}\x{6E4E}\x{6E4F}\x{6E50}\x{6E51}\x{6E52}\x{6E53}' .
+'\x{6E54}\x{6E55}\x{6E56}\x{6E57}\x{6E58}\x{6E59}\x{6E5A}\x{6E5B}\x{6E5C}' .
+'\x{6E5D}\x{6E5E}\x{6E5F}\x{6E60}\x{6E61}\x{6E62}\x{6E63}\x{6E64}\x{6E65}' .
+'\x{6E66}\x{6E67}\x{6E68}\x{6E69}\x{6E6A}\x{6E6B}\x{6E6D}\x{6E6E}\x{6E6F}' .
+'\x{6E70}\x{6E71}\x{6E72}\x{6E73}\x{6E74}\x{6E75}\x{6E77}\x{6E78}\x{6E79}' .
+'\x{6E7E}\x{6E7F}\x{6E80}\x{6E81}\x{6E82}\x{6E83}\x{6E84}\x{6E85}\x{6E86}' .
+'\x{6E87}\x{6E88}\x{6E89}\x{6E8A}\x{6E8D}\x{6E8E}\x{6E8F}\x{6E90}\x{6E91}' .
+'\x{6E92}\x{6E93}\x{6E94}\x{6E96}\x{6E97}\x{6E98}\x{6E99}\x{6E9A}\x{6E9B}' .
+'\x{6E9C}\x{6E9D}\x{6E9E}\x{6E9F}\x{6EA0}\x{6EA1}\x{6EA2}\x{6EA3}\x{6EA4}' .
+'\x{6EA5}\x{6EA6}\x{6EA7}\x{6EA8}\x{6EA9}\x{6EAA}\x{6EAB}\x{6EAC}\x{6EAD}' .
+'\x{6EAE}\x{6EAF}\x{6EB0}\x{6EB1}\x{6EB2}\x{6EB3}\x{6EB4}\x{6EB5}\x{6EB6}' .
+'\x{6EB7}\x{6EB8}\x{6EB9}\x{6EBA}\x{6EBB}\x{6EBC}\x{6EBD}\x{6EBE}\x{6EBF}' .
+'\x{6EC0}\x{6EC1}\x{6EC2}\x{6EC3}\x{6EC4}\x{6EC5}\x{6EC6}\x{6EC7}\x{6EC8}' .
+'\x{6EC9}\x{6ECA}\x{6ECB}\x{6ECC}\x{6ECD}\x{6ECE}\x{6ECF}\x{6ED0}\x{6ED1}' .
+'\x{6ED2}\x{6ED3}\x{6ED4}\x{6ED5}\x{6ED6}\x{6ED7}\x{6ED8}\x{6ED9}\x{6EDA}' .
+'\x{6EDC}\x{6EDE}\x{6EDF}\x{6EE0}\x{6EE1}\x{6EE2}\x{6EE4}\x{6EE5}\x{6EE6}' .
+'\x{6EE7}\x{6EE8}\x{6EE9}\x{6EEA}\x{6EEB}\x{6EEC}\x{6EED}\x{6EEE}\x{6EEF}' .
+'\x{6EF0}\x{6EF1}\x{6EF2}\x{6EF3}\x{6EF4}\x{6EF5}\x{6EF6}\x{6EF7}\x{6EF8}' .
+'\x{6EF9}\x{6EFA}\x{6EFB}\x{6EFC}\x{6EFD}\x{6EFE}\x{6EFF}\x{6F00}\x{6F01}' .
+'\x{6F02}\x{6F03}\x{6F05}\x{6F06}\x{6F07}\x{6F08}\x{6F09}\x{6F0A}\x{6F0C}' .
+'\x{6F0D}\x{6F0E}\x{6F0F}\x{6F10}\x{6F11}\x{6F12}\x{6F13}\x{6F14}\x{6F15}' .
+'\x{6F16}\x{6F17}\x{6F18}\x{6F19}\x{6F1A}\x{6F1B}\x{6F1C}\x{6F1D}\x{6F1E}' .
+'\x{6F1F}\x{6F20}\x{6F21}\x{6F22}\x{6F23}\x{6F24}\x{6F25}\x{6F26}\x{6F27}' .
+'\x{6F28}\x{6F29}\x{6F2A}\x{6F2B}\x{6F2C}\x{6F2D}\x{6F2E}\x{6F2F}\x{6F30}' .
+'\x{6F31}\x{6F32}\x{6F33}\x{6F34}\x{6F35}\x{6F36}\x{6F37}\x{6F38}\x{6F39}' .
+'\x{6F3A}\x{6F3B}\x{6F3C}\x{6F3D}\x{6F3E}\x{6F3F}\x{6F40}\x{6F41}\x{6F43}' .
+'\x{6F44}\x{6F45}\x{6F46}\x{6F47}\x{6F49}\x{6F4B}\x{6F4C}\x{6F4D}\x{6F4E}' .
+'\x{6F4F}\x{6F50}\x{6F51}\x{6F52}\x{6F53}\x{6F54}\x{6F55}\x{6F56}\x{6F57}' .
+'\x{6F58}\x{6F59}\x{6F5A}\x{6F5B}\x{6F5C}\x{6F5D}\x{6F5E}\x{6F5F}\x{6F60}' .
+'\x{6F61}\x{6F62}\x{6F63}\x{6F64}\x{6F65}\x{6F66}\x{6F67}\x{6F68}\x{6F69}' .
+'\x{6F6A}\x{6F6B}\x{6F6C}\x{6F6D}\x{6F6E}\x{6F6F}\x{6F70}\x{6F71}\x{6F72}' .
+'\x{6F73}\x{6F74}\x{6F75}\x{6F76}\x{6F77}\x{6F78}\x{6F7A}\x{6F7B}\x{6F7C}' .
+'\x{6F7D}\x{6F7E}\x{6F7F}\x{6F80}\x{6F81}\x{6F82}\x{6F83}\x{6F84}\x{6F85}' .
+'\x{6F86}\x{6F87}\x{6F88}\x{6F89}\x{6F8A}\x{6F8B}\x{6F8C}\x{6F8D}\x{6F8E}' .
+'\x{6F8F}\x{6F90}\x{6F91}\x{6F92}\x{6F93}\x{6F94}\x{6F95}\x{6F96}\x{6F97}' .
+'\x{6F99}\x{6F9B}\x{6F9C}\x{6F9D}\x{6F9E}\x{6FA0}\x{6FA1}\x{6FA2}\x{6FA3}' .
+'\x{6FA4}\x{6FA5}\x{6FA6}\x{6FA7}\x{6FA8}\x{6FA9}\x{6FAA}\x{6FAB}\x{6FAC}' .
+'\x{6FAD}\x{6FAE}\x{6FAF}\x{6FB0}\x{6FB1}\x{6FB2}\x{6FB3}\x{6FB4}\x{6FB5}' .
+'\x{6FB6}\x{6FB8}\x{6FB9}\x{6FBA}\x{6FBB}\x{6FBC}\x{6FBD}\x{6FBE}\x{6FBF}' .
+'\x{6FC0}\x{6FC1}\x{6FC2}\x{6FC3}\x{6FC4}\x{6FC6}\x{6FC7}\x{6FC8}\x{6FC9}' .
+'\x{6FCA}\x{6FCB}\x{6FCC}\x{6FCD}\x{6FCE}\x{6FCF}\x{6FD1}\x{6FD2}\x{6FD4}' .
+'\x{6FD5}\x{6FD6}\x{6FD7}\x{6FD8}\x{6FD9}\x{6FDA}\x{6FDB}\x{6FDC}\x{6FDD}' .
+'\x{6FDE}\x{6FDF}\x{6FE0}\x{6FE1}\x{6FE2}\x{6FE3}\x{6FE4}\x{6FE5}\x{6FE6}' .
+'\x{6FE7}\x{6FE8}\x{6FE9}\x{6FEA}\x{6FEB}\x{6FEC}\x{6FED}\x{6FEE}\x{6FEF}' .
+'\x{6FF0}\x{6FF1}\x{6FF2}\x{6FF3}\x{6FF4}\x{6FF6}\x{6FF7}\x{6FF8}\x{6FF9}' .
+'\x{6FFA}\x{6FFB}\x{6FFC}\x{6FFE}\x{6FFF}\x{7000}\x{7001}\x{7002}\x{7003}' .
+'\x{7004}\x{7005}\x{7006}\x{7007}\x{7008}\x{7009}\x{700A}\x{700B}\x{700C}' .
+'\x{700D}\x{700E}\x{700F}\x{7011}\x{7012}\x{7014}\x{7015}\x{7016}\x{7017}' .
+'\x{7018}\x{7019}\x{701A}\x{701B}\x{701C}\x{701D}\x{701F}\x{7020}\x{7021}' .
+'\x{7022}\x{7023}\x{7024}\x{7025}\x{7026}\x{7027}\x{7028}\x{7029}\x{702A}' .
+'\x{702B}\x{702C}\x{702D}\x{702E}\x{702F}\x{7030}\x{7031}\x{7032}\x{7033}' .
+'\x{7034}\x{7035}\x{7036}\x{7037}\x{7038}\x{7039}\x{703A}\x{703B}\x{703C}' .
+'\x{703D}\x{703E}\x{703F}\x{7040}\x{7041}\x{7042}\x{7043}\x{7044}\x{7045}' .
+'\x{7046}\x{7048}\x{7049}\x{704A}\x{704C}\x{704D}\x{704F}\x{7050}\x{7051}' .
+'\x{7052}\x{7053}\x{7054}\x{7055}\x{7056}\x{7057}\x{7058}\x{7059}\x{705A}' .
+'\x{705B}\x{705C}\x{705D}\x{705E}\x{705F}\x{7060}\x{7061}\x{7062}\x{7063}' .
+'\x{7064}\x{7065}\x{7066}\x{7067}\x{7068}\x{7069}\x{706A}\x{706B}\x{706C}' .
+'\x{706D}\x{706E}\x{706F}\x{7070}\x{7071}\x{7074}\x{7075}\x{7076}\x{7077}' .
+'\x{7078}\x{7079}\x{707A}\x{707C}\x{707D}\x{707E}\x{707F}\x{7080}\x{7082}' .
+'\x{7083}\x{7084}\x{7085}\x{7086}\x{7087}\x{7088}\x{7089}\x{708A}\x{708B}' .
+'\x{708C}\x{708E}\x{708F}\x{7090}\x{7091}\x{7092}\x{7093}\x{7094}\x{7095}' .
+'\x{7096}\x{7098}\x{7099}\x{709A}\x{709C}\x{709D}\x{709E}\x{709F}\x{70A0}' .
+'\x{70A1}\x{70A2}\x{70A3}\x{70A4}\x{70A5}\x{70A6}\x{70A7}\x{70A8}\x{70A9}' .
+'\x{70AB}\x{70AC}\x{70AD}\x{70AE}\x{70AF}\x{70B0}\x{70B1}\x{70B3}\x{70B4}' .
+'\x{70B5}\x{70B7}\x{70B8}\x{70B9}\x{70BA}\x{70BB}\x{70BC}\x{70BD}\x{70BE}' .
+'\x{70BF}\x{70C0}\x{70C1}\x{70C2}\x{70C3}\x{70C4}\x{70C5}\x{70C6}\x{70C7}' .
+'\x{70C8}\x{70C9}\x{70CA}\x{70CB}\x{70CC}\x{70CD}\x{70CE}\x{70CF}\x{70D0}' .
+'\x{70D1}\x{70D2}\x{70D3}\x{70D4}\x{70D6}\x{70D7}\x{70D8}\x{70D9}\x{70DA}' .
+'\x{70DB}\x{70DC}\x{70DD}\x{70DE}\x{70DF}\x{70E0}\x{70E1}\x{70E2}\x{70E3}' .
+'\x{70E4}\x{70E5}\x{70E6}\x{70E7}\x{70E8}\x{70E9}\x{70EA}\x{70EB}\x{70EC}' .
+'\x{70ED}\x{70EE}\x{70EF}\x{70F0}\x{70F1}\x{70F2}\x{70F3}\x{70F4}\x{70F5}' .
+'\x{70F6}\x{70F7}\x{70F8}\x{70F9}\x{70FA}\x{70FB}\x{70FC}\x{70FD}\x{70FF}' .
+'\x{7100}\x{7101}\x{7102}\x{7103}\x{7104}\x{7105}\x{7106}\x{7107}\x{7109}' .
+'\x{710A}\x{710B}\x{710C}\x{710D}\x{710E}\x{710F}\x{7110}\x{7111}\x{7112}' .
+'\x{7113}\x{7115}\x{7116}\x{7117}\x{7118}\x{7119}\x{711A}\x{711B}\x{711C}' .
+'\x{711D}\x{711E}\x{711F}\x{7120}\x{7121}\x{7122}\x{7123}\x{7125}\x{7126}' .
+'\x{7127}\x{7128}\x{7129}\x{712A}\x{712B}\x{712C}\x{712D}\x{712E}\x{712F}' .
+'\x{7130}\x{7131}\x{7132}\x{7135}\x{7136}\x{7137}\x{7138}\x{7139}\x{713A}' .
+'\x{713B}\x{713D}\x{713E}\x{713F}\x{7140}\x{7141}\x{7142}\x{7143}\x{7144}' .
+'\x{7145}\x{7146}\x{7147}\x{7148}\x{7149}\x{714A}\x{714B}\x{714C}\x{714D}' .
+'\x{714E}\x{714F}\x{7150}\x{7151}\x{7152}\x{7153}\x{7154}\x{7156}\x{7158}' .
+'\x{7159}\x{715A}\x{715B}\x{715C}\x{715D}\x{715E}\x{715F}\x{7160}\x{7161}' .
+'\x{7162}\x{7163}\x{7164}\x{7165}\x{7166}\x{7167}\x{7168}\x{7169}\x{716A}' .
+'\x{716C}\x{716E}\x{716F}\x{7170}\x{7171}\x{7172}\x{7173}\x{7174}\x{7175}' .
+'\x{7176}\x{7177}\x{7178}\x{7179}\x{717A}\x{717B}\x{717C}\x{717D}\x{717E}' .
+'\x{717F}\x{7180}\x{7181}\x{7182}\x{7183}\x{7184}\x{7185}\x{7186}\x{7187}' .
+'\x{7188}\x{7189}\x{718A}\x{718B}\x{718C}\x{718E}\x{718F}\x{7190}\x{7191}' .
+'\x{7192}\x{7193}\x{7194}\x{7195}\x{7197}\x{7198}\x{7199}\x{719A}\x{719B}' .
+'\x{719C}\x{719D}\x{719E}\x{719F}\x{71A0}\x{71A1}\x{71A2}\x{71A3}\x{71A4}' .
+'\x{71A5}\x{71A7}\x{71A8}\x{71A9}\x{71AA}\x{71AC}\x{71AD}\x{71AE}\x{71AF}' .
+'\x{71B0}\x{71B1}\x{71B2}\x{71B3}\x{71B4}\x{71B5}\x{71B7}\x{71B8}\x{71B9}' .
+'\x{71BA}\x{71BB}\x{71BC}\x{71BD}\x{71BE}\x{71BF}\x{71C0}\x{71C1}\x{71C2}' .
+'\x{71C3}\x{71C4}\x{71C5}\x{71C6}\x{71C7}\x{71C8}\x{71C9}\x{71CA}\x{71CB}' .
+'\x{71CD}\x{71CE}\x{71CF}\x{71D0}\x{71D1}\x{71D2}\x{71D4}\x{71D5}\x{71D6}' .
+'\x{71D7}\x{71D8}\x{71D9}\x{71DA}\x{71DB}\x{71DC}\x{71DD}\x{71DE}\x{71DF}' .
+'\x{71E0}\x{71E1}\x{71E2}\x{71E3}\x{71E4}\x{71E5}\x{71E6}\x{71E7}\x{71E8}' .
+'\x{71E9}\x{71EA}\x{71EB}\x{71EC}\x{71ED}\x{71EE}\x{71EF}\x{71F0}\x{71F1}' .
+'\x{71F2}\x{71F4}\x{71F5}\x{71F6}\x{71F7}\x{71F8}\x{71F9}\x{71FB}\x{71FC}' .
+'\x{71FD}\x{71FE}\x{71FF}\x{7201}\x{7202}\x{7203}\x{7204}\x{7205}\x{7206}' .
+'\x{7207}\x{7208}\x{7209}\x{720A}\x{720C}\x{720D}\x{720E}\x{720F}\x{7210}' .
+'\x{7212}\x{7213}\x{7214}\x{7216}\x{7218}\x{7219}\x{721A}\x{721B}\x{721C}' .
+'\x{721D}\x{721E}\x{721F}\x{7221}\x{7222}\x{7223}\x{7226}\x{7227}\x{7228}' .
+'\x{7229}\x{722A}\x{722B}\x{722C}\x{722D}\x{722E}\x{7230}\x{7231}\x{7232}' .
+'\x{7233}\x{7235}\x{7236}\x{7237}\x{7238}\x{7239}\x{723A}\x{723B}\x{723C}' .
+'\x{723D}\x{723E}\x{723F}\x{7240}\x{7241}\x{7242}\x{7243}\x{7244}\x{7246}' .
+'\x{7247}\x{7248}\x{7249}\x{724A}\x{724B}\x{724C}\x{724D}\x{724F}\x{7251}' .
+'\x{7252}\x{7253}\x{7254}\x{7256}\x{7257}\x{7258}\x{7259}\x{725A}\x{725B}' .
+'\x{725C}\x{725D}\x{725E}\x{725F}\x{7260}\x{7261}\x{7262}\x{7263}\x{7264}' .
+'\x{7265}\x{7266}\x{7267}\x{7268}\x{7269}\x{726A}\x{726B}\x{726C}\x{726D}' .
+'\x{726E}\x{726F}\x{7270}\x{7271}\x{7272}\x{7273}\x{7274}\x{7275}\x{7276}' .
+'\x{7277}\x{7278}\x{7279}\x{727A}\x{727B}\x{727C}\x{727D}\x{727E}\x{727F}' .
+'\x{7280}\x{7281}\x{7282}\x{7283}\x{7284}\x{7285}\x{7286}\x{7287}\x{7288}' .
+'\x{7289}\x{728A}\x{728B}\x{728C}\x{728D}\x{728E}\x{728F}\x{7290}\x{7291}' .
+'\x{7292}\x{7293}\x{7294}\x{7295}\x{7296}\x{7297}\x{7298}\x{7299}\x{729A}' .
+'\x{729B}\x{729C}\x{729D}\x{729E}\x{729F}\x{72A1}\x{72A2}\x{72A3}\x{72A4}' .
+'\x{72A5}\x{72A6}\x{72A7}\x{72A8}\x{72A9}\x{72AA}\x{72AC}\x{72AD}\x{72AE}' .
+'\x{72AF}\x{72B0}\x{72B1}\x{72B2}\x{72B3}\x{72B4}\x{72B5}\x{72B6}\x{72B7}' .
+'\x{72B8}\x{72B9}\x{72BA}\x{72BB}\x{72BC}\x{72BD}\x{72BF}\x{72C0}\x{72C1}' .
+'\x{72C2}\x{72C3}\x{72C4}\x{72C5}\x{72C6}\x{72C7}\x{72C8}\x{72C9}\x{72CA}' .
+'\x{72CB}\x{72CC}\x{72CD}\x{72CE}\x{72CF}\x{72D0}\x{72D1}\x{72D2}\x{72D3}' .
+'\x{72D4}\x{72D5}\x{72D6}\x{72D7}\x{72D8}\x{72D9}\x{72DA}\x{72DB}\x{72DC}' .
+'\x{72DD}\x{72DE}\x{72DF}\x{72E0}\x{72E1}\x{72E2}\x{72E3}\x{72E4}\x{72E5}' .
+'\x{72E6}\x{72E7}\x{72E8}\x{72E9}\x{72EA}\x{72EB}\x{72EC}\x{72ED}\x{72EE}' .
+'\x{72EF}\x{72F0}\x{72F1}\x{72F2}\x{72F3}\x{72F4}\x{72F5}\x{72F6}\x{72F7}' .
+'\x{72F8}\x{72F9}\x{72FA}\x{72FB}\x{72FC}\x{72FD}\x{72FE}\x{72FF}\x{7300}' .
+'\x{7301}\x{7303}\x{7304}\x{7305}\x{7306}\x{7307}\x{7308}\x{7309}\x{730A}' .
+'\x{730B}\x{730C}\x{730D}\x{730E}\x{730F}\x{7311}\x{7312}\x{7313}\x{7314}' .
+'\x{7315}\x{7316}\x{7317}\x{7318}\x{7319}\x{731A}\x{731B}\x{731C}\x{731D}' .
+'\x{731E}\x{7320}\x{7321}\x{7322}\x{7323}\x{7324}\x{7325}\x{7326}\x{7327}' .
+'\x{7329}\x{732A}\x{732B}\x{732C}\x{732D}\x{732E}\x{7330}\x{7331}\x{7332}' .
+'\x{7333}\x{7334}\x{7335}\x{7336}\x{7337}\x{7338}\x{7339}\x{733A}\x{733B}' .
+'\x{733C}\x{733D}\x{733E}\x{733F}\x{7340}\x{7341}\x{7342}\x{7343}\x{7344}' .
+'\x{7345}\x{7346}\x{7347}\x{7348}\x{7349}\x{734A}\x{734B}\x{734C}\x{734D}' .
+'\x{734E}\x{7350}\x{7351}\x{7352}\x{7354}\x{7355}\x{7356}\x{7357}\x{7358}' .
+'\x{7359}\x{735A}\x{735B}\x{735C}\x{735D}\x{735E}\x{735F}\x{7360}\x{7361}' .
+'\x{7362}\x{7364}\x{7365}\x{7366}\x{7367}\x{7368}\x{7369}\x{736A}\x{736B}' .
+'\x{736C}\x{736D}\x{736E}\x{736F}\x{7370}\x{7371}\x{7372}\x{7373}\x{7374}' .
+'\x{7375}\x{7376}\x{7377}\x{7378}\x{7379}\x{737A}\x{737B}\x{737C}\x{737D}' .
+'\x{737E}\x{737F}\x{7380}\x{7381}\x{7382}\x{7383}\x{7384}\x{7385}\x{7386}' .
+'\x{7387}\x{7388}\x{7389}\x{738A}\x{738B}\x{738C}\x{738D}\x{738E}\x{738F}' .
+'\x{7390}\x{7391}\x{7392}\x{7393}\x{7394}\x{7395}\x{7396}\x{7397}\x{7398}' .
+'\x{7399}\x{739A}\x{739B}\x{739D}\x{739E}\x{739F}\x{73A0}\x{73A1}\x{73A2}' .
+'\x{73A3}\x{73A4}\x{73A5}\x{73A6}\x{73A7}\x{73A8}\x{73A9}\x{73AA}\x{73AB}' .
+'\x{73AC}\x{73AD}\x{73AE}\x{73AF}\x{73B0}\x{73B1}\x{73B2}\x{73B3}\x{73B4}' .
+'\x{73B5}\x{73B6}\x{73B7}\x{73B8}\x{73B9}\x{73BA}\x{73BB}\x{73BC}\x{73BD}' .
+'\x{73BE}\x{73BF}\x{73C0}\x{73C2}\x{73C3}\x{73C4}\x{73C5}\x{73C6}\x{73C7}' .
+'\x{73C8}\x{73C9}\x{73CA}\x{73CB}\x{73CC}\x{73CD}\x{73CE}\x{73CF}\x{73D0}' .
+'\x{73D1}\x{73D2}\x{73D3}\x{73D4}\x{73D5}\x{73D6}\x{73D7}\x{73D8}\x{73D9}' .
+'\x{73DA}\x{73DB}\x{73DC}\x{73DD}\x{73DE}\x{73DF}\x{73E0}\x{73E2}\x{73E3}' .
+'\x{73E5}\x{73E6}\x{73E7}\x{73E8}\x{73E9}\x{73EA}\x{73EB}\x{73EC}\x{73ED}' .
+'\x{73EE}\x{73EF}\x{73F0}\x{73F1}\x{73F2}\x{73F4}\x{73F5}\x{73F6}\x{73F7}' .
+'\x{73F8}\x{73F9}\x{73FA}\x{73FC}\x{73FD}\x{73FE}\x{73FF}\x{7400}\x{7401}' .
+'\x{7402}\x{7403}\x{7404}\x{7405}\x{7406}\x{7407}\x{7408}\x{7409}\x{740A}' .
+'\x{740B}\x{740C}\x{740D}\x{740E}\x{740F}\x{7410}\x{7411}\x{7412}\x{7413}' .
+'\x{7414}\x{7415}\x{7416}\x{7417}\x{7419}\x{741A}\x{741B}\x{741C}\x{741D}' .
+'\x{741E}\x{741F}\x{7420}\x{7421}\x{7422}\x{7423}\x{7424}\x{7425}\x{7426}' .
+'\x{7427}\x{7428}\x{7429}\x{742A}\x{742B}\x{742C}\x{742D}\x{742E}\x{742F}' .
+'\x{7430}\x{7431}\x{7432}\x{7433}\x{7434}\x{7435}\x{7436}\x{7437}\x{7438}' .
+'\x{743A}\x{743B}\x{743C}\x{743D}\x{743F}\x{7440}\x{7441}\x{7442}\x{7443}' .
+'\x{7444}\x{7445}\x{7446}\x{7448}\x{744A}\x{744B}\x{744C}\x{744D}\x{744E}' .
+'\x{744F}\x{7450}\x{7451}\x{7452}\x{7453}\x{7454}\x{7455}\x{7456}\x{7457}' .
+'\x{7459}\x{745A}\x{745B}\x{745C}\x{745D}\x{745E}\x{745F}\x{7461}\x{7462}' .
+'\x{7463}\x{7464}\x{7465}\x{7466}\x{7467}\x{7468}\x{7469}\x{746A}\x{746B}' .
+'\x{746C}\x{746D}\x{746E}\x{746F}\x{7470}\x{7471}\x{7472}\x{7473}\x{7474}' .
+'\x{7475}\x{7476}\x{7477}\x{7478}\x{7479}\x{747A}\x{747C}\x{747D}\x{747E}' .
+'\x{747F}\x{7480}\x{7481}\x{7482}\x{7483}\x{7485}\x{7486}\x{7487}\x{7488}' .
+'\x{7489}\x{748A}\x{748B}\x{748C}\x{748D}\x{748E}\x{748F}\x{7490}\x{7491}' .
+'\x{7492}\x{7493}\x{7494}\x{7495}\x{7497}\x{7498}\x{7499}\x{749A}\x{749B}' .
+'\x{749C}\x{749E}\x{749F}\x{74A0}\x{74A1}\x{74A3}\x{74A4}\x{74A5}\x{74A6}' .
+'\x{74A7}\x{74A8}\x{74A9}\x{74AA}\x{74AB}\x{74AC}\x{74AD}\x{74AE}\x{74AF}' .
+'\x{74B0}\x{74B1}\x{74B2}\x{74B3}\x{74B4}\x{74B5}\x{74B6}\x{74B7}\x{74B8}' .
+'\x{74B9}\x{74BA}\x{74BB}\x{74BC}\x{74BD}\x{74BE}\x{74BF}\x{74C0}\x{74C1}' .
+'\x{74C2}\x{74C3}\x{74C4}\x{74C5}\x{74C6}\x{74CA}\x{74CB}\x{74CD}\x{74CE}' .
+'\x{74CF}\x{74D0}\x{74D1}\x{74D2}\x{74D3}\x{74D4}\x{74D5}\x{74D6}\x{74D7}' .
+'\x{74D8}\x{74D9}\x{74DA}\x{74DB}\x{74DC}\x{74DD}\x{74DE}\x{74DF}\x{74E0}' .
+'\x{74E1}\x{74E2}\x{74E3}\x{74E4}\x{74E5}\x{74E6}\x{74E7}\x{74E8}\x{74E9}' .
+'\x{74EA}\x{74EC}\x{74ED}\x{74EE}\x{74EF}\x{74F0}\x{74F1}\x{74F2}\x{74F3}' .
+'\x{74F4}\x{74F5}\x{74F6}\x{74F7}\x{74F8}\x{74F9}\x{74FA}\x{74FB}\x{74FC}' .
+'\x{74FD}\x{74FE}\x{74FF}\x{7500}\x{7501}\x{7502}\x{7503}\x{7504}\x{7505}' .
+'\x{7506}\x{7507}\x{7508}\x{7509}\x{750A}\x{750B}\x{750C}\x{750D}\x{750F}' .
+'\x{7510}\x{7511}\x{7512}\x{7513}\x{7514}\x{7515}\x{7516}\x{7517}\x{7518}' .
+'\x{7519}\x{751A}\x{751B}\x{751C}\x{751D}\x{751E}\x{751F}\x{7521}\x{7522}' .
+'\x{7523}\x{7524}\x{7525}\x{7526}\x{7527}\x{7528}\x{7529}\x{752A}\x{752B}' .
+'\x{752C}\x{752D}\x{752E}\x{752F}\x{7530}\x{7531}\x{7532}\x{7533}\x{7535}' .
+'\x{7536}\x{7537}\x{7538}\x{7539}\x{753A}\x{753B}\x{753C}\x{753D}\x{753E}' .
+'\x{753F}\x{7540}\x{7542}\x{7543}\x{7544}\x{7545}\x{7546}\x{7547}\x{7548}' .
+'\x{7549}\x{754B}\x{754C}\x{754D}\x{754E}\x{754F}\x{7550}\x{7551}\x{7553}' .
+'\x{7554}\x{7556}\x{7557}\x{7558}\x{7559}\x{755A}\x{755B}\x{755C}\x{755D}' .
+'\x{755F}\x{7560}\x{7562}\x{7563}\x{7564}\x{7565}\x{7566}\x{7567}\x{7568}' .
+'\x{7569}\x{756A}\x{756B}\x{756C}\x{756D}\x{756E}\x{756F}\x{7570}\x{7572}' .
+'\x{7574}\x{7575}\x{7576}\x{7577}\x{7578}\x{7579}\x{757C}\x{757D}\x{757E}' .
+'\x{757F}\x{7580}\x{7581}\x{7582}\x{7583}\x{7584}\x{7586}\x{7587}\x{7588}' .
+'\x{7589}\x{758A}\x{758B}\x{758C}\x{758D}\x{758F}\x{7590}\x{7591}\x{7592}' .
+'\x{7593}\x{7594}\x{7595}\x{7596}\x{7597}\x{7598}\x{7599}\x{759A}\x{759B}' .
+'\x{759C}\x{759D}\x{759E}\x{759F}\x{75A0}\x{75A1}\x{75A2}\x{75A3}\x{75A4}' .
+'\x{75A5}\x{75A6}\x{75A7}\x{75A8}\x{75AA}\x{75AB}\x{75AC}\x{75AD}\x{75AE}' .
+'\x{75AF}\x{75B0}\x{75B1}\x{75B2}\x{75B3}\x{75B4}\x{75B5}\x{75B6}\x{75B8}' .
+'\x{75B9}\x{75BA}\x{75BB}\x{75BC}\x{75BD}\x{75BE}\x{75BF}\x{75C0}\x{75C1}' .
+'\x{75C2}\x{75C3}\x{75C4}\x{75C5}\x{75C6}\x{75C7}\x{75C8}\x{75C9}\x{75CA}' .
+'\x{75CB}\x{75CC}\x{75CD}\x{75CE}\x{75CF}\x{75D0}\x{75D1}\x{75D2}\x{75D3}' .
+'\x{75D4}\x{75D5}\x{75D6}\x{75D7}\x{75D8}\x{75D9}\x{75DA}\x{75DB}\x{75DD}' .
+'\x{75DE}\x{75DF}\x{75E0}\x{75E1}\x{75E2}\x{75E3}\x{75E4}\x{75E5}\x{75E6}' .
+'\x{75E7}\x{75E8}\x{75EA}\x{75EB}\x{75EC}\x{75ED}\x{75EF}\x{75F0}\x{75F1}' .
+'\x{75F2}\x{75F3}\x{75F4}\x{75F5}\x{75F6}\x{75F7}\x{75F8}\x{75F9}\x{75FA}' .
+'\x{75FB}\x{75FC}\x{75FD}\x{75FE}\x{75FF}\x{7600}\x{7601}\x{7602}\x{7603}' .
+'\x{7604}\x{7605}\x{7606}\x{7607}\x{7608}\x{7609}\x{760A}\x{760B}\x{760C}' .
+'\x{760D}\x{760E}\x{760F}\x{7610}\x{7611}\x{7612}\x{7613}\x{7614}\x{7615}' .
+'\x{7616}\x{7617}\x{7618}\x{7619}\x{761A}\x{761B}\x{761C}\x{761D}\x{761E}' .
+'\x{761F}\x{7620}\x{7621}\x{7622}\x{7623}\x{7624}\x{7625}\x{7626}\x{7627}' .
+'\x{7628}\x{7629}\x{762A}\x{762B}\x{762D}\x{762E}\x{762F}\x{7630}\x{7631}' .
+'\x{7632}\x{7633}\x{7634}\x{7635}\x{7636}\x{7637}\x{7638}\x{7639}\x{763A}' .
+'\x{763B}\x{763C}\x{763D}\x{763E}\x{763F}\x{7640}\x{7641}\x{7642}\x{7643}' .
+'\x{7646}\x{7647}\x{7648}\x{7649}\x{764A}\x{764B}\x{764C}\x{764D}\x{764F}' .
+'\x{7650}\x{7652}\x{7653}\x{7654}\x{7656}\x{7657}\x{7658}\x{7659}\x{765A}' .
+'\x{765B}\x{765C}\x{765D}\x{765E}\x{765F}\x{7660}\x{7661}\x{7662}\x{7663}' .
+'\x{7664}\x{7665}\x{7666}\x{7667}\x{7668}\x{7669}\x{766A}\x{766B}\x{766C}' .
+'\x{766D}\x{766E}\x{766F}\x{7670}\x{7671}\x{7672}\x{7674}\x{7675}\x{7676}' .
+'\x{7677}\x{7678}\x{7679}\x{767B}\x{767C}\x{767D}\x{767E}\x{767F}\x{7680}' .
+'\x{7681}\x{7682}\x{7683}\x{7684}\x{7685}\x{7686}\x{7687}\x{7688}\x{7689}' .
+'\x{768A}\x{768B}\x{768C}\x{768E}\x{768F}\x{7690}\x{7691}\x{7692}\x{7693}' .
+'\x{7694}\x{7695}\x{7696}\x{7697}\x{7698}\x{7699}\x{769A}\x{769B}\x{769C}' .
+'\x{769D}\x{769E}\x{769F}\x{76A0}\x{76A3}\x{76A4}\x{76A6}\x{76A7}\x{76A9}' .
+'\x{76AA}\x{76AB}\x{76AC}\x{76AD}\x{76AE}\x{76AF}\x{76B0}\x{76B1}\x{76B2}' .
+'\x{76B4}\x{76B5}\x{76B7}\x{76B8}\x{76BA}\x{76BB}\x{76BC}\x{76BD}\x{76BE}' .
+'\x{76BF}\x{76C0}\x{76C2}\x{76C3}\x{76C4}\x{76C5}\x{76C6}\x{76C7}\x{76C8}' .
+'\x{76C9}\x{76CA}\x{76CD}\x{76CE}\x{76CF}\x{76D0}\x{76D1}\x{76D2}\x{76D3}' .
+'\x{76D4}\x{76D5}\x{76D6}\x{76D7}\x{76D8}\x{76DA}\x{76DB}\x{76DC}\x{76DD}' .
+'\x{76DE}\x{76DF}\x{76E0}\x{76E1}\x{76E2}\x{76E3}\x{76E4}\x{76E5}\x{76E6}' .
+'\x{76E7}\x{76E8}\x{76E9}\x{76EA}\x{76EC}\x{76ED}\x{76EE}\x{76EF}\x{76F0}' .
+'\x{76F1}\x{76F2}\x{76F3}\x{76F4}\x{76F5}\x{76F6}\x{76F7}\x{76F8}\x{76F9}' .
+'\x{76FA}\x{76FB}\x{76FC}\x{76FD}\x{76FE}\x{76FF}\x{7701}\x{7703}\x{7704}' .
+'\x{7705}\x{7706}\x{7707}\x{7708}\x{7709}\x{770A}\x{770B}\x{770C}\x{770D}' .
+'\x{770F}\x{7710}\x{7711}\x{7712}\x{7713}\x{7714}\x{7715}\x{7716}\x{7717}' .
+'\x{7718}\x{7719}\x{771A}\x{771B}\x{771C}\x{771D}\x{771E}\x{771F}\x{7720}' .
+'\x{7722}\x{7723}\x{7725}\x{7726}\x{7727}\x{7728}\x{7729}\x{772A}\x{772C}' .
+'\x{772D}\x{772E}\x{772F}\x{7730}\x{7731}\x{7732}\x{7733}\x{7734}\x{7735}' .
+'\x{7736}\x{7737}\x{7738}\x{7739}\x{773A}\x{773B}\x{773C}\x{773D}\x{773E}' .
+'\x{7740}\x{7741}\x{7743}\x{7744}\x{7745}\x{7746}\x{7747}\x{7748}\x{7749}' .
+'\x{774A}\x{774B}\x{774C}\x{774D}\x{774E}\x{774F}\x{7750}\x{7751}\x{7752}' .
+'\x{7753}\x{7754}\x{7755}\x{7756}\x{7757}\x{7758}\x{7759}\x{775A}\x{775B}' .
+'\x{775C}\x{775D}\x{775E}\x{775F}\x{7760}\x{7761}\x{7762}\x{7763}\x{7765}' .
+'\x{7766}\x{7767}\x{7768}\x{7769}\x{776A}\x{776B}\x{776C}\x{776D}\x{776E}' .
+'\x{776F}\x{7770}\x{7771}\x{7772}\x{7773}\x{7774}\x{7775}\x{7776}\x{7777}' .
+'\x{7778}\x{7779}\x{777A}\x{777B}\x{777C}\x{777D}\x{777E}\x{777F}\x{7780}' .
+'\x{7781}\x{7782}\x{7783}\x{7784}\x{7785}\x{7786}\x{7787}\x{7788}\x{7789}' .
+'\x{778A}\x{778B}\x{778C}\x{778D}\x{778E}\x{778F}\x{7790}\x{7791}\x{7792}' .
+'\x{7793}\x{7794}\x{7795}\x{7797}\x{7798}\x{7799}\x{779A}\x{779B}\x{779C}' .
+'\x{779D}\x{779E}\x{779F}\x{77A0}\x{77A1}\x{77A2}\x{77A3}\x{77A5}\x{77A6}' .
+'\x{77A7}\x{77A8}\x{77A9}\x{77AA}\x{77AB}\x{77AC}\x{77AD}\x{77AE}\x{77AF}' .
+'\x{77B0}\x{77B1}\x{77B2}\x{77B3}\x{77B4}\x{77B5}\x{77B6}\x{77B7}\x{77B8}' .
+'\x{77B9}\x{77BA}\x{77BB}\x{77BC}\x{77BD}\x{77BF}\x{77C0}\x{77C2}\x{77C3}' .
+'\x{77C4}\x{77C5}\x{77C6}\x{77C7}\x{77C8}\x{77C9}\x{77CA}\x{77CB}\x{77CC}' .
+'\x{77CD}\x{77CE}\x{77CF}\x{77D0}\x{77D1}\x{77D3}\x{77D4}\x{77D5}\x{77D6}' .
+'\x{77D7}\x{77D8}\x{77D9}\x{77DA}\x{77DB}\x{77DC}\x{77DE}\x{77DF}\x{77E0}' .
+'\x{77E1}\x{77E2}\x{77E3}\x{77E5}\x{77E7}\x{77E8}\x{77E9}\x{77EA}\x{77EB}' .
+'\x{77EC}\x{77ED}\x{77EE}\x{77EF}\x{77F0}\x{77F1}\x{77F2}\x{77F3}\x{77F6}' .
+'\x{77F7}\x{77F8}\x{77F9}\x{77FA}\x{77FB}\x{77FC}\x{77FD}\x{77FE}\x{77FF}' .
+'\x{7800}\x{7801}\x{7802}\x{7803}\x{7804}\x{7805}\x{7806}\x{7808}\x{7809}' .
+'\x{780A}\x{780B}\x{780C}\x{780D}\x{780E}\x{780F}\x{7810}\x{7811}\x{7812}' .
+'\x{7813}\x{7814}\x{7815}\x{7816}\x{7817}\x{7818}\x{7819}\x{781A}\x{781B}' .
+'\x{781C}\x{781D}\x{781E}\x{781F}\x{7820}\x{7821}\x{7822}\x{7823}\x{7825}' .
+'\x{7826}\x{7827}\x{7828}\x{7829}\x{782A}\x{782B}\x{782C}\x{782D}\x{782E}' .
+'\x{782F}\x{7830}\x{7831}\x{7832}\x{7833}\x{7834}\x{7835}\x{7837}\x{7838}' .
+'\x{7839}\x{783A}\x{783B}\x{783C}\x{783D}\x{783E}\x{7840}\x{7841}\x{7843}' .
+'\x{7844}\x{7845}\x{7847}\x{7848}\x{7849}\x{784A}\x{784C}\x{784D}\x{784E}' .
+'\x{7850}\x{7851}\x{7852}\x{7853}\x{7854}\x{7855}\x{7856}\x{7857}\x{7858}' .
+'\x{7859}\x{785A}\x{785B}\x{785C}\x{785D}\x{785E}\x{785F}\x{7860}\x{7861}' .
+'\x{7862}\x{7863}\x{7864}\x{7865}\x{7866}\x{7867}\x{7868}\x{7869}\x{786A}' .
+'\x{786B}\x{786C}\x{786D}\x{786E}\x{786F}\x{7870}\x{7871}\x{7872}\x{7873}' .
+'\x{7874}\x{7875}\x{7877}\x{7878}\x{7879}\x{787A}\x{787B}\x{787C}\x{787D}' .
+'\x{787E}\x{787F}\x{7880}\x{7881}\x{7882}\x{7883}\x{7884}\x{7885}\x{7886}' .
+'\x{7887}\x{7889}\x{788A}\x{788B}\x{788C}\x{788D}\x{788E}\x{788F}\x{7890}' .
+'\x{7891}\x{7892}\x{7893}\x{7894}\x{7895}\x{7896}\x{7897}\x{7898}\x{7899}' .
+'\x{789A}\x{789B}\x{789C}\x{789D}\x{789E}\x{789F}\x{78A0}\x{78A1}\x{78A2}' .
+'\x{78A3}\x{78A4}\x{78A5}\x{78A6}\x{78A7}\x{78A8}\x{78A9}\x{78AA}\x{78AB}' .
+'\x{78AC}\x{78AD}\x{78AE}\x{78AF}\x{78B0}\x{78B1}\x{78B2}\x{78B3}\x{78B4}' .
+'\x{78B5}\x{78B6}\x{78B7}\x{78B8}\x{78B9}\x{78BA}\x{78BB}\x{78BC}\x{78BD}' .
+'\x{78BE}\x{78BF}\x{78C0}\x{78C1}\x{78C3}\x{78C4}\x{78C5}\x{78C6}\x{78C8}' .
+'\x{78C9}\x{78CA}\x{78CB}\x{78CC}\x{78CD}\x{78CE}\x{78CF}\x{78D0}\x{78D1}' .
+'\x{78D3}\x{78D4}\x{78D5}\x{78D6}\x{78D7}\x{78D8}\x{78D9}\x{78DA}\x{78DB}' .
+'\x{78DC}\x{78DD}\x{78DE}\x{78DF}\x{78E0}\x{78E1}\x{78E2}\x{78E3}\x{78E4}' .
+'\x{78E5}\x{78E6}\x{78E7}\x{78E8}\x{78E9}\x{78EA}\x{78EB}\x{78EC}\x{78ED}' .
+'\x{78EE}\x{78EF}\x{78F1}\x{78F2}\x{78F3}\x{78F4}\x{78F5}\x{78F6}\x{78F7}' .
+'\x{78F9}\x{78FA}\x{78FB}\x{78FC}\x{78FD}\x{78FE}\x{78FF}\x{7901}\x{7902}' .
+'\x{7903}\x{7904}\x{7905}\x{7906}\x{7907}\x{7909}\x{790A}\x{790B}\x{790C}' .
+'\x{790E}\x{790F}\x{7910}\x{7911}\x{7912}\x{7913}\x{7914}\x{7916}\x{7917}' .
+'\x{7918}\x{7919}\x{791A}\x{791B}\x{791C}\x{791D}\x{791E}\x{7921}\x{7922}' .
+'\x{7923}\x{7924}\x{7925}\x{7926}\x{7927}\x{7928}\x{7929}\x{792A}\x{792B}' .
+'\x{792C}\x{792D}\x{792E}\x{792F}\x{7930}\x{7931}\x{7933}\x{7934}\x{7935}' .
+'\x{7937}\x{7938}\x{7939}\x{793A}\x{793B}\x{793C}\x{793D}\x{793E}\x{793F}' .
+'\x{7940}\x{7941}\x{7942}\x{7943}\x{7944}\x{7945}\x{7946}\x{7947}\x{7948}' .
+'\x{7949}\x{794A}\x{794B}\x{794C}\x{794D}\x{794E}\x{794F}\x{7950}\x{7951}' .
+'\x{7952}\x{7953}\x{7954}\x{7955}\x{7956}\x{7957}\x{7958}\x{795A}\x{795B}' .
+'\x{795C}\x{795D}\x{795E}\x{795F}\x{7960}\x{7961}\x{7962}\x{7963}\x{7964}' .
+'\x{7965}\x{7966}\x{7967}\x{7968}\x{7969}\x{796A}\x{796B}\x{796D}\x{796F}' .
+'\x{7970}\x{7971}\x{7972}\x{7973}\x{7974}\x{7977}\x{7978}\x{7979}\x{797A}' .
+'\x{797B}\x{797C}\x{797D}\x{797E}\x{797F}\x{7980}\x{7981}\x{7982}\x{7983}' .
+'\x{7984}\x{7985}\x{7988}\x{7989}\x{798A}\x{798B}\x{798C}\x{798D}\x{798E}' .
+'\x{798F}\x{7990}\x{7991}\x{7992}\x{7993}\x{7994}\x{7995}\x{7996}\x{7997}' .
+'\x{7998}\x{7999}\x{799A}\x{799B}\x{799C}\x{799F}\x{79A0}\x{79A1}\x{79A2}' .
+'\x{79A3}\x{79A4}\x{79A5}\x{79A6}\x{79A7}\x{79A8}\x{79AA}\x{79AB}\x{79AC}' .
+'\x{79AD}\x{79AE}\x{79AF}\x{79B0}\x{79B1}\x{79B2}\x{79B3}\x{79B4}\x{79B5}' .
+'\x{79B6}\x{79B7}\x{79B8}\x{79B9}\x{79BA}\x{79BB}\x{79BD}\x{79BE}\x{79BF}' .
+'\x{79C0}\x{79C1}\x{79C2}\x{79C3}\x{79C5}\x{79C6}\x{79C8}\x{79C9}\x{79CA}' .
+'\x{79CB}\x{79CD}\x{79CE}\x{79CF}\x{79D0}\x{79D1}\x{79D2}\x{79D3}\x{79D5}' .
+'\x{79D6}\x{79D8}\x{79D9}\x{79DA}\x{79DB}\x{79DC}\x{79DD}\x{79DE}\x{79DF}' .
+'\x{79E0}\x{79E1}\x{79E2}\x{79E3}\x{79E4}\x{79E5}\x{79E6}\x{79E7}\x{79E8}' .
+'\x{79E9}\x{79EA}\x{79EB}\x{79EC}\x{79ED}\x{79EE}\x{79EF}\x{79F0}\x{79F1}' .
+'\x{79F2}\x{79F3}\x{79F4}\x{79F5}\x{79F6}\x{79F7}\x{79F8}\x{79F9}\x{79FA}' .
+'\x{79FB}\x{79FC}\x{79FD}\x{79FE}\x{79FF}\x{7A00}\x{7A02}\x{7A03}\x{7A04}' .
+'\x{7A05}\x{7A06}\x{7A08}\x{7A0A}\x{7A0B}\x{7A0C}\x{7A0D}\x{7A0E}\x{7A0F}' .
+'\x{7A10}\x{7A11}\x{7A12}\x{7A13}\x{7A14}\x{7A15}\x{7A16}\x{7A17}\x{7A18}' .
+'\x{7A19}\x{7A1A}\x{7A1B}\x{7A1C}\x{7A1D}\x{7A1E}\x{7A1F}\x{7A20}\x{7A21}' .
+'\x{7A22}\x{7A23}\x{7A24}\x{7A25}\x{7A26}\x{7A27}\x{7A28}\x{7A29}\x{7A2A}' .
+'\x{7A2B}\x{7A2D}\x{7A2E}\x{7A2F}\x{7A30}\x{7A31}\x{7A32}\x{7A33}\x{7A34}' .
+'\x{7A35}\x{7A37}\x{7A39}\x{7A3B}\x{7A3C}\x{7A3D}\x{7A3E}\x{7A3F}\x{7A40}' .
+'\x{7A41}\x{7A42}\x{7A43}\x{7A44}\x{7A45}\x{7A46}\x{7A47}\x{7A48}\x{7A49}' .
+'\x{7A4A}\x{7A4B}\x{7A4C}\x{7A4D}\x{7A4E}\x{7A50}\x{7A51}\x{7A52}\x{7A53}' .
+'\x{7A54}\x{7A55}\x{7A56}\x{7A57}\x{7A58}\x{7A59}\x{7A5A}\x{7A5B}\x{7A5C}' .
+'\x{7A5D}\x{7A5E}\x{7A5F}\x{7A60}\x{7A61}\x{7A62}\x{7A65}\x{7A66}\x{7A67}' .
+'\x{7A68}\x{7A69}\x{7A6B}\x{7A6C}\x{7A6D}\x{7A6E}\x{7A70}\x{7A71}\x{7A72}' .
+'\x{7A73}\x{7A74}\x{7A75}\x{7A76}\x{7A77}\x{7A78}\x{7A79}\x{7A7A}\x{7A7B}' .
+'\x{7A7C}\x{7A7D}\x{7A7E}\x{7A7F}\x{7A80}\x{7A81}\x{7A83}\x{7A84}\x{7A85}' .
+'\x{7A86}\x{7A87}\x{7A88}\x{7A89}\x{7A8A}\x{7A8B}\x{7A8C}\x{7A8D}\x{7A8E}' .
+'\x{7A8F}\x{7A90}\x{7A91}\x{7A92}\x{7A93}\x{7A94}\x{7A95}\x{7A96}\x{7A97}' .
+'\x{7A98}\x{7A99}\x{7A9C}\x{7A9D}\x{7A9E}\x{7A9F}\x{7AA0}\x{7AA1}\x{7AA2}' .
+'\x{7AA3}\x{7AA4}\x{7AA5}\x{7AA6}\x{7AA7}\x{7AA8}\x{7AA9}\x{7AAA}\x{7AAB}' .
+'\x{7AAC}\x{7AAD}\x{7AAE}\x{7AAF}\x{7AB0}\x{7AB1}\x{7AB2}\x{7AB3}\x{7AB4}' .
+'\x{7AB5}\x{7AB6}\x{7AB7}\x{7AB8}\x{7ABA}\x{7ABE}\x{7ABF}\x{7AC0}\x{7AC1}' .
+'\x{7AC4}\x{7AC5}\x{7AC7}\x{7AC8}\x{7AC9}\x{7ACA}\x{7ACB}\x{7ACC}\x{7ACD}' .
+'\x{7ACE}\x{7ACF}\x{7AD0}\x{7AD1}\x{7AD2}\x{7AD3}\x{7AD4}\x{7AD5}\x{7AD6}' .
+'\x{7AD8}\x{7AD9}\x{7ADB}\x{7ADC}\x{7ADD}\x{7ADE}\x{7ADF}\x{7AE0}\x{7AE1}' .
+'\x{7AE2}\x{7AE3}\x{7AE4}\x{7AE5}\x{7AE6}\x{7AE7}\x{7AE8}\x{7AEA}\x{7AEB}' .
+'\x{7AEC}\x{7AED}\x{7AEE}\x{7AEF}\x{7AF0}\x{7AF1}\x{7AF2}\x{7AF3}\x{7AF4}' .
+'\x{7AF6}\x{7AF7}\x{7AF8}\x{7AF9}\x{7AFA}\x{7AFB}\x{7AFD}\x{7AFE}\x{7AFF}' .
+'\x{7B00}\x{7B01}\x{7B02}\x{7B03}\x{7B04}\x{7B05}\x{7B06}\x{7B08}\x{7B09}' .
+'\x{7B0A}\x{7B0B}\x{7B0C}\x{7B0D}\x{7B0E}\x{7B0F}\x{7B10}\x{7B11}\x{7B12}' .
+'\x{7B13}\x{7B14}\x{7B15}\x{7B16}\x{7B17}\x{7B18}\x{7B19}\x{7B1A}\x{7B1B}' .
+'\x{7B1C}\x{7B1D}\x{7B1E}\x{7B20}\x{7B21}\x{7B22}\x{7B23}\x{7B24}\x{7B25}' .
+'\x{7B26}\x{7B28}\x{7B2A}\x{7B2B}\x{7B2C}\x{7B2D}\x{7B2E}\x{7B2F}\x{7B30}' .
+'\x{7B31}\x{7B32}\x{7B33}\x{7B34}\x{7B35}\x{7B36}\x{7B37}\x{7B38}\x{7B39}' .
+'\x{7B3A}\x{7B3B}\x{7B3C}\x{7B3D}\x{7B3E}\x{7B3F}\x{7B40}\x{7B41}\x{7B43}' .
+'\x{7B44}\x{7B45}\x{7B46}\x{7B47}\x{7B48}\x{7B49}\x{7B4A}\x{7B4B}\x{7B4C}' .
+'\x{7B4D}\x{7B4E}\x{7B4F}\x{7B50}\x{7B51}\x{7B52}\x{7B54}\x{7B55}\x{7B56}' .
+'\x{7B57}\x{7B58}\x{7B59}\x{7B5A}\x{7B5B}\x{7B5C}\x{7B5D}\x{7B5E}\x{7B5F}' .
+'\x{7B60}\x{7B61}\x{7B62}\x{7B63}\x{7B64}\x{7B65}\x{7B66}\x{7B67}\x{7B68}' .
+'\x{7B69}\x{7B6A}\x{7B6B}\x{7B6C}\x{7B6D}\x{7B6E}\x{7B70}\x{7B71}\x{7B72}' .
+'\x{7B73}\x{7B74}\x{7B75}\x{7B76}\x{7B77}\x{7B78}\x{7B79}\x{7B7B}\x{7B7C}' .
+'\x{7B7D}\x{7B7E}\x{7B7F}\x{7B80}\x{7B81}\x{7B82}\x{7B83}\x{7B84}\x{7B85}' .
+'\x{7B87}\x{7B88}\x{7B89}\x{7B8A}\x{7B8B}\x{7B8C}\x{7B8D}\x{7B8E}\x{7B8F}' .
+'\x{7B90}\x{7B91}\x{7B93}\x{7B94}\x{7B95}\x{7B96}\x{7B97}\x{7B98}\x{7B99}' .
+'\x{7B9A}\x{7B9B}\x{7B9C}\x{7B9D}\x{7B9E}\x{7B9F}\x{7BA0}\x{7BA1}\x{7BA2}' .
+'\x{7BA4}\x{7BA6}\x{7BA7}\x{7BA8}\x{7BA9}\x{7BAA}\x{7BAB}\x{7BAC}\x{7BAD}' .
+'\x{7BAE}\x{7BAF}\x{7BB1}\x{7BB3}\x{7BB4}\x{7BB5}\x{7BB6}\x{7BB7}\x{7BB8}' .
+'\x{7BB9}\x{7BBA}\x{7BBB}\x{7BBC}\x{7BBD}\x{7BBE}\x{7BBF}\x{7BC0}\x{7BC1}' .
+'\x{7BC2}\x{7BC3}\x{7BC4}\x{7BC5}\x{7BC6}\x{7BC7}\x{7BC8}\x{7BC9}\x{7BCA}' .
+'\x{7BCB}\x{7BCC}\x{7BCD}\x{7BCE}\x{7BD0}\x{7BD1}\x{7BD2}\x{7BD3}\x{7BD4}' .
+'\x{7BD5}\x{7BD6}\x{7BD7}\x{7BD8}\x{7BD9}\x{7BDA}\x{7BDB}\x{7BDC}\x{7BDD}' .
+'\x{7BDE}\x{7BDF}\x{7BE0}\x{7BE1}\x{7BE2}\x{7BE3}\x{7BE4}\x{7BE5}\x{7BE6}' .
+'\x{7BE7}\x{7BE8}\x{7BE9}\x{7BEA}\x{7BEB}\x{7BEC}\x{7BED}\x{7BEE}\x{7BEF}' .
+'\x{7BF0}\x{7BF1}\x{7BF2}\x{7BF3}\x{7BF4}\x{7BF5}\x{7BF6}\x{7BF7}\x{7BF8}' .
+'\x{7BF9}\x{7BFB}\x{7BFC}\x{7BFD}\x{7BFE}\x{7BFF}\x{7C00}\x{7C01}\x{7C02}' .
+'\x{7C03}\x{7C04}\x{7C05}\x{7C06}\x{7C07}\x{7C08}\x{7C09}\x{7C0A}\x{7C0B}' .
+'\x{7C0C}\x{7C0D}\x{7C0E}\x{7C0F}\x{7C10}\x{7C11}\x{7C12}\x{7C13}\x{7C15}' .
+'\x{7C16}\x{7C17}\x{7C18}\x{7C19}\x{7C1A}\x{7C1C}\x{7C1D}\x{7C1E}\x{7C1F}' .
+'\x{7C20}\x{7C21}\x{7C22}\x{7C23}\x{7C24}\x{7C25}\x{7C26}\x{7C27}\x{7C28}' .
+'\x{7C29}\x{7C2A}\x{7C2B}\x{7C2C}\x{7C2D}\x{7C30}\x{7C31}\x{7C32}\x{7C33}' .
+'\x{7C34}\x{7C35}\x{7C36}\x{7C37}\x{7C38}\x{7C39}\x{7C3A}\x{7C3B}\x{7C3C}' .
+'\x{7C3D}\x{7C3E}\x{7C3F}\x{7C40}\x{7C41}\x{7C42}\x{7C43}\x{7C44}\x{7C45}' .
+'\x{7C46}\x{7C47}\x{7C48}\x{7C49}\x{7C4A}\x{7C4B}\x{7C4C}\x{7C4D}\x{7C4E}' .
+'\x{7C50}\x{7C51}\x{7C53}\x{7C54}\x{7C56}\x{7C57}\x{7C58}\x{7C59}\x{7C5A}' .
+'\x{7C5B}\x{7C5C}\x{7C5E}\x{7C5F}\x{7C60}\x{7C61}\x{7C62}\x{7C63}\x{7C64}' .
+'\x{7C65}\x{7C66}\x{7C67}\x{7C68}\x{7C69}\x{7C6A}\x{7C6B}\x{7C6C}\x{7C6D}' .
+'\x{7C6E}\x{7C6F}\x{7C70}\x{7C71}\x{7C72}\x{7C73}\x{7C74}\x{7C75}\x{7C77}' .
+'\x{7C78}\x{7C79}\x{7C7A}\x{7C7B}\x{7C7C}\x{7C7D}\x{7C7E}\x{7C7F}\x{7C80}' .
+'\x{7C81}\x{7C82}\x{7C84}\x{7C85}\x{7C86}\x{7C88}\x{7C89}\x{7C8A}\x{7C8B}' .
+'\x{7C8C}\x{7C8D}\x{7C8E}\x{7C8F}\x{7C90}\x{7C91}\x{7C92}\x{7C94}\x{7C95}' .
+'\x{7C96}\x{7C97}\x{7C98}\x{7C99}\x{7C9B}\x{7C9C}\x{7C9D}\x{7C9E}\x{7C9F}' .
+'\x{7CA0}\x{7CA1}\x{7CA2}\x{7CA3}\x{7CA4}\x{7CA5}\x{7CA6}\x{7CA7}\x{7CA8}' .
+'\x{7CA9}\x{7CAA}\x{7CAD}\x{7CAE}\x{7CAF}\x{7CB0}\x{7CB1}\x{7CB2}\x{7CB3}' .
+'\x{7CB4}\x{7CB5}\x{7CB6}\x{7CB7}\x{7CB8}\x{7CB9}\x{7CBA}\x{7CBB}\x{7CBC}' .
+'\x{7CBD}\x{7CBE}\x{7CBF}\x{7CC0}\x{7CC1}\x{7CC2}\x{7CC3}\x{7CC4}\x{7CC5}' .
+'\x{7CC6}\x{7CC7}\x{7CC8}\x{7CC9}\x{7CCA}\x{7CCB}\x{7CCC}\x{7CCD}\x{7CCE}' .
+'\x{7CCF}\x{7CD0}\x{7CD1}\x{7CD2}\x{7CD4}\x{7CD5}\x{7CD6}\x{7CD7}\x{7CD8}' .
+'\x{7CD9}\x{7CDC}\x{7CDD}\x{7CDE}\x{7CDF}\x{7CE0}\x{7CE2}\x{7CE4}\x{7CE7}' .
+'\x{7CE8}\x{7CE9}\x{7CEA}\x{7CEB}\x{7CEC}\x{7CED}\x{7CEE}\x{7CEF}\x{7CF0}' .
+'\x{7CF1}\x{7CF2}\x{7CF3}\x{7CF4}\x{7CF5}\x{7CF6}\x{7CF7}\x{7CF8}\x{7CF9}' .
+'\x{7CFA}\x{7CFB}\x{7CFD}\x{7CFE}\x{7D00}\x{7D01}\x{7D02}\x{7D03}\x{7D04}' .
+'\x{7D05}\x{7D06}\x{7D07}\x{7D08}\x{7D09}\x{7D0A}\x{7D0B}\x{7D0C}\x{7D0D}' .
+'\x{7D0E}\x{7D0F}\x{7D10}\x{7D11}\x{7D12}\x{7D13}\x{7D14}\x{7D15}\x{7D16}' .
+'\x{7D17}\x{7D18}\x{7D19}\x{7D1A}\x{7D1B}\x{7D1C}\x{7D1D}\x{7D1E}\x{7D1F}' .
+'\x{7D20}\x{7D21}\x{7D22}\x{7D24}\x{7D25}\x{7D26}\x{7D27}\x{7D28}\x{7D29}' .
+'\x{7D2B}\x{7D2C}\x{7D2E}\x{7D2F}\x{7D30}\x{7D31}\x{7D32}\x{7D33}\x{7D34}' .
+'\x{7D35}\x{7D36}\x{7D37}\x{7D38}\x{7D39}\x{7D3A}\x{7D3B}\x{7D3C}\x{7D3D}' .
+'\x{7D3E}\x{7D3F}\x{7D40}\x{7D41}\x{7D42}\x{7D43}\x{7D44}\x{7D45}\x{7D46}' .
+'\x{7D47}\x{7D49}\x{7D4A}\x{7D4B}\x{7D4C}\x{7D4E}\x{7D4F}\x{7D50}\x{7D51}' .
+'\x{7D52}\x{7D53}\x{7D54}\x{7D55}\x{7D56}\x{7D57}\x{7D58}\x{7D59}\x{7D5B}' .
+'\x{7D5C}\x{7D5D}\x{7D5E}\x{7D5F}\x{7D60}\x{7D61}\x{7D62}\x{7D63}\x{7D65}' .
+'\x{7D66}\x{7D67}\x{7D68}\x{7D69}\x{7D6A}\x{7D6B}\x{7D6C}\x{7D6D}\x{7D6E}' .
+'\x{7D6F}\x{7D70}\x{7D71}\x{7D72}\x{7D73}\x{7D74}\x{7D75}\x{7D76}\x{7D77}' .
+'\x{7D79}\x{7D7A}\x{7D7B}\x{7D7C}\x{7D7D}\x{7D7E}\x{7D7F}\x{7D80}\x{7D81}' .
+'\x{7D83}\x{7D84}\x{7D85}\x{7D86}\x{7D87}\x{7D88}\x{7D89}\x{7D8A}\x{7D8B}' .
+'\x{7D8C}\x{7D8D}\x{7D8E}\x{7D8F}\x{7D90}\x{7D91}\x{7D92}\x{7D93}\x{7D94}' .
+'\x{7D96}\x{7D97}\x{7D99}\x{7D9B}\x{7D9C}\x{7D9D}\x{7D9E}\x{7D9F}\x{7DA0}' .
+'\x{7DA1}\x{7DA2}\x{7DA3}\x{7DA5}\x{7DA6}\x{7DA7}\x{7DA9}\x{7DAA}\x{7DAB}' .
+'\x{7DAC}\x{7DAD}\x{7DAE}\x{7DAF}\x{7DB0}\x{7DB1}\x{7DB2}\x{7DB3}\x{7DB4}' .
+'\x{7DB5}\x{7DB6}\x{7DB7}\x{7DB8}\x{7DB9}\x{7DBA}\x{7DBB}\x{7DBC}\x{7DBD}' .
+'\x{7DBE}\x{7DBF}\x{7DC0}\x{7DC1}\x{7DC2}\x{7DC3}\x{7DC4}\x{7DC5}\x{7DC6}' .
+'\x{7DC7}\x{7DC8}\x{7DC9}\x{7DCA}\x{7DCB}\x{7DCC}\x{7DCE}\x{7DCF}\x{7DD0}' .
+'\x{7DD1}\x{7DD2}\x{7DD4}\x{7DD5}\x{7DD6}\x{7DD7}\x{7DD8}\x{7DD9}\x{7DDA}' .
+'\x{7DDB}\x{7DDD}\x{7DDE}\x{7DDF}\x{7DE0}\x{7DE1}\x{7DE2}\x{7DE3}\x{7DE6}' .
+'\x{7DE7}\x{7DE8}\x{7DE9}\x{7DEA}\x{7DEC}\x{7DED}\x{7DEE}\x{7DEF}\x{7DF0}' .
+'\x{7DF1}\x{7DF2}\x{7DF3}\x{7DF4}\x{7DF5}\x{7DF6}\x{7DF7}\x{7DF8}\x{7DF9}' .
+'\x{7DFA}\x{7DFB}\x{7DFC}\x{7E00}\x{7E01}\x{7E02}\x{7E03}\x{7E04}\x{7E05}' .
+'\x{7E06}\x{7E07}\x{7E08}\x{7E09}\x{7E0A}\x{7E0B}\x{7E0C}\x{7E0D}\x{7E0E}' .
+'\x{7E0F}\x{7E10}\x{7E11}\x{7E12}\x{7E13}\x{7E14}\x{7E15}\x{7E16}\x{7E17}' .
+'\x{7E19}\x{7E1A}\x{7E1B}\x{7E1C}\x{7E1D}\x{7E1E}\x{7E1F}\x{7E20}\x{7E21}' .
+'\x{7E22}\x{7E23}\x{7E24}\x{7E25}\x{7E26}\x{7E27}\x{7E28}\x{7E29}\x{7E2A}' .
+'\x{7E2B}\x{7E2C}\x{7E2D}\x{7E2E}\x{7E2F}\x{7E30}\x{7E31}\x{7E32}\x{7E33}' .
+'\x{7E34}\x{7E35}\x{7E36}\x{7E37}\x{7E38}\x{7E39}\x{7E3A}\x{7E3B}\x{7E3C}' .
+'\x{7E3D}\x{7E3E}\x{7E3F}\x{7E40}\x{7E41}\x{7E42}\x{7E43}\x{7E44}\x{7E45}' .
+'\x{7E46}\x{7E47}\x{7E48}\x{7E49}\x{7E4C}\x{7E4D}\x{7E4E}\x{7E4F}\x{7E50}' .
+'\x{7E51}\x{7E52}\x{7E53}\x{7E54}\x{7E55}\x{7E56}\x{7E57}\x{7E58}\x{7E59}' .
+'\x{7E5A}\x{7E5C}\x{7E5D}\x{7E5E}\x{7E5F}\x{7E60}\x{7E61}\x{7E62}\x{7E63}' .
+'\x{7E65}\x{7E66}\x{7E67}\x{7E68}\x{7E69}\x{7E6A}\x{7E6B}\x{7E6C}\x{7E6D}' .
+'\x{7E6E}\x{7E6F}\x{7E70}\x{7E71}\x{7E72}\x{7E73}\x{7E74}\x{7E75}\x{7E76}' .
+'\x{7E77}\x{7E78}\x{7E79}\x{7E7A}\x{7E7B}\x{7E7C}\x{7E7D}\x{7E7E}\x{7E7F}' .
+'\x{7E80}\x{7E81}\x{7E82}\x{7E83}\x{7E84}\x{7E85}\x{7E86}\x{7E87}\x{7E88}' .
+'\x{7E89}\x{7E8A}\x{7E8B}\x{7E8C}\x{7E8D}\x{7E8E}\x{7E8F}\x{7E90}\x{7E91}' .
+'\x{7E92}\x{7E93}\x{7E94}\x{7E95}\x{7E96}\x{7E97}\x{7E98}\x{7E99}\x{7E9A}' .
+'\x{7E9B}\x{7E9C}\x{7E9E}\x{7E9F}\x{7EA0}\x{7EA1}\x{7EA2}\x{7EA3}\x{7EA4}' .
+'\x{7EA5}\x{7EA6}\x{7EA7}\x{7EA8}\x{7EA9}\x{7EAA}\x{7EAB}\x{7EAC}\x{7EAD}' .
+'\x{7EAE}\x{7EAF}\x{7EB0}\x{7EB1}\x{7EB2}\x{7EB3}\x{7EB4}\x{7EB5}\x{7EB6}' .
+'\x{7EB7}\x{7EB8}\x{7EB9}\x{7EBA}\x{7EBB}\x{7EBC}\x{7EBD}\x{7EBE}\x{7EBF}' .
+'\x{7EC0}\x{7EC1}\x{7EC2}\x{7EC3}\x{7EC4}\x{7EC5}\x{7EC6}\x{7EC7}\x{7EC8}' .
+'\x{7EC9}\x{7ECA}\x{7ECB}\x{7ECC}\x{7ECD}\x{7ECE}\x{7ECF}\x{7ED0}\x{7ED1}' .
+'\x{7ED2}\x{7ED3}\x{7ED4}\x{7ED5}\x{7ED6}\x{7ED7}\x{7ED8}\x{7ED9}\x{7EDA}' .
+'\x{7EDB}\x{7EDC}\x{7EDD}\x{7EDE}\x{7EDF}\x{7EE0}\x{7EE1}\x{7EE2}\x{7EE3}' .
+'\x{7EE4}\x{7EE5}\x{7EE6}\x{7EE7}\x{7EE8}\x{7EE9}\x{7EEA}\x{7EEB}\x{7EEC}' .
+'\x{7EED}\x{7EEE}\x{7EEF}\x{7EF0}\x{7EF1}\x{7EF2}\x{7EF3}\x{7EF4}\x{7EF5}' .
+'\x{7EF6}\x{7EF7}\x{7EF8}\x{7EF9}\x{7EFA}\x{7EFB}\x{7EFC}\x{7EFD}\x{7EFE}' .
+'\x{7EFF}\x{7F00}\x{7F01}\x{7F02}\x{7F03}\x{7F04}\x{7F05}\x{7F06}\x{7F07}' .
+'\x{7F08}\x{7F09}\x{7F0A}\x{7F0B}\x{7F0C}\x{7F0D}\x{7F0E}\x{7F0F}\x{7F10}' .
+'\x{7F11}\x{7F12}\x{7F13}\x{7F14}\x{7F15}\x{7F16}\x{7F17}\x{7F18}\x{7F19}' .
+'\x{7F1A}\x{7F1B}\x{7F1C}\x{7F1D}\x{7F1E}\x{7F1F}\x{7F20}\x{7F21}\x{7F22}' .
+'\x{7F23}\x{7F24}\x{7F25}\x{7F26}\x{7F27}\x{7F28}\x{7F29}\x{7F2A}\x{7F2B}' .
+'\x{7F2C}\x{7F2D}\x{7F2E}\x{7F2F}\x{7F30}\x{7F31}\x{7F32}\x{7F33}\x{7F34}' .
+'\x{7F35}\x{7F36}\x{7F37}\x{7F38}\x{7F39}\x{7F3A}\x{7F3D}\x{7F3E}\x{7F3F}' .
+'\x{7F40}\x{7F42}\x{7F43}\x{7F44}\x{7F45}\x{7F47}\x{7F48}\x{7F49}\x{7F4A}' .
+'\x{7F4B}\x{7F4C}\x{7F4D}\x{7F4E}\x{7F4F}\x{7F50}\x{7F51}\x{7F52}\x{7F53}' .
+'\x{7F54}\x{7F55}\x{7F56}\x{7F57}\x{7F58}\x{7F5A}\x{7F5B}\x{7F5C}\x{7F5D}' .
+'\x{7F5E}\x{7F5F}\x{7F60}\x{7F61}\x{7F62}\x{7F63}\x{7F64}\x{7F65}\x{7F66}' .
+'\x{7F67}\x{7F68}\x{7F69}\x{7F6A}\x{7F6B}\x{7F6C}\x{7F6D}\x{7F6E}\x{7F6F}' .
+'\x{7F70}\x{7F71}\x{7F72}\x{7F73}\x{7F74}\x{7F75}\x{7F76}\x{7F77}\x{7F78}' .
+'\x{7F79}\x{7F7A}\x{7F7B}\x{7F7C}\x{7F7D}\x{7F7E}\x{7F7F}\x{7F80}\x{7F81}' .
+'\x{7F82}\x{7F83}\x{7F85}\x{7F86}\x{7F87}\x{7F88}\x{7F89}\x{7F8A}\x{7F8B}' .
+'\x{7F8C}\x{7F8D}\x{7F8E}\x{7F8F}\x{7F91}\x{7F92}\x{7F93}\x{7F94}\x{7F95}' .
+'\x{7F96}\x{7F98}\x{7F9A}\x{7F9B}\x{7F9C}\x{7F9D}\x{7F9E}\x{7F9F}\x{7FA0}' .
+'\x{7FA1}\x{7FA2}\x{7FA3}\x{7FA4}\x{7FA5}\x{7FA6}\x{7FA7}\x{7FA8}\x{7FA9}' .
+'\x{7FAA}\x{7FAB}\x{7FAC}\x{7FAD}\x{7FAE}\x{7FAF}\x{7FB0}\x{7FB1}\x{7FB2}' .
+'\x{7FB3}\x{7FB5}\x{7FB6}\x{7FB7}\x{7FB8}\x{7FB9}\x{7FBA}\x{7FBB}\x{7FBC}' .
+'\x{7FBD}\x{7FBE}\x{7FBF}\x{7FC0}\x{7FC1}\x{7FC2}\x{7FC3}\x{7FC4}\x{7FC5}' .
+'\x{7FC6}\x{7FC7}\x{7FC8}\x{7FC9}\x{7FCA}\x{7FCB}\x{7FCC}\x{7FCD}\x{7FCE}' .
+'\x{7FCF}\x{7FD0}\x{7FD1}\x{7FD2}\x{7FD3}\x{7FD4}\x{7FD5}\x{7FD7}\x{7FD8}' .
+'\x{7FD9}\x{7FDA}\x{7FDB}\x{7FDC}\x{7FDE}\x{7FDF}\x{7FE0}\x{7FE1}\x{7FE2}' .
+'\x{7FE3}\x{7FE5}\x{7FE6}\x{7FE7}\x{7FE8}\x{7FE9}\x{7FEA}\x{7FEB}\x{7FEC}' .
+'\x{7FED}\x{7FEE}\x{7FEF}\x{7FF0}\x{7FF1}\x{7FF2}\x{7FF3}\x{7FF4}\x{7FF5}' .
+'\x{7FF6}\x{7FF7}\x{7FF8}\x{7FF9}\x{7FFA}\x{7FFB}\x{7FFC}\x{7FFD}\x{7FFE}' .
+'\x{7FFF}\x{8000}\x{8001}\x{8002}\x{8003}\x{8004}\x{8005}\x{8006}\x{8007}' .
+'\x{8008}\x{8009}\x{800B}\x{800C}\x{800D}\x{800E}\x{800F}\x{8010}\x{8011}' .
+'\x{8012}\x{8013}\x{8014}\x{8015}\x{8016}\x{8017}\x{8018}\x{8019}\x{801A}' .
+'\x{801B}\x{801C}\x{801D}\x{801E}\x{801F}\x{8020}\x{8021}\x{8022}\x{8023}' .
+'\x{8024}\x{8025}\x{8026}\x{8027}\x{8028}\x{8029}\x{802A}\x{802B}\x{802C}' .
+'\x{802D}\x{802E}\x{8030}\x{8031}\x{8032}\x{8033}\x{8034}\x{8035}\x{8036}' .
+'\x{8037}\x{8038}\x{8039}\x{803A}\x{803B}\x{803D}\x{803E}\x{803F}\x{8041}' .
+'\x{8042}\x{8043}\x{8044}\x{8045}\x{8046}\x{8047}\x{8048}\x{8049}\x{804A}' .
+'\x{804B}\x{804C}\x{804D}\x{804E}\x{804F}\x{8050}\x{8051}\x{8052}\x{8053}' .
+'\x{8054}\x{8055}\x{8056}\x{8057}\x{8058}\x{8059}\x{805A}\x{805B}\x{805C}' .
+'\x{805D}\x{805E}\x{805F}\x{8060}\x{8061}\x{8062}\x{8063}\x{8064}\x{8065}' .
+'\x{8067}\x{8068}\x{8069}\x{806A}\x{806B}\x{806C}\x{806D}\x{806E}\x{806F}' .
+'\x{8070}\x{8071}\x{8072}\x{8073}\x{8074}\x{8075}\x{8076}\x{8077}\x{8078}' .
+'\x{8079}\x{807A}\x{807B}\x{807C}\x{807D}\x{807E}\x{807F}\x{8080}\x{8081}' .
+'\x{8082}\x{8083}\x{8084}\x{8085}\x{8086}\x{8087}\x{8089}\x{808A}\x{808B}' .
+'\x{808C}\x{808D}\x{808F}\x{8090}\x{8091}\x{8092}\x{8093}\x{8095}\x{8096}' .
+'\x{8097}\x{8098}\x{8099}\x{809A}\x{809B}\x{809C}\x{809D}\x{809E}\x{809F}' .
+'\x{80A0}\x{80A1}\x{80A2}\x{80A3}\x{80A4}\x{80A5}\x{80A9}\x{80AA}\x{80AB}' .
+'\x{80AD}\x{80AE}\x{80AF}\x{80B0}\x{80B1}\x{80B2}\x{80B4}\x{80B5}\x{80B6}' .
+'\x{80B7}\x{80B8}\x{80BA}\x{80BB}\x{80BC}\x{80BD}\x{80BE}\x{80BF}\x{80C0}' .
+'\x{80C1}\x{80C2}\x{80C3}\x{80C4}\x{80C5}\x{80C6}\x{80C7}\x{80C8}\x{80C9}' .
+'\x{80CA}\x{80CB}\x{80CC}\x{80CD}\x{80CE}\x{80CF}\x{80D0}\x{80D1}\x{80D2}' .
+'\x{80D3}\x{80D4}\x{80D5}\x{80D6}\x{80D7}\x{80D8}\x{80D9}\x{80DA}\x{80DB}' .
+'\x{80DC}\x{80DD}\x{80DE}\x{80E0}\x{80E1}\x{80E2}\x{80E3}\x{80E4}\x{80E5}' .
+'\x{80E6}\x{80E7}\x{80E8}\x{80E9}\x{80EA}\x{80EB}\x{80EC}\x{80ED}\x{80EE}' .
+'\x{80EF}\x{80F0}\x{80F1}\x{80F2}\x{80F3}\x{80F4}\x{80F5}\x{80F6}\x{80F7}' .
+'\x{80F8}\x{80F9}\x{80FA}\x{80FB}\x{80FC}\x{80FD}\x{80FE}\x{80FF}\x{8100}' .
+'\x{8101}\x{8102}\x{8105}\x{8106}\x{8107}\x{8108}\x{8109}\x{810A}\x{810B}' .
+'\x{810C}\x{810D}\x{810E}\x{810F}\x{8110}\x{8111}\x{8112}\x{8113}\x{8114}' .
+'\x{8115}\x{8116}\x{8118}\x{8119}\x{811A}\x{811B}\x{811C}\x{811D}\x{811E}' .
+'\x{811F}\x{8120}\x{8121}\x{8122}\x{8123}\x{8124}\x{8125}\x{8126}\x{8127}' .
+'\x{8128}\x{8129}\x{812A}\x{812B}\x{812C}\x{812D}\x{812E}\x{812F}\x{8130}' .
+'\x{8131}\x{8132}\x{8136}\x{8137}\x{8138}\x{8139}\x{813A}\x{813B}\x{813C}' .
+'\x{813D}\x{813E}\x{813F}\x{8140}\x{8141}\x{8142}\x{8143}\x{8144}\x{8145}' .
+'\x{8146}\x{8147}\x{8148}\x{8149}\x{814A}\x{814B}\x{814C}\x{814D}\x{814E}' .
+'\x{814F}\x{8150}\x{8151}\x{8152}\x{8153}\x{8154}\x{8155}\x{8156}\x{8157}' .
+'\x{8158}\x{8159}\x{815A}\x{815B}\x{815C}\x{815D}\x{815E}\x{8160}\x{8161}' .
+'\x{8162}\x{8163}\x{8164}\x{8165}\x{8166}\x{8167}\x{8168}\x{8169}\x{816A}' .
+'\x{816B}\x{816C}\x{816D}\x{816E}\x{816F}\x{8170}\x{8171}\x{8172}\x{8173}' .
+'\x{8174}\x{8175}\x{8176}\x{8177}\x{8178}\x{8179}\x{817A}\x{817B}\x{817C}' .
+'\x{817D}\x{817E}\x{817F}\x{8180}\x{8181}\x{8182}\x{8183}\x{8185}\x{8186}' .
+'\x{8187}\x{8188}\x{8189}\x{818A}\x{818B}\x{818C}\x{818D}\x{818E}\x{818F}' .
+'\x{8191}\x{8192}\x{8193}\x{8194}\x{8195}\x{8197}\x{8198}\x{8199}\x{819A}' .
+'\x{819B}\x{819C}\x{819D}\x{819E}\x{819F}\x{81A0}\x{81A1}\x{81A2}\x{81A3}' .
+'\x{81A4}\x{81A5}\x{81A6}\x{81A7}\x{81A8}\x{81A9}\x{81AA}\x{81AB}\x{81AC}' .
+'\x{81AD}\x{81AE}\x{81AF}\x{81B0}\x{81B1}\x{81B2}\x{81B3}\x{81B4}\x{81B5}' .
+'\x{81B6}\x{81B7}\x{81B8}\x{81B9}\x{81BA}\x{81BB}\x{81BC}\x{81BD}\x{81BE}' .
+'\x{81BF}\x{81C0}\x{81C1}\x{81C2}\x{81C3}\x{81C4}\x{81C5}\x{81C6}\x{81C7}' .
+'\x{81C8}\x{81C9}\x{81CA}\x{81CC}\x{81CD}\x{81CE}\x{81CF}\x{81D0}\x{81D1}' .
+'\x{81D2}\x{81D4}\x{81D5}\x{81D6}\x{81D7}\x{81D8}\x{81D9}\x{81DA}\x{81DB}' .
+'\x{81DC}\x{81DD}\x{81DE}\x{81DF}\x{81E0}\x{81E1}\x{81E2}\x{81E3}\x{81E5}' .
+'\x{81E6}\x{81E7}\x{81E8}\x{81E9}\x{81EA}\x{81EB}\x{81EC}\x{81ED}\x{81EE}' .
+'\x{81F1}\x{81F2}\x{81F3}\x{81F4}\x{81F5}\x{81F6}\x{81F7}\x{81F8}\x{81F9}' .
+'\x{81FA}\x{81FB}\x{81FC}\x{81FD}\x{81FE}\x{81FF}\x{8200}\x{8201}\x{8202}' .
+'\x{8203}\x{8204}\x{8205}\x{8206}\x{8207}\x{8208}\x{8209}\x{820A}\x{820B}' .
+'\x{820C}\x{820D}\x{820E}\x{820F}\x{8210}\x{8211}\x{8212}\x{8214}\x{8215}' .
+'\x{8216}\x{8218}\x{8219}\x{821A}\x{821B}\x{821C}\x{821D}\x{821E}\x{821F}' .
+'\x{8220}\x{8221}\x{8222}\x{8223}\x{8225}\x{8226}\x{8227}\x{8228}\x{8229}' .
+'\x{822A}\x{822B}\x{822C}\x{822D}\x{822F}\x{8230}\x{8231}\x{8232}\x{8233}' .
+'\x{8234}\x{8235}\x{8236}\x{8237}\x{8238}\x{8239}\x{823A}\x{823B}\x{823C}' .
+'\x{823D}\x{823E}\x{823F}\x{8240}\x{8242}\x{8243}\x{8244}\x{8245}\x{8246}' .
+'\x{8247}\x{8248}\x{8249}\x{824A}\x{824B}\x{824C}\x{824D}\x{824E}\x{824F}' .
+'\x{8250}\x{8251}\x{8252}\x{8253}\x{8254}\x{8255}\x{8256}\x{8257}\x{8258}' .
+'\x{8259}\x{825A}\x{825B}\x{825C}\x{825D}\x{825E}\x{825F}\x{8260}\x{8261}' .
+'\x{8263}\x{8264}\x{8266}\x{8267}\x{8268}\x{8269}\x{826A}\x{826B}\x{826C}' .
+'\x{826D}\x{826E}\x{826F}\x{8270}\x{8271}\x{8272}\x{8273}\x{8274}\x{8275}' .
+'\x{8276}\x{8277}\x{8278}\x{8279}\x{827A}\x{827B}\x{827C}\x{827D}\x{827E}' .
+'\x{827F}\x{8280}\x{8281}\x{8282}\x{8283}\x{8284}\x{8285}\x{8286}\x{8287}' .
+'\x{8288}\x{8289}\x{828A}\x{828B}\x{828D}\x{828E}\x{828F}\x{8290}\x{8291}' .
+'\x{8292}\x{8293}\x{8294}\x{8295}\x{8296}\x{8297}\x{8298}\x{8299}\x{829A}' .
+'\x{829B}\x{829C}\x{829D}\x{829E}\x{829F}\x{82A0}\x{82A1}\x{82A2}\x{82A3}' .
+'\x{82A4}\x{82A5}\x{82A6}\x{82A7}\x{82A8}\x{82A9}\x{82AA}\x{82AB}\x{82AC}' .
+'\x{82AD}\x{82AE}\x{82AF}\x{82B0}\x{82B1}\x{82B3}\x{82B4}\x{82B5}\x{82B6}' .
+'\x{82B7}\x{82B8}\x{82B9}\x{82BA}\x{82BB}\x{82BC}\x{82BD}\x{82BE}\x{82BF}' .
+'\x{82C0}\x{82C1}\x{82C2}\x{82C3}\x{82C4}\x{82C5}\x{82C6}\x{82C7}\x{82C8}' .
+'\x{82C9}\x{82CA}\x{82CB}\x{82CC}\x{82CD}\x{82CE}\x{82CF}\x{82D0}\x{82D1}' .
+'\x{82D2}\x{82D3}\x{82D4}\x{82D5}\x{82D6}\x{82D7}\x{82D8}\x{82D9}\x{82DA}' .
+'\x{82DB}\x{82DC}\x{82DD}\x{82DE}\x{82DF}\x{82E0}\x{82E1}\x{82E3}\x{82E4}' .
+'\x{82E5}\x{82E6}\x{82E7}\x{82E8}\x{82E9}\x{82EA}\x{82EB}\x{82EC}\x{82ED}' .
+'\x{82EE}\x{82EF}\x{82F0}\x{82F1}\x{82F2}\x{82F3}\x{82F4}\x{82F5}\x{82F6}' .
+'\x{82F7}\x{82F8}\x{82F9}\x{82FA}\x{82FB}\x{82FD}\x{82FE}\x{82FF}\x{8300}' .
+'\x{8301}\x{8302}\x{8303}\x{8304}\x{8305}\x{8306}\x{8307}\x{8308}\x{8309}' .
+'\x{830B}\x{830C}\x{830D}\x{830E}\x{830F}\x{8311}\x{8312}\x{8313}\x{8314}' .
+'\x{8315}\x{8316}\x{8317}\x{8318}\x{8319}\x{831A}\x{831B}\x{831C}\x{831D}' .
+'\x{831E}\x{831F}\x{8320}\x{8321}\x{8322}\x{8323}\x{8324}\x{8325}\x{8326}' .
+'\x{8327}\x{8328}\x{8329}\x{832A}\x{832B}\x{832C}\x{832D}\x{832E}\x{832F}' .
+'\x{8331}\x{8332}\x{8333}\x{8334}\x{8335}\x{8336}\x{8337}\x{8338}\x{8339}' .
+'\x{833A}\x{833B}\x{833C}\x{833D}\x{833E}\x{833F}\x{8340}\x{8341}\x{8342}' .
+'\x{8343}\x{8344}\x{8345}\x{8346}\x{8347}\x{8348}\x{8349}\x{834A}\x{834B}' .
+'\x{834C}\x{834D}\x{834E}\x{834F}\x{8350}\x{8351}\x{8352}\x{8353}\x{8354}' .
+'\x{8356}\x{8357}\x{8358}\x{8359}\x{835A}\x{835B}\x{835C}\x{835D}\x{835E}' .
+'\x{835F}\x{8360}\x{8361}\x{8362}\x{8363}\x{8364}\x{8365}\x{8366}\x{8367}' .
+'\x{8368}\x{8369}\x{836A}\x{836B}\x{836C}\x{836D}\x{836E}\x{836F}\x{8370}' .
+'\x{8371}\x{8372}\x{8373}\x{8374}\x{8375}\x{8376}\x{8377}\x{8378}\x{8379}' .
+'\x{837A}\x{837B}\x{837C}\x{837D}\x{837E}\x{837F}\x{8380}\x{8381}\x{8382}' .
+'\x{8383}\x{8384}\x{8385}\x{8386}\x{8387}\x{8388}\x{8389}\x{838A}\x{838B}' .
+'\x{838C}\x{838D}\x{838E}\x{838F}\x{8390}\x{8391}\x{8392}\x{8393}\x{8394}' .
+'\x{8395}\x{8396}\x{8397}\x{8398}\x{8399}\x{839A}\x{839B}\x{839C}\x{839D}' .
+'\x{839E}\x{83A0}\x{83A1}\x{83A2}\x{83A3}\x{83A4}\x{83A5}\x{83A6}\x{83A7}' .
+'\x{83A8}\x{83A9}\x{83AA}\x{83AB}\x{83AC}\x{83AD}\x{83AE}\x{83AF}\x{83B0}' .
+'\x{83B1}\x{83B2}\x{83B3}\x{83B4}\x{83B6}\x{83B7}\x{83B8}\x{83B9}\x{83BA}' .
+'\x{83BB}\x{83BC}\x{83BD}\x{83BF}\x{83C0}\x{83C1}\x{83C2}\x{83C3}\x{83C4}' .
+'\x{83C5}\x{83C6}\x{83C7}\x{83C8}\x{83C9}\x{83CA}\x{83CB}\x{83CC}\x{83CD}' .
+'\x{83CE}\x{83CF}\x{83D0}\x{83D1}\x{83D2}\x{83D3}\x{83D4}\x{83D5}\x{83D6}' .
+'\x{83D7}\x{83D8}\x{83D9}\x{83DA}\x{83DB}\x{83DC}\x{83DD}\x{83DE}\x{83DF}' .
+'\x{83E0}\x{83E1}\x{83E2}\x{83E3}\x{83E4}\x{83E5}\x{83E7}\x{83E8}\x{83E9}' .
+'\x{83EA}\x{83EB}\x{83EC}\x{83EE}\x{83EF}\x{83F0}\x{83F1}\x{83F2}\x{83F3}' .
+'\x{83F4}\x{83F5}\x{83F6}\x{83F7}\x{83F8}\x{83F9}\x{83FA}\x{83FB}\x{83FC}' .
+'\x{83FD}\x{83FE}\x{83FF}\x{8400}\x{8401}\x{8402}\x{8403}\x{8404}\x{8405}' .
+'\x{8406}\x{8407}\x{8408}\x{8409}\x{840A}\x{840B}\x{840C}\x{840D}\x{840E}' .
+'\x{840F}\x{8410}\x{8411}\x{8412}\x{8413}\x{8415}\x{8418}\x{8419}\x{841A}' .
+'\x{841B}\x{841C}\x{841D}\x{841E}\x{8421}\x{8422}\x{8423}\x{8424}\x{8425}' .
+'\x{8426}\x{8427}\x{8428}\x{8429}\x{842A}\x{842B}\x{842C}\x{842D}\x{842E}' .
+'\x{842F}\x{8430}\x{8431}\x{8432}\x{8433}\x{8434}\x{8435}\x{8436}\x{8437}' .
+'\x{8438}\x{8439}\x{843A}\x{843B}\x{843C}\x{843D}\x{843E}\x{843F}\x{8440}' .
+'\x{8441}\x{8442}\x{8443}\x{8444}\x{8445}\x{8446}\x{8447}\x{8448}\x{8449}' .
+'\x{844A}\x{844B}\x{844C}\x{844D}\x{844E}\x{844F}\x{8450}\x{8451}\x{8452}' .
+'\x{8453}\x{8454}\x{8455}\x{8456}\x{8457}\x{8459}\x{845A}\x{845B}\x{845C}' .
+'\x{845D}\x{845E}\x{845F}\x{8460}\x{8461}\x{8462}\x{8463}\x{8464}\x{8465}' .
+'\x{8466}\x{8467}\x{8468}\x{8469}\x{846A}\x{846B}\x{846C}\x{846D}\x{846E}' .
+'\x{846F}\x{8470}\x{8471}\x{8472}\x{8473}\x{8474}\x{8475}\x{8476}\x{8477}' .
+'\x{8478}\x{8479}\x{847A}\x{847B}\x{847C}\x{847D}\x{847E}\x{847F}\x{8480}' .
+'\x{8481}\x{8482}\x{8484}\x{8485}\x{8486}\x{8487}\x{8488}\x{8489}\x{848A}' .
+'\x{848B}\x{848C}\x{848D}\x{848E}\x{848F}\x{8490}\x{8491}\x{8492}\x{8493}' .
+'\x{8494}\x{8496}\x{8497}\x{8498}\x{8499}\x{849A}\x{849B}\x{849C}\x{849D}' .
+'\x{849E}\x{849F}\x{84A0}\x{84A1}\x{84A2}\x{84A3}\x{84A4}\x{84A5}\x{84A6}' .
+'\x{84A7}\x{84A8}\x{84A9}\x{84AA}\x{84AB}\x{84AC}\x{84AE}\x{84AF}\x{84B0}' .
+'\x{84B1}\x{84B2}\x{84B3}\x{84B4}\x{84B5}\x{84B6}\x{84B8}\x{84B9}\x{84BA}' .
+'\x{84BB}\x{84BC}\x{84BD}\x{84BE}\x{84BF}\x{84C0}\x{84C1}\x{84C2}\x{84C4}' .
+'\x{84C5}\x{84C6}\x{84C7}\x{84C8}\x{84C9}\x{84CA}\x{84CB}\x{84CC}\x{84CD}' .
+'\x{84CE}\x{84CF}\x{84D0}\x{84D1}\x{84D2}\x{84D3}\x{84D4}\x{84D5}\x{84D6}' .
+'\x{84D7}\x{84D8}\x{84D9}\x{84DB}\x{84DC}\x{84DD}\x{84DE}\x{84DF}\x{84E0}' .
+'\x{84E1}\x{84E2}\x{84E3}\x{84E4}\x{84E5}\x{84E6}\x{84E7}\x{84E8}\x{84E9}' .
+'\x{84EA}\x{84EB}\x{84EC}\x{84EE}\x{84EF}\x{84F0}\x{84F1}\x{84F2}\x{84F3}' .
+'\x{84F4}\x{84F5}\x{84F6}\x{84F7}\x{84F8}\x{84F9}\x{84FA}\x{84FB}\x{84FC}' .
+'\x{84FD}\x{84FE}\x{84FF}\x{8500}\x{8501}\x{8502}\x{8503}\x{8504}\x{8506}' .
+'\x{8507}\x{8508}\x{8509}\x{850A}\x{850B}\x{850C}\x{850D}\x{850E}\x{850F}' .
+'\x{8511}\x{8512}\x{8513}\x{8514}\x{8515}\x{8516}\x{8517}\x{8518}\x{8519}' .
+'\x{851A}\x{851B}\x{851C}\x{851D}\x{851E}\x{851F}\x{8520}\x{8521}\x{8522}' .
+'\x{8523}\x{8524}\x{8525}\x{8526}\x{8527}\x{8528}\x{8529}\x{852A}\x{852B}' .
+'\x{852C}\x{852D}\x{852E}\x{852F}\x{8530}\x{8531}\x{8534}\x{8535}\x{8536}' .
+'\x{8537}\x{8538}\x{8539}\x{853A}\x{853B}\x{853C}\x{853D}\x{853E}\x{853F}' .
+'\x{8540}\x{8541}\x{8542}\x{8543}\x{8544}\x{8545}\x{8546}\x{8547}\x{8548}' .
+'\x{8549}\x{854A}\x{854B}\x{854D}\x{854E}\x{854F}\x{8551}\x{8552}\x{8553}' .
+'\x{8554}\x{8555}\x{8556}\x{8557}\x{8558}\x{8559}\x{855A}\x{855B}\x{855C}' .
+'\x{855D}\x{855E}\x{855F}\x{8560}\x{8561}\x{8562}\x{8563}\x{8564}\x{8565}' .
+'\x{8566}\x{8567}\x{8568}\x{8569}\x{856A}\x{856B}\x{856C}\x{856D}\x{856E}' .
+'\x{856F}\x{8570}\x{8571}\x{8572}\x{8573}\x{8574}\x{8575}\x{8576}\x{8577}' .
+'\x{8578}\x{8579}\x{857A}\x{857B}\x{857C}\x{857D}\x{857E}\x{8580}\x{8581}' .
+'\x{8582}\x{8583}\x{8584}\x{8585}\x{8586}\x{8587}\x{8588}\x{8589}\x{858A}' .
+'\x{858B}\x{858C}\x{858D}\x{858E}\x{858F}\x{8590}\x{8591}\x{8592}\x{8594}' .
+'\x{8595}\x{8596}\x{8598}\x{8599}\x{859A}\x{859B}\x{859C}\x{859D}\x{859E}' .
+'\x{859F}\x{85A0}\x{85A1}\x{85A2}\x{85A3}\x{85A4}\x{85A5}\x{85A6}\x{85A7}' .
+'\x{85A8}\x{85A9}\x{85AA}\x{85AB}\x{85AC}\x{85AD}\x{85AE}\x{85AF}\x{85B0}' .
+'\x{85B1}\x{85B3}\x{85B4}\x{85B5}\x{85B6}\x{85B7}\x{85B8}\x{85B9}\x{85BA}' .
+'\x{85BC}\x{85BD}\x{85BE}\x{85BF}\x{85C0}\x{85C1}\x{85C2}\x{85C3}\x{85C4}' .
+'\x{85C5}\x{85C6}\x{85C7}\x{85C8}\x{85C9}\x{85CA}\x{85CB}\x{85CD}\x{85CE}' .
+'\x{85CF}\x{85D0}\x{85D1}\x{85D2}\x{85D3}\x{85D4}\x{85D5}\x{85D6}\x{85D7}' .
+'\x{85D8}\x{85D9}\x{85DA}\x{85DB}\x{85DC}\x{85DD}\x{85DE}\x{85DF}\x{85E0}' .
+'\x{85E1}\x{85E2}\x{85E3}\x{85E4}\x{85E5}\x{85E6}\x{85E7}\x{85E8}\x{85E9}' .
+'\x{85EA}\x{85EB}\x{85EC}\x{85ED}\x{85EF}\x{85F0}\x{85F1}\x{85F2}\x{85F4}' .
+'\x{85F5}\x{85F6}\x{85F7}\x{85F8}\x{85F9}\x{85FA}\x{85FB}\x{85FD}\x{85FE}' .
+'\x{85FF}\x{8600}\x{8601}\x{8602}\x{8604}\x{8605}\x{8606}\x{8607}\x{8608}' .
+'\x{8609}\x{860A}\x{860B}\x{860C}\x{860F}\x{8611}\x{8612}\x{8613}\x{8614}' .
+'\x{8616}\x{8617}\x{8618}\x{8619}\x{861A}\x{861B}\x{861C}\x{861E}\x{861F}' .
+'\x{8620}\x{8621}\x{8622}\x{8623}\x{8624}\x{8625}\x{8626}\x{8627}\x{8628}' .
+'\x{8629}\x{862A}\x{862B}\x{862C}\x{862D}\x{862E}\x{862F}\x{8630}\x{8631}' .
+'\x{8632}\x{8633}\x{8634}\x{8635}\x{8636}\x{8638}\x{8639}\x{863A}\x{863B}' .
+'\x{863C}\x{863D}\x{863E}\x{863F}\x{8640}\x{8641}\x{8642}\x{8643}\x{8644}' .
+'\x{8645}\x{8646}\x{8647}\x{8648}\x{8649}\x{864A}\x{864B}\x{864C}\x{864D}' .
+'\x{864E}\x{864F}\x{8650}\x{8651}\x{8652}\x{8653}\x{8654}\x{8655}\x{8656}' .
+'\x{8658}\x{8659}\x{865A}\x{865B}\x{865C}\x{865D}\x{865E}\x{865F}\x{8660}' .
+'\x{8661}\x{8662}\x{8663}\x{8664}\x{8665}\x{8666}\x{8667}\x{8668}\x{8669}' .
+'\x{866A}\x{866B}\x{866C}\x{866D}\x{866E}\x{866F}\x{8670}\x{8671}\x{8672}' .
+'\x{8673}\x{8674}\x{8676}\x{8677}\x{8678}\x{8679}\x{867A}\x{867B}\x{867C}' .
+'\x{867D}\x{867E}\x{867F}\x{8680}\x{8681}\x{8682}\x{8683}\x{8684}\x{8685}' .
+'\x{8686}\x{8687}\x{8688}\x{868A}\x{868B}\x{868C}\x{868D}\x{868E}\x{868F}' .
+'\x{8690}\x{8691}\x{8693}\x{8694}\x{8695}\x{8696}\x{8697}\x{8698}\x{8699}' .
+'\x{869A}\x{869B}\x{869C}\x{869D}\x{869E}\x{869F}\x{86A1}\x{86A2}\x{86A3}' .
+'\x{86A4}\x{86A5}\x{86A7}\x{86A8}\x{86A9}\x{86AA}\x{86AB}\x{86AC}\x{86AD}' .
+'\x{86AE}\x{86AF}\x{86B0}\x{86B1}\x{86B2}\x{86B3}\x{86B4}\x{86B5}\x{86B6}' .
+'\x{86B7}\x{86B8}\x{86B9}\x{86BA}\x{86BB}\x{86BC}\x{86BD}\x{86BE}\x{86BF}' .
+'\x{86C0}\x{86C1}\x{86C2}\x{86C3}\x{86C4}\x{86C5}\x{86C6}\x{86C7}\x{86C8}' .
+'\x{86C9}\x{86CA}\x{86CB}\x{86CC}\x{86CE}\x{86CF}\x{86D0}\x{86D1}\x{86D2}' .
+'\x{86D3}\x{86D4}\x{86D6}\x{86D7}\x{86D8}\x{86D9}\x{86DA}\x{86DB}\x{86DC}' .
+'\x{86DD}\x{86DE}\x{86DF}\x{86E1}\x{86E2}\x{86E3}\x{86E4}\x{86E5}\x{86E6}' .
+'\x{86E8}\x{86E9}\x{86EA}\x{86EB}\x{86EC}\x{86ED}\x{86EE}\x{86EF}\x{86F0}' .
+'\x{86F1}\x{86F2}\x{86F3}\x{86F4}\x{86F5}\x{86F6}\x{86F7}\x{86F8}\x{86F9}' .
+'\x{86FA}\x{86FB}\x{86FC}\x{86FE}\x{86FF}\x{8700}\x{8701}\x{8702}\x{8703}' .
+'\x{8704}\x{8705}\x{8706}\x{8707}\x{8708}\x{8709}\x{870A}\x{870B}\x{870C}' .
+'\x{870D}\x{870E}\x{870F}\x{8710}\x{8711}\x{8712}\x{8713}\x{8714}\x{8715}' .
+'\x{8716}\x{8717}\x{8718}\x{8719}\x{871A}\x{871B}\x{871C}\x{871E}\x{871F}' .
+'\x{8720}\x{8721}\x{8722}\x{8723}\x{8724}\x{8725}\x{8726}\x{8727}\x{8728}' .
+'\x{8729}\x{872A}\x{872B}\x{872C}\x{872D}\x{872E}\x{8730}\x{8731}\x{8732}' .
+'\x{8733}\x{8734}\x{8735}\x{8736}\x{8737}\x{8738}\x{8739}\x{873A}\x{873B}' .
+'\x{873C}\x{873E}\x{873F}\x{8740}\x{8741}\x{8742}\x{8743}\x{8744}\x{8746}' .
+'\x{8747}\x{8748}\x{8749}\x{874A}\x{874C}\x{874D}\x{874E}\x{874F}\x{8750}' .
+'\x{8751}\x{8752}\x{8753}\x{8754}\x{8755}\x{8756}\x{8757}\x{8758}\x{8759}' .
+'\x{875A}\x{875B}\x{875C}\x{875D}\x{875E}\x{875F}\x{8760}\x{8761}\x{8762}' .
+'\x{8763}\x{8764}\x{8765}\x{8766}\x{8767}\x{8768}\x{8769}\x{876A}\x{876B}' .
+'\x{876C}\x{876D}\x{876E}\x{876F}\x{8770}\x{8772}\x{8773}\x{8774}\x{8775}' .
+'\x{8776}\x{8777}\x{8778}\x{8779}\x{877A}\x{877B}\x{877C}\x{877D}\x{877E}' .
+'\x{8780}\x{8781}\x{8782}\x{8783}\x{8784}\x{8785}\x{8786}\x{8787}\x{8788}' .
+'\x{8789}\x{878A}\x{878B}\x{878C}\x{878D}\x{878F}\x{8790}\x{8791}\x{8792}' .
+'\x{8793}\x{8794}\x{8795}\x{8796}\x{8797}\x{8798}\x{879A}\x{879B}\x{879C}' .
+'\x{879D}\x{879E}\x{879F}\x{87A0}\x{87A1}\x{87A2}\x{87A3}\x{87A4}\x{87A5}' .
+'\x{87A6}\x{87A7}\x{87A8}\x{87A9}\x{87AA}\x{87AB}\x{87AC}\x{87AD}\x{87AE}' .
+'\x{87AF}\x{87B0}\x{87B1}\x{87B2}\x{87B3}\x{87B4}\x{87B5}\x{87B6}\x{87B7}' .
+'\x{87B8}\x{87B9}\x{87BA}\x{87BB}\x{87BC}\x{87BD}\x{87BE}\x{87BF}\x{87C0}' .
+'\x{87C1}\x{87C2}\x{87C3}\x{87C4}\x{87C5}\x{87C6}\x{87C7}\x{87C8}\x{87C9}' .
+'\x{87CA}\x{87CB}\x{87CC}\x{87CD}\x{87CE}\x{87CF}\x{87D0}\x{87D1}\x{87D2}' .
+'\x{87D3}\x{87D4}\x{87D5}\x{87D6}\x{87D7}\x{87D8}\x{87D9}\x{87DB}\x{87DC}' .
+'\x{87DD}\x{87DE}\x{87DF}\x{87E0}\x{87E1}\x{87E2}\x{87E3}\x{87E4}\x{87E5}' .
+'\x{87E6}\x{87E7}\x{87E8}\x{87E9}\x{87EA}\x{87EB}\x{87EC}\x{87ED}\x{87EE}' .
+'\x{87EF}\x{87F1}\x{87F2}\x{87F3}\x{87F4}\x{87F5}\x{87F6}\x{87F7}\x{87F8}' .
+'\x{87F9}\x{87FA}\x{87FB}\x{87FC}\x{87FD}\x{87FE}\x{87FF}\x{8800}\x{8801}' .
+'\x{8802}\x{8803}\x{8804}\x{8805}\x{8806}\x{8808}\x{8809}\x{880A}\x{880B}' .
+'\x{880C}\x{880D}\x{880E}\x{880F}\x{8810}\x{8811}\x{8813}\x{8814}\x{8815}' .
+'\x{8816}\x{8817}\x{8818}\x{8819}\x{881A}\x{881B}\x{881C}\x{881D}\x{881E}' .
+'\x{881F}\x{8820}\x{8821}\x{8822}\x{8823}\x{8824}\x{8825}\x{8826}\x{8827}' .
+'\x{8828}\x{8829}\x{882A}\x{882B}\x{882C}\x{882E}\x{882F}\x{8830}\x{8831}' .
+'\x{8832}\x{8833}\x{8834}\x{8835}\x{8836}\x{8837}\x{8838}\x{8839}\x{883B}' .
+'\x{883C}\x{883D}\x{883E}\x{883F}\x{8840}\x{8841}\x{8842}\x{8843}\x{8844}' .
+'\x{8845}\x{8846}\x{8848}\x{8849}\x{884A}\x{884B}\x{884C}\x{884D}\x{884E}' .
+'\x{884F}\x{8850}\x{8851}\x{8852}\x{8853}\x{8854}\x{8855}\x{8856}\x{8857}' .
+'\x{8859}\x{885A}\x{885B}\x{885D}\x{885E}\x{8860}\x{8861}\x{8862}\x{8863}' .
+'\x{8864}\x{8865}\x{8866}\x{8867}\x{8868}\x{8869}\x{886A}\x{886B}\x{886C}' .
+'\x{886D}\x{886E}\x{886F}\x{8870}\x{8871}\x{8872}\x{8873}\x{8874}\x{8875}' .
+'\x{8876}\x{8877}\x{8878}\x{8879}\x{887B}\x{887C}\x{887D}\x{887E}\x{887F}' .
+'\x{8880}\x{8881}\x{8882}\x{8883}\x{8884}\x{8885}\x{8886}\x{8887}\x{8888}' .
+'\x{8889}\x{888A}\x{888B}\x{888C}\x{888D}\x{888E}\x{888F}\x{8890}\x{8891}' .
+'\x{8892}\x{8893}\x{8894}\x{8895}\x{8896}\x{8897}\x{8898}\x{8899}\x{889A}' .
+'\x{889B}\x{889C}\x{889D}\x{889E}\x{889F}\x{88A0}\x{88A1}\x{88A2}\x{88A3}' .
+'\x{88A4}\x{88A5}\x{88A6}\x{88A7}\x{88A8}\x{88A9}\x{88AA}\x{88AB}\x{88AC}' .
+'\x{88AD}\x{88AE}\x{88AF}\x{88B0}\x{88B1}\x{88B2}\x{88B3}\x{88B4}\x{88B6}' .
+'\x{88B7}\x{88B8}\x{88B9}\x{88BA}\x{88BB}\x{88BC}\x{88BD}\x{88BE}\x{88BF}' .
+'\x{88C0}\x{88C1}\x{88C2}\x{88C3}\x{88C4}\x{88C5}\x{88C6}\x{88C7}\x{88C8}' .
+'\x{88C9}\x{88CA}\x{88CB}\x{88CC}\x{88CD}\x{88CE}\x{88CF}\x{88D0}\x{88D1}' .
+'\x{88D2}\x{88D3}\x{88D4}\x{88D5}\x{88D6}\x{88D7}\x{88D8}\x{88D9}\x{88DA}' .
+'\x{88DB}\x{88DC}\x{88DD}\x{88DE}\x{88DF}\x{88E0}\x{88E1}\x{88E2}\x{88E3}' .
+'\x{88E4}\x{88E5}\x{88E7}\x{88E8}\x{88EA}\x{88EB}\x{88EC}\x{88EE}\x{88EF}' .
+'\x{88F0}\x{88F1}\x{88F2}\x{88F3}\x{88F4}\x{88F5}\x{88F6}\x{88F7}\x{88F8}' .
+'\x{88F9}\x{88FA}\x{88FB}\x{88FC}\x{88FD}\x{88FE}\x{88FF}\x{8900}\x{8901}' .
+'\x{8902}\x{8904}\x{8905}\x{8906}\x{8907}\x{8908}\x{8909}\x{890A}\x{890B}' .
+'\x{890C}\x{890D}\x{890E}\x{8910}\x{8911}\x{8912}\x{8913}\x{8914}\x{8915}' .
+'\x{8916}\x{8917}\x{8918}\x{8919}\x{891A}\x{891B}\x{891C}\x{891D}\x{891E}' .
+'\x{891F}\x{8920}\x{8921}\x{8922}\x{8923}\x{8925}\x{8926}\x{8927}\x{8928}' .
+'\x{8929}\x{892A}\x{892B}\x{892C}\x{892D}\x{892E}\x{892F}\x{8930}\x{8931}' .
+'\x{8932}\x{8933}\x{8934}\x{8935}\x{8936}\x{8937}\x{8938}\x{8939}\x{893A}' .
+'\x{893B}\x{893C}\x{893D}\x{893E}\x{893F}\x{8940}\x{8941}\x{8942}\x{8943}' .
+'\x{8944}\x{8945}\x{8946}\x{8947}\x{8948}\x{8949}\x{894A}\x{894B}\x{894C}' .
+'\x{894E}\x{894F}\x{8950}\x{8951}\x{8952}\x{8953}\x{8954}\x{8955}\x{8956}' .
+'\x{8957}\x{8958}\x{8959}\x{895A}\x{895B}\x{895C}\x{895D}\x{895E}\x{895F}' .
+'\x{8960}\x{8961}\x{8962}\x{8963}\x{8964}\x{8966}\x{8967}\x{8968}\x{8969}' .
+'\x{896A}\x{896B}\x{896C}\x{896D}\x{896E}\x{896F}\x{8970}\x{8971}\x{8972}' .
+'\x{8973}\x{8974}\x{8976}\x{8977}\x{8978}\x{8979}\x{897A}\x{897B}\x{897C}' .
+'\x{897E}\x{897F}\x{8980}\x{8981}\x{8982}\x{8983}\x{8984}\x{8985}\x{8986}' .
+'\x{8987}\x{8988}\x{8989}\x{898A}\x{898B}\x{898C}\x{898E}\x{898F}\x{8991}' .
+'\x{8992}\x{8993}\x{8995}\x{8996}\x{8997}\x{8998}\x{899A}\x{899B}\x{899C}' .
+'\x{899D}\x{899E}\x{899F}\x{89A0}\x{89A1}\x{89A2}\x{89A3}\x{89A4}\x{89A5}' .
+'\x{89A6}\x{89A7}\x{89A8}\x{89AA}\x{89AB}\x{89AC}\x{89AD}\x{89AE}\x{89AF}' .
+'\x{89B1}\x{89B2}\x{89B3}\x{89B5}\x{89B6}\x{89B7}\x{89B8}\x{89B9}\x{89BA}' .
+'\x{89BD}\x{89BE}\x{89BF}\x{89C0}\x{89C1}\x{89C2}\x{89C3}\x{89C4}\x{89C5}' .
+'\x{89C6}\x{89C7}\x{89C8}\x{89C9}\x{89CA}\x{89CB}\x{89CC}\x{89CD}\x{89CE}' .
+'\x{89CF}\x{89D0}\x{89D1}\x{89D2}\x{89D3}\x{89D4}\x{89D5}\x{89D6}\x{89D7}' .
+'\x{89D8}\x{89D9}\x{89DA}\x{89DB}\x{89DC}\x{89DD}\x{89DE}\x{89DF}\x{89E0}' .
+'\x{89E1}\x{89E2}\x{89E3}\x{89E4}\x{89E5}\x{89E6}\x{89E7}\x{89E8}\x{89E9}' .
+'\x{89EA}\x{89EB}\x{89EC}\x{89ED}\x{89EF}\x{89F0}\x{89F1}\x{89F2}\x{89F3}' .
+'\x{89F4}\x{89F6}\x{89F7}\x{89F8}\x{89FA}\x{89FB}\x{89FC}\x{89FE}\x{89FF}' .
+'\x{8A00}\x{8A01}\x{8A02}\x{8A03}\x{8A04}\x{8A07}\x{8A08}\x{8A09}\x{8A0A}' .
+'\x{8A0B}\x{8A0C}\x{8A0D}\x{8A0E}\x{8A0F}\x{8A10}\x{8A11}\x{8A12}\x{8A13}' .
+'\x{8A15}\x{8A16}\x{8A17}\x{8A18}\x{8A1A}\x{8A1B}\x{8A1C}\x{8A1D}\x{8A1E}' .
+'\x{8A1F}\x{8A22}\x{8A23}\x{8A24}\x{8A25}\x{8A26}\x{8A27}\x{8A28}\x{8A29}' .
+'\x{8A2A}\x{8A2C}\x{8A2D}\x{8A2E}\x{8A2F}\x{8A30}\x{8A31}\x{8A32}\x{8A34}' .
+'\x{8A35}\x{8A36}\x{8A37}\x{8A38}\x{8A39}\x{8A3A}\x{8A3B}\x{8A3C}\x{8A3E}' .
+'\x{8A3F}\x{8A40}\x{8A41}\x{8A42}\x{8A43}\x{8A44}\x{8A45}\x{8A46}\x{8A47}' .
+'\x{8A48}\x{8A49}\x{8A4A}\x{8A4C}\x{8A4D}\x{8A4E}\x{8A4F}\x{8A50}\x{8A51}' .
+'\x{8A52}\x{8A53}\x{8A54}\x{8A55}\x{8A56}\x{8A57}\x{8A58}\x{8A59}\x{8A5A}' .
+'\x{8A5B}\x{8A5C}\x{8A5D}\x{8A5E}\x{8A5F}\x{8A60}\x{8A61}\x{8A62}\x{8A63}' .
+'\x{8A65}\x{8A66}\x{8A67}\x{8A68}\x{8A69}\x{8A6A}\x{8A6B}\x{8A6C}\x{8A6D}' .
+'\x{8A6E}\x{8A6F}\x{8A70}\x{8A71}\x{8A72}\x{8A73}\x{8A74}\x{8A75}\x{8A76}' .
+'\x{8A77}\x{8A79}\x{8A7A}\x{8A7B}\x{8A7C}\x{8A7E}\x{8A7F}\x{8A80}\x{8A81}' .
+'\x{8A82}\x{8A83}\x{8A84}\x{8A85}\x{8A86}\x{8A87}\x{8A89}\x{8A8A}\x{8A8B}' .
+'\x{8A8C}\x{8A8D}\x{8A8E}\x{8A8F}\x{8A90}\x{8A91}\x{8A92}\x{8A93}\x{8A94}' .
+'\x{8A95}\x{8A96}\x{8A97}\x{8A98}\x{8A99}\x{8A9A}\x{8A9B}\x{8A9C}\x{8A9D}' .
+'\x{8A9E}\x{8AA0}\x{8AA1}\x{8AA2}\x{8AA3}\x{8AA4}\x{8AA5}\x{8AA6}\x{8AA7}' .
+'\x{8AA8}\x{8AA9}\x{8AAA}\x{8AAB}\x{8AAC}\x{8AAE}\x{8AB0}\x{8AB1}\x{8AB2}' .
+'\x{8AB3}\x{8AB4}\x{8AB5}\x{8AB6}\x{8AB8}\x{8AB9}\x{8ABA}\x{8ABB}\x{8ABC}' .
+'\x{8ABD}\x{8ABE}\x{8ABF}\x{8AC0}\x{8AC1}\x{8AC2}\x{8AC3}\x{8AC4}\x{8AC5}' .
+'\x{8AC6}\x{8AC7}\x{8AC8}\x{8AC9}\x{8ACA}\x{8ACB}\x{8ACC}\x{8ACD}\x{8ACE}' .
+'\x{8ACF}\x{8AD1}\x{8AD2}\x{8AD3}\x{8AD4}\x{8AD5}\x{8AD6}\x{8AD7}\x{8AD8}' .
+'\x{8AD9}\x{8ADA}\x{8ADB}\x{8ADC}\x{8ADD}\x{8ADE}\x{8ADF}\x{8AE0}\x{8AE1}' .
+'\x{8AE2}\x{8AE3}\x{8AE4}\x{8AE5}\x{8AE6}\x{8AE7}\x{8AE8}\x{8AE9}\x{8AEA}' .
+'\x{8AEB}\x{8AED}\x{8AEE}\x{8AEF}\x{8AF0}\x{8AF1}\x{8AF2}\x{8AF3}\x{8AF4}' .
+'\x{8AF5}\x{8AF6}\x{8AF7}\x{8AF8}\x{8AF9}\x{8AFA}\x{8AFB}\x{8AFC}\x{8AFD}' .
+'\x{8AFE}\x{8AFF}\x{8B00}\x{8B01}\x{8B02}\x{8B03}\x{8B04}\x{8B05}\x{8B06}' .
+'\x{8B07}\x{8B08}\x{8B09}\x{8B0A}\x{8B0B}\x{8B0D}\x{8B0E}\x{8B0F}\x{8B10}' .
+'\x{8B11}\x{8B12}\x{8B13}\x{8B14}\x{8B15}\x{8B16}\x{8B17}\x{8B18}\x{8B19}' .
+'\x{8B1A}\x{8B1B}\x{8B1C}\x{8B1D}\x{8B1E}\x{8B1F}\x{8B20}\x{8B21}\x{8B22}' .
+'\x{8B23}\x{8B24}\x{8B25}\x{8B26}\x{8B27}\x{8B28}\x{8B2A}\x{8B2B}\x{8B2C}' .
+'\x{8B2D}\x{8B2E}\x{8B2F}\x{8B30}\x{8B31}\x{8B33}\x{8B34}\x{8B35}\x{8B36}' .
+'\x{8B37}\x{8B39}\x{8B3A}\x{8B3B}\x{8B3C}\x{8B3D}\x{8B3E}\x{8B40}\x{8B41}' .
+'\x{8B42}\x{8B43}\x{8B44}\x{8B45}\x{8B46}\x{8B47}\x{8B48}\x{8B49}\x{8B4A}' .
+'\x{8B4B}\x{8B4C}\x{8B4D}\x{8B4E}\x{8B4F}\x{8B50}\x{8B51}\x{8B52}\x{8B53}' .
+'\x{8B54}\x{8B55}\x{8B56}\x{8B57}\x{8B58}\x{8B59}\x{8B5A}\x{8B5B}\x{8B5C}' .
+'\x{8B5D}\x{8B5E}\x{8B5F}\x{8B60}\x{8B63}\x{8B64}\x{8B65}\x{8B66}\x{8B67}' .
+'\x{8B68}\x{8B6A}\x{8B6B}\x{8B6C}\x{8B6D}\x{8B6E}\x{8B6F}\x{8B70}\x{8B71}' .
+'\x{8B73}\x{8B74}\x{8B76}\x{8B77}\x{8B78}\x{8B79}\x{8B7A}\x{8B7B}\x{8B7D}' .
+'\x{8B7E}\x{8B7F}\x{8B80}\x{8B82}\x{8B83}\x{8B84}\x{8B85}\x{8B86}\x{8B88}' .
+'\x{8B89}\x{8B8A}\x{8B8B}\x{8B8C}\x{8B8E}\x{8B90}\x{8B91}\x{8B92}\x{8B93}' .
+'\x{8B94}\x{8B95}\x{8B96}\x{8B97}\x{8B98}\x{8B99}\x{8B9A}\x{8B9C}\x{8B9D}' .
+'\x{8B9E}\x{8B9F}\x{8BA0}\x{8BA1}\x{8BA2}\x{8BA3}\x{8BA4}\x{8BA5}\x{8BA6}' .
+'\x{8BA7}\x{8BA8}\x{8BA9}\x{8BAA}\x{8BAB}\x{8BAC}\x{8BAD}\x{8BAE}\x{8BAF}' .
+'\x{8BB0}\x{8BB1}\x{8BB2}\x{8BB3}\x{8BB4}\x{8BB5}\x{8BB6}\x{8BB7}\x{8BB8}' .
+'\x{8BB9}\x{8BBA}\x{8BBB}\x{8BBC}\x{8BBD}\x{8BBE}\x{8BBF}\x{8BC0}\x{8BC1}' .
+'\x{8BC2}\x{8BC3}\x{8BC4}\x{8BC5}\x{8BC6}\x{8BC7}\x{8BC8}\x{8BC9}\x{8BCA}' .
+'\x{8BCB}\x{8BCC}\x{8BCD}\x{8BCE}\x{8BCF}\x{8BD0}\x{8BD1}\x{8BD2}\x{8BD3}' .
+'\x{8BD4}\x{8BD5}\x{8BD6}\x{8BD7}\x{8BD8}\x{8BD9}\x{8BDA}\x{8BDB}\x{8BDC}' .
+'\x{8BDD}\x{8BDE}\x{8BDF}\x{8BE0}\x{8BE1}\x{8BE2}\x{8BE3}\x{8BE4}\x{8BE5}' .
+'\x{8BE6}\x{8BE7}\x{8BE8}\x{8BE9}\x{8BEA}\x{8BEB}\x{8BEC}\x{8BED}\x{8BEE}' .
+'\x{8BEF}\x{8BF0}\x{8BF1}\x{8BF2}\x{8BF3}\x{8BF4}\x{8BF5}\x{8BF6}\x{8BF7}' .
+'\x{8BF8}\x{8BF9}\x{8BFA}\x{8BFB}\x{8BFC}\x{8BFD}\x{8BFE}\x{8BFF}\x{8C00}' .
+'\x{8C01}\x{8C02}\x{8C03}\x{8C04}\x{8C05}\x{8C06}\x{8C07}\x{8C08}\x{8C09}' .
+'\x{8C0A}\x{8C0B}\x{8C0C}\x{8C0D}\x{8C0E}\x{8C0F}\x{8C10}\x{8C11}\x{8C12}' .
+'\x{8C13}\x{8C14}\x{8C15}\x{8C16}\x{8C17}\x{8C18}\x{8C19}\x{8C1A}\x{8C1B}' .
+'\x{8C1C}\x{8C1D}\x{8C1E}\x{8C1F}\x{8C20}\x{8C21}\x{8C22}\x{8C23}\x{8C24}' .
+'\x{8C25}\x{8C26}\x{8C27}\x{8C28}\x{8C29}\x{8C2A}\x{8C2B}\x{8C2C}\x{8C2D}' .
+'\x{8C2E}\x{8C2F}\x{8C30}\x{8C31}\x{8C32}\x{8C33}\x{8C34}\x{8C35}\x{8C36}' .
+'\x{8C37}\x{8C39}\x{8C3A}\x{8C3B}\x{8C3C}\x{8C3D}\x{8C3E}\x{8C3F}\x{8C41}' .
+'\x{8C42}\x{8C43}\x{8C45}\x{8C46}\x{8C47}\x{8C48}\x{8C49}\x{8C4A}\x{8C4B}' .
+'\x{8C4C}\x{8C4D}\x{8C4E}\x{8C4F}\x{8C50}\x{8C54}\x{8C55}\x{8C56}\x{8C57}' .
+'\x{8C59}\x{8C5A}\x{8C5B}\x{8C5C}\x{8C5D}\x{8C5E}\x{8C5F}\x{8C60}\x{8C61}' .
+'\x{8C62}\x{8C63}\x{8C64}\x{8C65}\x{8C66}\x{8C67}\x{8C68}\x{8C69}\x{8C6A}' .
+'\x{8C6B}\x{8C6C}\x{8C6D}\x{8C6E}\x{8C6F}\x{8C70}\x{8C71}\x{8C72}\x{8C73}' .
+'\x{8C75}\x{8C76}\x{8C77}\x{8C78}\x{8C79}\x{8C7A}\x{8C7B}\x{8C7D}\x{8C7E}' .
+'\x{8C80}\x{8C81}\x{8C82}\x{8C84}\x{8C85}\x{8C86}\x{8C88}\x{8C89}\x{8C8A}' .
+'\x{8C8C}\x{8C8D}\x{8C8F}\x{8C90}\x{8C91}\x{8C92}\x{8C93}\x{8C94}\x{8C95}' .
+'\x{8C96}\x{8C97}\x{8C98}\x{8C99}\x{8C9A}\x{8C9C}\x{8C9D}\x{8C9E}\x{8C9F}' .
+'\x{8CA0}\x{8CA1}\x{8CA2}\x{8CA3}\x{8CA4}\x{8CA5}\x{8CA7}\x{8CA8}\x{8CA9}' .
+'\x{8CAA}\x{8CAB}\x{8CAC}\x{8CAD}\x{8CAE}\x{8CAF}\x{8CB0}\x{8CB1}\x{8CB2}' .
+'\x{8CB3}\x{8CB4}\x{8CB5}\x{8CB6}\x{8CB7}\x{8CB8}\x{8CB9}\x{8CBA}\x{8CBB}' .
+'\x{8CBC}\x{8CBD}\x{8CBE}\x{8CBF}\x{8CC0}\x{8CC1}\x{8CC2}\x{8CC3}\x{8CC4}' .
+'\x{8CC5}\x{8CC6}\x{8CC7}\x{8CC8}\x{8CC9}\x{8CCA}\x{8CCC}\x{8CCE}\x{8CCF}' .
+'\x{8CD0}\x{8CD1}\x{8CD2}\x{8CD3}\x{8CD4}\x{8CD5}\x{8CD7}\x{8CD9}\x{8CDA}' .
+'\x{8CDB}\x{8CDC}\x{8CDD}\x{8CDE}\x{8CDF}\x{8CE0}\x{8CE1}\x{8CE2}\x{8CE3}' .
+'\x{8CE4}\x{8CE5}\x{8CE6}\x{8CE7}\x{8CE8}\x{8CEA}\x{8CEB}\x{8CEC}\x{8CED}' .
+'\x{8CEE}\x{8CEF}\x{8CF0}\x{8CF1}\x{8CF2}\x{8CF3}\x{8CF4}\x{8CF5}\x{8CF6}' .
+'\x{8CF8}\x{8CF9}\x{8CFA}\x{8CFB}\x{8CFC}\x{8CFD}\x{8CFE}\x{8CFF}\x{8D00}' .
+'\x{8D02}\x{8D03}\x{8D04}\x{8D05}\x{8D06}\x{8D07}\x{8D08}\x{8D09}\x{8D0A}' .
+'\x{8D0B}\x{8D0C}\x{8D0D}\x{8D0E}\x{8D0F}\x{8D10}\x{8D13}\x{8D14}\x{8D15}' .
+'\x{8D16}\x{8D17}\x{8D18}\x{8D19}\x{8D1A}\x{8D1B}\x{8D1C}\x{8D1D}\x{8D1E}' .
+'\x{8D1F}\x{8D20}\x{8D21}\x{8D22}\x{8D23}\x{8D24}\x{8D25}\x{8D26}\x{8D27}' .
+'\x{8D28}\x{8D29}\x{8D2A}\x{8D2B}\x{8D2C}\x{8D2D}\x{8D2E}\x{8D2F}\x{8D30}' .
+'\x{8D31}\x{8D32}\x{8D33}\x{8D34}\x{8D35}\x{8D36}\x{8D37}\x{8D38}\x{8D39}' .
+'\x{8D3A}\x{8D3B}\x{8D3C}\x{8D3D}\x{8D3E}\x{8D3F}\x{8D40}\x{8D41}\x{8D42}' .
+'\x{8D43}\x{8D44}\x{8D45}\x{8D46}\x{8D47}\x{8D48}\x{8D49}\x{8D4A}\x{8D4B}' .
+'\x{8D4C}\x{8D4D}\x{8D4E}\x{8D4F}\x{8D50}\x{8D51}\x{8D52}\x{8D53}\x{8D54}' .
+'\x{8D55}\x{8D56}\x{8D57}\x{8D58}\x{8D59}\x{8D5A}\x{8D5B}\x{8D5C}\x{8D5D}' .
+'\x{8D5E}\x{8D5F}\x{8D60}\x{8D61}\x{8D62}\x{8D63}\x{8D64}\x{8D65}\x{8D66}' .
+'\x{8D67}\x{8D68}\x{8D69}\x{8D6A}\x{8D6B}\x{8D6C}\x{8D6D}\x{8D6E}\x{8D6F}' .
+'\x{8D70}\x{8D71}\x{8D72}\x{8D73}\x{8D74}\x{8D75}\x{8D76}\x{8D77}\x{8D78}' .
+'\x{8D79}\x{8D7A}\x{8D7B}\x{8D7D}\x{8D7E}\x{8D7F}\x{8D80}\x{8D81}\x{8D82}' .
+'\x{8D83}\x{8D84}\x{8D85}\x{8D86}\x{8D87}\x{8D88}\x{8D89}\x{8D8A}\x{8D8B}' .
+'\x{8D8C}\x{8D8D}\x{8D8E}\x{8D8F}\x{8D90}\x{8D91}\x{8D92}\x{8D93}\x{8D94}' .
+'\x{8D95}\x{8D96}\x{8D97}\x{8D98}\x{8D99}\x{8D9A}\x{8D9B}\x{8D9C}\x{8D9D}' .
+'\x{8D9E}\x{8D9F}\x{8DA0}\x{8DA1}\x{8DA2}\x{8DA3}\x{8DA4}\x{8DA5}\x{8DA7}' .
+'\x{8DA8}\x{8DA9}\x{8DAA}\x{8DAB}\x{8DAC}\x{8DAD}\x{8DAE}\x{8DAF}\x{8DB0}' .
+'\x{8DB1}\x{8DB2}\x{8DB3}\x{8DB4}\x{8DB5}\x{8DB6}\x{8DB7}\x{8DB8}\x{8DB9}' .
+'\x{8DBA}\x{8DBB}\x{8DBC}\x{8DBD}\x{8DBE}\x{8DBF}\x{8DC1}\x{8DC2}\x{8DC3}' .
+'\x{8DC4}\x{8DC5}\x{8DC6}\x{8DC7}\x{8DC8}\x{8DC9}\x{8DCA}\x{8DCB}\x{8DCC}' .
+'\x{8DCD}\x{8DCE}\x{8DCF}\x{8DD0}\x{8DD1}\x{8DD2}\x{8DD3}\x{8DD4}\x{8DD5}' .
+'\x{8DD6}\x{8DD7}\x{8DD8}\x{8DD9}\x{8DDA}\x{8DDB}\x{8DDC}\x{8DDD}\x{8DDE}' .
+'\x{8DDF}\x{8DE0}\x{8DE1}\x{8DE2}\x{8DE3}\x{8DE4}\x{8DE6}\x{8DE7}\x{8DE8}' .
+'\x{8DE9}\x{8DEA}\x{8DEB}\x{8DEC}\x{8DED}\x{8DEE}\x{8DEF}\x{8DF0}\x{8DF1}' .
+'\x{8DF2}\x{8DF3}\x{8DF4}\x{8DF5}\x{8DF6}\x{8DF7}\x{8DF8}\x{8DF9}\x{8DFA}' .
+'\x{8DFB}\x{8DFC}\x{8DFD}\x{8DFE}\x{8DFF}\x{8E00}\x{8E02}\x{8E03}\x{8E04}' .
+'\x{8E05}\x{8E06}\x{8E07}\x{8E08}\x{8E09}\x{8E0A}\x{8E0C}\x{8E0D}\x{8E0E}' .
+'\x{8E0F}\x{8E10}\x{8E11}\x{8E12}\x{8E13}\x{8E14}\x{8E15}\x{8E16}\x{8E17}' .
+'\x{8E18}\x{8E19}\x{8E1A}\x{8E1B}\x{8E1C}\x{8E1D}\x{8E1E}\x{8E1F}\x{8E20}' .
+'\x{8E21}\x{8E22}\x{8E23}\x{8E24}\x{8E25}\x{8E26}\x{8E27}\x{8E28}\x{8E29}' .
+'\x{8E2A}\x{8E2B}\x{8E2C}\x{8E2D}\x{8E2E}\x{8E2F}\x{8E30}\x{8E31}\x{8E33}' .
+'\x{8E34}\x{8E35}\x{8E36}\x{8E37}\x{8E38}\x{8E39}\x{8E3A}\x{8E3B}\x{8E3C}' .
+'\x{8E3D}\x{8E3E}\x{8E3F}\x{8E40}\x{8E41}\x{8E42}\x{8E43}\x{8E44}\x{8E45}' .
+'\x{8E47}\x{8E48}\x{8E49}\x{8E4A}\x{8E4B}\x{8E4C}\x{8E4D}\x{8E4E}\x{8E50}' .
+'\x{8E51}\x{8E52}\x{8E53}\x{8E54}\x{8E55}\x{8E56}\x{8E57}\x{8E58}\x{8E59}' .
+'\x{8E5A}\x{8E5B}\x{8E5C}\x{8E5D}\x{8E5E}\x{8E5F}\x{8E60}\x{8E61}\x{8E62}' .
+'\x{8E63}\x{8E64}\x{8E65}\x{8E66}\x{8E67}\x{8E68}\x{8E69}\x{8E6A}\x{8E6B}' .
+'\x{8E6C}\x{8E6D}\x{8E6F}\x{8E70}\x{8E71}\x{8E72}\x{8E73}\x{8E74}\x{8E76}' .
+'\x{8E78}\x{8E7A}\x{8E7B}\x{8E7C}\x{8E7D}\x{8E7E}\x{8E7F}\x{8E80}\x{8E81}' .
+'\x{8E82}\x{8E83}\x{8E84}\x{8E85}\x{8E86}\x{8E87}\x{8E88}\x{8E89}\x{8E8A}' .
+'\x{8E8B}\x{8E8C}\x{8E8D}\x{8E8E}\x{8E8F}\x{8E90}\x{8E91}\x{8E92}\x{8E93}' .
+'\x{8E94}\x{8E95}\x{8E96}\x{8E97}\x{8E98}\x{8E9A}\x{8E9C}\x{8E9D}\x{8E9E}' .
+'\x{8E9F}\x{8EA0}\x{8EA1}\x{8EA3}\x{8EA4}\x{8EA5}\x{8EA6}\x{8EA7}\x{8EA8}' .
+'\x{8EA9}\x{8EAA}\x{8EAB}\x{8EAC}\x{8EAD}\x{8EAE}\x{8EAF}\x{8EB0}\x{8EB1}' .
+'\x{8EB2}\x{8EB4}\x{8EB5}\x{8EB8}\x{8EB9}\x{8EBA}\x{8EBB}\x{8EBC}\x{8EBD}' .
+'\x{8EBE}\x{8EBF}\x{8EC0}\x{8EC2}\x{8EC3}\x{8EC5}\x{8EC6}\x{8EC7}\x{8EC8}' .
+'\x{8EC9}\x{8ECA}\x{8ECB}\x{8ECC}\x{8ECD}\x{8ECE}\x{8ECF}\x{8ED0}\x{8ED1}' .
+'\x{8ED2}\x{8ED3}\x{8ED4}\x{8ED5}\x{8ED6}\x{8ED7}\x{8ED8}\x{8EDA}\x{8EDB}' .
+'\x{8EDC}\x{8EDD}\x{8EDE}\x{8EDF}\x{8EE0}\x{8EE1}\x{8EE4}\x{8EE5}\x{8EE6}' .
+'\x{8EE7}\x{8EE8}\x{8EE9}\x{8EEA}\x{8EEB}\x{8EEC}\x{8EED}\x{8EEE}\x{8EEF}' .
+'\x{8EF1}\x{8EF2}\x{8EF3}\x{8EF4}\x{8EF5}\x{8EF6}\x{8EF7}\x{8EF8}\x{8EF9}' .
+'\x{8EFA}\x{8EFB}\x{8EFC}\x{8EFD}\x{8EFE}\x{8EFF}\x{8F00}\x{8F01}\x{8F02}' .
+'\x{8F03}\x{8F04}\x{8F05}\x{8F06}\x{8F07}\x{8F08}\x{8F09}\x{8F0A}\x{8F0B}' .
+'\x{8F0D}\x{8F0E}\x{8F10}\x{8F11}\x{8F12}\x{8F13}\x{8F14}\x{8F15}\x{8F16}' .
+'\x{8F17}\x{8F18}\x{8F1A}\x{8F1B}\x{8F1C}\x{8F1D}\x{8F1E}\x{8F1F}\x{8F20}' .
+'\x{8F21}\x{8F22}\x{8F23}\x{8F24}\x{8F25}\x{8F26}\x{8F27}\x{8F28}\x{8F29}' .
+'\x{8F2A}\x{8F2B}\x{8F2C}\x{8F2E}\x{8F2F}\x{8F30}\x{8F31}\x{8F32}\x{8F33}' .
+'\x{8F34}\x{8F35}\x{8F36}\x{8F37}\x{8F38}\x{8F39}\x{8F3B}\x{8F3C}\x{8F3D}' .
+'\x{8F3E}\x{8F3F}\x{8F40}\x{8F42}\x{8F43}\x{8F44}\x{8F45}\x{8F46}\x{8F47}' .
+'\x{8F48}\x{8F49}\x{8F4A}\x{8F4B}\x{8F4C}\x{8F4D}\x{8F4E}\x{8F4F}\x{8F50}' .
+'\x{8F51}\x{8F52}\x{8F53}\x{8F54}\x{8F55}\x{8F56}\x{8F57}\x{8F58}\x{8F59}' .
+'\x{8F5A}\x{8F5B}\x{8F5D}\x{8F5E}\x{8F5F}\x{8F60}\x{8F61}\x{8F62}\x{8F63}' .
+'\x{8F64}\x{8F65}\x{8F66}\x{8F67}\x{8F68}\x{8F69}\x{8F6A}\x{8F6B}\x{8F6C}' .
+'\x{8F6D}\x{8F6E}\x{8F6F}\x{8F70}\x{8F71}\x{8F72}\x{8F73}\x{8F74}\x{8F75}' .
+'\x{8F76}\x{8F77}\x{8F78}\x{8F79}\x{8F7A}\x{8F7B}\x{8F7C}\x{8F7D}\x{8F7E}' .
+'\x{8F7F}\x{8F80}\x{8F81}\x{8F82}\x{8F83}\x{8F84}\x{8F85}\x{8F86}\x{8F87}' .
+'\x{8F88}\x{8F89}\x{8F8A}\x{8F8B}\x{8F8C}\x{8F8D}\x{8F8E}\x{8F8F}\x{8F90}' .
+'\x{8F91}\x{8F92}\x{8F93}\x{8F94}\x{8F95}\x{8F96}\x{8F97}\x{8F98}\x{8F99}' .
+'\x{8F9A}\x{8F9B}\x{8F9C}\x{8F9E}\x{8F9F}\x{8FA0}\x{8FA1}\x{8FA2}\x{8FA3}' .
+'\x{8FA5}\x{8FA6}\x{8FA7}\x{8FA8}\x{8FA9}\x{8FAA}\x{8FAB}\x{8FAC}\x{8FAD}' .
+'\x{8FAE}\x{8FAF}\x{8FB0}\x{8FB1}\x{8FB2}\x{8FB4}\x{8FB5}\x{8FB6}\x{8FB7}' .
+'\x{8FB8}\x{8FB9}\x{8FBB}\x{8FBC}\x{8FBD}\x{8FBE}\x{8FBF}\x{8FC0}\x{8FC1}' .
+'\x{8FC2}\x{8FC4}\x{8FC5}\x{8FC6}\x{8FC7}\x{8FC8}\x{8FC9}\x{8FCB}\x{8FCC}' .
+'\x{8FCD}\x{8FCE}\x{8FCF}\x{8FD0}\x{8FD1}\x{8FD2}\x{8FD3}\x{8FD4}\x{8FD5}' .
+'\x{8FD6}\x{8FD7}\x{8FD8}\x{8FD9}\x{8FDA}\x{8FDB}\x{8FDC}\x{8FDD}\x{8FDE}' .
+'\x{8FDF}\x{8FE0}\x{8FE1}\x{8FE2}\x{8FE3}\x{8FE4}\x{8FE5}\x{8FE6}\x{8FE8}' .
+'\x{8FE9}\x{8FEA}\x{8FEB}\x{8FEC}\x{8FED}\x{8FEE}\x{8FEF}\x{8FF0}\x{8FF1}' .
+'\x{8FF2}\x{8FF3}\x{8FF4}\x{8FF5}\x{8FF6}\x{8FF7}\x{8FF8}\x{8FF9}\x{8FFA}' .
+'\x{8FFB}\x{8FFC}\x{8FFD}\x{8FFE}\x{8FFF}\x{9000}\x{9001}\x{9002}\x{9003}' .
+'\x{9004}\x{9005}\x{9006}\x{9007}\x{9008}\x{9009}\x{900A}\x{900B}\x{900C}' .
+'\x{900D}\x{900F}\x{9010}\x{9011}\x{9012}\x{9013}\x{9014}\x{9015}\x{9016}' .
+'\x{9017}\x{9018}\x{9019}\x{901A}\x{901B}\x{901C}\x{901D}\x{901E}\x{901F}' .
+'\x{9020}\x{9021}\x{9022}\x{9023}\x{9024}\x{9025}\x{9026}\x{9027}\x{9028}' .
+'\x{9029}\x{902B}\x{902D}\x{902E}\x{902F}\x{9030}\x{9031}\x{9032}\x{9033}' .
+'\x{9034}\x{9035}\x{9036}\x{9038}\x{903A}\x{903B}\x{903C}\x{903D}\x{903E}' .
+'\x{903F}\x{9041}\x{9042}\x{9043}\x{9044}\x{9045}\x{9047}\x{9048}\x{9049}' .
+'\x{904A}\x{904B}\x{904C}\x{904D}\x{904E}\x{904F}\x{9050}\x{9051}\x{9052}' .
+'\x{9053}\x{9054}\x{9055}\x{9056}\x{9057}\x{9058}\x{9059}\x{905A}\x{905B}' .
+'\x{905C}\x{905D}\x{905E}\x{905F}\x{9060}\x{9061}\x{9062}\x{9063}\x{9064}' .
+'\x{9065}\x{9066}\x{9067}\x{9068}\x{9069}\x{906A}\x{906B}\x{906C}\x{906D}' .
+'\x{906E}\x{906F}\x{9070}\x{9071}\x{9072}\x{9073}\x{9074}\x{9075}\x{9076}' .
+'\x{9077}\x{9078}\x{9079}\x{907A}\x{907B}\x{907C}\x{907D}\x{907E}\x{907F}' .
+'\x{9080}\x{9081}\x{9082}\x{9083}\x{9084}\x{9085}\x{9086}\x{9087}\x{9088}' .
+'\x{9089}\x{908A}\x{908B}\x{908C}\x{908D}\x{908E}\x{908F}\x{9090}\x{9091}' .
+'\x{9092}\x{9093}\x{9094}\x{9095}\x{9096}\x{9097}\x{9098}\x{9099}\x{909A}' .
+'\x{909B}\x{909C}\x{909D}\x{909E}\x{909F}\x{90A0}\x{90A1}\x{90A2}\x{90A3}' .
+'\x{90A4}\x{90A5}\x{90A6}\x{90A7}\x{90A8}\x{90A9}\x{90AA}\x{90AC}\x{90AD}' .
+'\x{90AE}\x{90AF}\x{90B0}\x{90B1}\x{90B2}\x{90B3}\x{90B4}\x{90B5}\x{90B6}' .
+'\x{90B7}\x{90B8}\x{90B9}\x{90BA}\x{90BB}\x{90BC}\x{90BD}\x{90BE}\x{90BF}' .
+'\x{90C0}\x{90C1}\x{90C2}\x{90C3}\x{90C4}\x{90C5}\x{90C6}\x{90C7}\x{90C8}' .
+'\x{90C9}\x{90CA}\x{90CB}\x{90CE}\x{90CF}\x{90D0}\x{90D1}\x{90D3}\x{90D4}' .
+'\x{90D5}\x{90D6}\x{90D7}\x{90D8}\x{90D9}\x{90DA}\x{90DB}\x{90DC}\x{90DD}' .
+'\x{90DE}\x{90DF}\x{90E0}\x{90E1}\x{90E2}\x{90E3}\x{90E4}\x{90E5}\x{90E6}' .
+'\x{90E7}\x{90E8}\x{90E9}\x{90EA}\x{90EB}\x{90EC}\x{90ED}\x{90EE}\x{90EF}' .
+'\x{90F0}\x{90F1}\x{90F2}\x{90F3}\x{90F4}\x{90F5}\x{90F7}\x{90F8}\x{90F9}' .
+'\x{90FA}\x{90FB}\x{90FC}\x{90FD}\x{90FE}\x{90FF}\x{9100}\x{9101}\x{9102}' .
+'\x{9103}\x{9104}\x{9105}\x{9106}\x{9107}\x{9108}\x{9109}\x{910B}\x{910C}' .
+'\x{910D}\x{910E}\x{910F}\x{9110}\x{9111}\x{9112}\x{9113}\x{9114}\x{9115}' .
+'\x{9116}\x{9117}\x{9118}\x{9119}\x{911A}\x{911B}\x{911C}\x{911D}\x{911E}' .
+'\x{911F}\x{9120}\x{9121}\x{9122}\x{9123}\x{9124}\x{9125}\x{9126}\x{9127}' .
+'\x{9128}\x{9129}\x{912A}\x{912B}\x{912C}\x{912D}\x{912E}\x{912F}\x{9130}' .
+'\x{9131}\x{9132}\x{9133}\x{9134}\x{9135}\x{9136}\x{9137}\x{9138}\x{9139}' .
+'\x{913A}\x{913B}\x{913E}\x{913F}\x{9140}\x{9141}\x{9142}\x{9143}\x{9144}' .
+'\x{9145}\x{9146}\x{9147}\x{9148}\x{9149}\x{914A}\x{914B}\x{914C}\x{914D}' .
+'\x{914E}\x{914F}\x{9150}\x{9151}\x{9152}\x{9153}\x{9154}\x{9155}\x{9156}' .
+'\x{9157}\x{9158}\x{915A}\x{915B}\x{915C}\x{915D}\x{915E}\x{915F}\x{9160}' .
+'\x{9161}\x{9162}\x{9163}\x{9164}\x{9165}\x{9166}\x{9167}\x{9168}\x{9169}' .
+'\x{916A}\x{916B}\x{916C}\x{916D}\x{916E}\x{916F}\x{9170}\x{9171}\x{9172}' .
+'\x{9173}\x{9174}\x{9175}\x{9176}\x{9177}\x{9178}\x{9179}\x{917A}\x{917C}' .
+'\x{917D}\x{917E}\x{917F}\x{9180}\x{9181}\x{9182}\x{9183}\x{9184}\x{9185}' .
+'\x{9186}\x{9187}\x{9188}\x{9189}\x{918A}\x{918B}\x{918C}\x{918D}\x{918E}' .
+'\x{918F}\x{9190}\x{9191}\x{9192}\x{9193}\x{9194}\x{9196}\x{9199}\x{919A}' .
+'\x{919B}\x{919C}\x{919D}\x{919E}\x{919F}\x{91A0}\x{91A1}\x{91A2}\x{91A3}' .
+'\x{91A5}\x{91A6}\x{91A7}\x{91A8}\x{91AA}\x{91AB}\x{91AC}\x{91AD}\x{91AE}' .
+'\x{91AF}\x{91B0}\x{91B1}\x{91B2}\x{91B3}\x{91B4}\x{91B5}\x{91B6}\x{91B7}' .
+'\x{91B9}\x{91BA}\x{91BB}\x{91BC}\x{91BD}\x{91BE}\x{91C0}\x{91C1}\x{91C2}' .
+'\x{91C3}\x{91C5}\x{91C6}\x{91C7}\x{91C9}\x{91CA}\x{91CB}\x{91CC}\x{91CD}' .
+'\x{91CE}\x{91CF}\x{91D0}\x{91D1}\x{91D2}\x{91D3}\x{91D4}\x{91D5}\x{91D7}' .
+'\x{91D8}\x{91D9}\x{91DA}\x{91DB}\x{91DC}\x{91DD}\x{91DE}\x{91DF}\x{91E2}' .
+'\x{91E3}\x{91E4}\x{91E5}\x{91E6}\x{91E7}\x{91E8}\x{91E9}\x{91EA}\x{91EB}' .
+'\x{91EC}\x{91ED}\x{91EE}\x{91F0}\x{91F1}\x{91F2}\x{91F3}\x{91F4}\x{91F5}' .
+'\x{91F7}\x{91F8}\x{91F9}\x{91FA}\x{91FB}\x{91FD}\x{91FE}\x{91FF}\x{9200}' .
+'\x{9201}\x{9202}\x{9203}\x{9204}\x{9205}\x{9206}\x{9207}\x{9208}\x{9209}' .
+'\x{920A}\x{920B}\x{920C}\x{920D}\x{920E}\x{920F}\x{9210}\x{9211}\x{9212}' .
+'\x{9214}\x{9215}\x{9216}\x{9217}\x{9218}\x{9219}\x{921A}\x{921B}\x{921C}' .
+'\x{921D}\x{921E}\x{9220}\x{9221}\x{9223}\x{9224}\x{9225}\x{9226}\x{9227}' .
+'\x{9228}\x{9229}\x{922A}\x{922B}\x{922D}\x{922E}\x{922F}\x{9230}\x{9231}' .
+'\x{9232}\x{9233}\x{9234}\x{9235}\x{9236}\x{9237}\x{9238}\x{9239}\x{923A}' .
+'\x{923B}\x{923C}\x{923D}\x{923E}\x{923F}\x{9240}\x{9241}\x{9242}\x{9245}' .
+'\x{9246}\x{9247}\x{9248}\x{9249}\x{924A}\x{924B}\x{924C}\x{924D}\x{924E}' .
+'\x{924F}\x{9250}\x{9251}\x{9252}\x{9253}\x{9254}\x{9255}\x{9256}\x{9257}' .
+'\x{9258}\x{9259}\x{925A}\x{925B}\x{925C}\x{925D}\x{925E}\x{925F}\x{9260}' .
+'\x{9261}\x{9262}\x{9263}\x{9264}\x{9265}\x{9266}\x{9267}\x{9268}\x{926B}' .
+'\x{926C}\x{926D}\x{926E}\x{926F}\x{9270}\x{9272}\x{9273}\x{9274}\x{9275}' .
+'\x{9276}\x{9277}\x{9278}\x{9279}\x{927A}\x{927B}\x{927C}\x{927D}\x{927E}' .
+'\x{927F}\x{9280}\x{9282}\x{9283}\x{9285}\x{9286}\x{9287}\x{9288}\x{9289}' .
+'\x{928A}\x{928B}\x{928C}\x{928D}\x{928E}\x{928F}\x{9290}\x{9291}\x{9292}' .
+'\x{9293}\x{9294}\x{9295}\x{9296}\x{9297}\x{9298}\x{9299}\x{929A}\x{929B}' .
+'\x{929C}\x{929D}\x{929F}\x{92A0}\x{92A1}\x{92A2}\x{92A3}\x{92A4}\x{92A5}' .
+'\x{92A6}\x{92A7}\x{92A8}\x{92A9}\x{92AA}\x{92AB}\x{92AC}\x{92AD}\x{92AE}' .
+'\x{92AF}\x{92B0}\x{92B1}\x{92B2}\x{92B3}\x{92B4}\x{92B5}\x{92B6}\x{92B7}' .
+'\x{92B8}\x{92B9}\x{92BA}\x{92BB}\x{92BC}\x{92BE}\x{92BF}\x{92C0}\x{92C1}' .
+'\x{92C2}\x{92C3}\x{92C4}\x{92C5}\x{92C6}\x{92C7}\x{92C8}\x{92C9}\x{92CA}' .
+'\x{92CB}\x{92CC}\x{92CD}\x{92CE}\x{92CF}\x{92D0}\x{92D1}\x{92D2}\x{92D3}' .
+'\x{92D5}\x{92D6}\x{92D7}\x{92D8}\x{92D9}\x{92DA}\x{92DC}\x{92DD}\x{92DE}' .
+'\x{92DF}\x{92E0}\x{92E1}\x{92E3}\x{92E4}\x{92E5}\x{92E6}\x{92E7}\x{92E8}' .
+'\x{92E9}\x{92EA}\x{92EB}\x{92EC}\x{92ED}\x{92EE}\x{92EF}\x{92F0}\x{92F1}' .
+'\x{92F2}\x{92F3}\x{92F4}\x{92F5}\x{92F6}\x{92F7}\x{92F8}\x{92F9}\x{92FA}' .
+'\x{92FB}\x{92FC}\x{92FD}\x{92FE}\x{92FF}\x{9300}\x{9301}\x{9302}\x{9303}' .
+'\x{9304}\x{9305}\x{9306}\x{9307}\x{9308}\x{9309}\x{930A}\x{930B}\x{930C}' .
+'\x{930D}\x{930E}\x{930F}\x{9310}\x{9311}\x{9312}\x{9313}\x{9314}\x{9315}' .
+'\x{9316}\x{9317}\x{9318}\x{9319}\x{931A}\x{931B}\x{931D}\x{931E}\x{931F}' .
+'\x{9320}\x{9321}\x{9322}\x{9323}\x{9324}\x{9325}\x{9326}\x{9327}\x{9328}' .
+'\x{9329}\x{932A}\x{932B}\x{932D}\x{932E}\x{932F}\x{9332}\x{9333}\x{9334}' .
+'\x{9335}\x{9336}\x{9337}\x{9338}\x{9339}\x{933A}\x{933B}\x{933C}\x{933D}' .
+'\x{933E}\x{933F}\x{9340}\x{9341}\x{9342}\x{9343}\x{9344}\x{9345}\x{9346}' .
+'\x{9347}\x{9348}\x{9349}\x{934A}\x{934B}\x{934C}\x{934D}\x{934E}\x{934F}' .
+'\x{9350}\x{9351}\x{9352}\x{9353}\x{9354}\x{9355}\x{9356}\x{9357}\x{9358}' .
+'\x{9359}\x{935A}\x{935B}\x{935C}\x{935D}\x{935E}\x{935F}\x{9360}\x{9361}' .
+'\x{9363}\x{9364}\x{9365}\x{9366}\x{9367}\x{9369}\x{936A}\x{936C}\x{936D}' .
+'\x{936E}\x{9370}\x{9371}\x{9372}\x{9374}\x{9375}\x{9376}\x{9377}\x{9379}' .
+'\x{937A}\x{937B}\x{937C}\x{937D}\x{937E}\x{9380}\x{9382}\x{9383}\x{9384}' .
+'\x{9385}\x{9386}\x{9387}\x{9388}\x{9389}\x{938A}\x{938C}\x{938D}\x{938E}' .
+'\x{938F}\x{9390}\x{9391}\x{9392}\x{9393}\x{9394}\x{9395}\x{9396}\x{9397}' .
+'\x{9398}\x{9399}\x{939A}\x{939B}\x{939D}\x{939E}\x{939F}\x{93A1}\x{93A2}' .
+'\x{93A3}\x{93A4}\x{93A5}\x{93A6}\x{93A7}\x{93A8}\x{93A9}\x{93AA}\x{93AC}' .
+'\x{93AD}\x{93AE}\x{93AF}\x{93B0}\x{93B1}\x{93B2}\x{93B3}\x{93B4}\x{93B5}' .
+'\x{93B6}\x{93B7}\x{93B8}\x{93B9}\x{93BA}\x{93BC}\x{93BD}\x{93BE}\x{93BF}' .
+'\x{93C0}\x{93C1}\x{93C2}\x{93C3}\x{93C4}\x{93C5}\x{93C6}\x{93C7}\x{93C8}' .
+'\x{93C9}\x{93CA}\x{93CB}\x{93CC}\x{93CD}\x{93CE}\x{93CF}\x{93D0}\x{93D1}' .
+'\x{93D2}\x{93D3}\x{93D4}\x{93D5}\x{93D6}\x{93D7}\x{93D8}\x{93D9}\x{93DA}' .
+'\x{93DB}\x{93DC}\x{93DD}\x{93DE}\x{93DF}\x{93E1}\x{93E2}\x{93E3}\x{93E4}' .
+'\x{93E6}\x{93E7}\x{93E8}\x{93E9}\x{93EA}\x{93EB}\x{93EC}\x{93ED}\x{93EE}' .
+'\x{93EF}\x{93F0}\x{93F1}\x{93F2}\x{93F4}\x{93F5}\x{93F6}\x{93F7}\x{93F8}' .
+'\x{93F9}\x{93FA}\x{93FB}\x{93FC}\x{93FD}\x{93FE}\x{93FF}\x{9400}\x{9401}' .
+'\x{9403}\x{9404}\x{9405}\x{9406}\x{9407}\x{9408}\x{9409}\x{940A}\x{940B}' .
+'\x{940C}\x{940D}\x{940E}\x{940F}\x{9410}\x{9411}\x{9412}\x{9413}\x{9414}' .
+'\x{9415}\x{9416}\x{9418}\x{9419}\x{941B}\x{941D}\x{9420}\x{9422}\x{9423}' .
+'\x{9425}\x{9426}\x{9427}\x{9428}\x{9429}\x{942A}\x{942B}\x{942C}\x{942D}' .
+'\x{942E}\x{942F}\x{9430}\x{9431}\x{9432}\x{9433}\x{9434}\x{9435}\x{9436}' .
+'\x{9437}\x{9438}\x{9439}\x{943A}\x{943B}\x{943C}\x{943D}\x{943E}\x{943F}' .
+'\x{9440}\x{9441}\x{9442}\x{9444}\x{9445}\x{9446}\x{9447}\x{9448}\x{9449}' .
+'\x{944A}\x{944B}\x{944C}\x{944D}\x{944F}\x{9450}\x{9451}\x{9452}\x{9453}' .
+'\x{9454}\x{9455}\x{9456}\x{9457}\x{9458}\x{9459}\x{945B}\x{945C}\x{945D}' .
+'\x{945E}\x{945F}\x{9460}\x{9461}\x{9462}\x{9463}\x{9464}\x{9465}\x{9466}' .
+'\x{9467}\x{9468}\x{9469}\x{946A}\x{946B}\x{946D}\x{946E}\x{946F}\x{9470}' .
+'\x{9471}\x{9472}\x{9473}\x{9474}\x{9475}\x{9476}\x{9477}\x{9478}\x{9479}' .
+'\x{947A}\x{947C}\x{947D}\x{947E}\x{947F}\x{9480}\x{9481}\x{9482}\x{9483}' .
+'\x{9484}\x{9485}\x{9486}\x{9487}\x{9488}\x{9489}\x{948A}\x{948B}\x{948C}' .
+'\x{948D}\x{948E}\x{948F}\x{9490}\x{9491}\x{9492}\x{9493}\x{9494}\x{9495}' .
+'\x{9496}\x{9497}\x{9498}\x{9499}\x{949A}\x{949B}\x{949C}\x{949D}\x{949E}' .
+'\x{949F}\x{94A0}\x{94A1}\x{94A2}\x{94A3}\x{94A4}\x{94A5}\x{94A6}\x{94A7}' .
+'\x{94A8}\x{94A9}\x{94AA}\x{94AB}\x{94AC}\x{94AD}\x{94AE}\x{94AF}\x{94B0}' .
+'\x{94B1}\x{94B2}\x{94B3}\x{94B4}\x{94B5}\x{94B6}\x{94B7}\x{94B8}\x{94B9}' .
+'\x{94BA}\x{94BB}\x{94BC}\x{94BD}\x{94BE}\x{94BF}\x{94C0}\x{94C1}\x{94C2}' .
+'\x{94C3}\x{94C4}\x{94C5}\x{94C6}\x{94C7}\x{94C8}\x{94C9}\x{94CA}\x{94CB}' .
+'\x{94CC}\x{94CD}\x{94CE}\x{94CF}\x{94D0}\x{94D1}\x{94D2}\x{94D3}\x{94D4}' .
+'\x{94D5}\x{94D6}\x{94D7}\x{94D8}\x{94D9}\x{94DA}\x{94DB}\x{94DC}\x{94DD}' .
+'\x{94DE}\x{94DF}\x{94E0}\x{94E1}\x{94E2}\x{94E3}\x{94E4}\x{94E5}\x{94E6}' .
+'\x{94E7}\x{94E8}\x{94E9}\x{94EA}\x{94EB}\x{94EC}\x{94ED}\x{94EE}\x{94EF}' .
+'\x{94F0}\x{94F1}\x{94F2}\x{94F3}\x{94F4}\x{94F5}\x{94F6}\x{94F7}\x{94F8}' .
+'\x{94F9}\x{94FA}\x{94FB}\x{94FC}\x{94FD}\x{94FE}\x{94FF}\x{9500}\x{9501}' .
+'\x{9502}\x{9503}\x{9504}\x{9505}\x{9506}\x{9507}\x{9508}\x{9509}\x{950A}' .
+'\x{950B}\x{950C}\x{950D}\x{950E}\x{950F}\x{9510}\x{9511}\x{9512}\x{9513}' .
+'\x{9514}\x{9515}\x{9516}\x{9517}\x{9518}\x{9519}\x{951A}\x{951B}\x{951C}' .
+'\x{951D}\x{951E}\x{951F}\x{9520}\x{9521}\x{9522}\x{9523}\x{9524}\x{9525}' .
+'\x{9526}\x{9527}\x{9528}\x{9529}\x{952A}\x{952B}\x{952C}\x{952D}\x{952E}' .
+'\x{952F}\x{9530}\x{9531}\x{9532}\x{9533}\x{9534}\x{9535}\x{9536}\x{9537}' .
+'\x{9538}\x{9539}\x{953A}\x{953B}\x{953C}\x{953D}\x{953E}\x{953F}\x{9540}' .
+'\x{9541}\x{9542}\x{9543}\x{9544}\x{9545}\x{9546}\x{9547}\x{9548}\x{9549}' .
+'\x{954A}\x{954B}\x{954C}\x{954D}\x{954E}\x{954F}\x{9550}\x{9551}\x{9552}' .
+'\x{9553}\x{9554}\x{9555}\x{9556}\x{9557}\x{9558}\x{9559}\x{955A}\x{955B}' .
+'\x{955C}\x{955D}\x{955E}\x{955F}\x{9560}\x{9561}\x{9562}\x{9563}\x{9564}' .
+'\x{9565}\x{9566}\x{9567}\x{9568}\x{9569}\x{956A}\x{956B}\x{956C}\x{956D}' .
+'\x{956E}\x{956F}\x{9570}\x{9571}\x{9572}\x{9573}\x{9574}\x{9575}\x{9576}' .
+'\x{9577}\x{957A}\x{957B}\x{957C}\x{957D}\x{957F}\x{9580}\x{9581}\x{9582}' .
+'\x{9583}\x{9584}\x{9586}\x{9587}\x{9588}\x{9589}\x{958A}\x{958B}\x{958C}' .
+'\x{958D}\x{958E}\x{958F}\x{9590}\x{9591}\x{9592}\x{9593}\x{9594}\x{9595}' .
+'\x{9596}\x{9598}\x{9599}\x{959A}\x{959B}\x{959C}\x{959D}\x{959E}\x{959F}' .
+'\x{95A1}\x{95A2}\x{95A3}\x{95A4}\x{95A5}\x{95A6}\x{95A7}\x{95A8}\x{95A9}' .
+'\x{95AA}\x{95AB}\x{95AC}\x{95AD}\x{95AE}\x{95AF}\x{95B0}\x{95B1}\x{95B2}' .
+'\x{95B5}\x{95B6}\x{95B7}\x{95B9}\x{95BA}\x{95BB}\x{95BC}\x{95BD}\x{95BE}' .
+'\x{95BF}\x{95C0}\x{95C2}\x{95C3}\x{95C4}\x{95C5}\x{95C6}\x{95C7}\x{95C8}' .
+'\x{95C9}\x{95CA}\x{95CB}\x{95CC}\x{95CD}\x{95CE}\x{95CF}\x{95D0}\x{95D1}' .
+'\x{95D2}\x{95D3}\x{95D4}\x{95D5}\x{95D6}\x{95D7}\x{95D8}\x{95DA}\x{95DB}' .
+'\x{95DC}\x{95DE}\x{95DF}\x{95E0}\x{95E1}\x{95E2}\x{95E3}\x{95E4}\x{95E5}' .
+'\x{95E6}\x{95E7}\x{95E8}\x{95E9}\x{95EA}\x{95EB}\x{95EC}\x{95ED}\x{95EE}' .
+'\x{95EF}\x{95F0}\x{95F1}\x{95F2}\x{95F3}\x{95F4}\x{95F5}\x{95F6}\x{95F7}' .
+'\x{95F8}\x{95F9}\x{95FA}\x{95FB}\x{95FC}\x{95FD}\x{95FE}\x{95FF}\x{9600}' .
+'\x{9601}\x{9602}\x{9603}\x{9604}\x{9605}\x{9606}\x{9607}\x{9608}\x{9609}' .
+'\x{960A}\x{960B}\x{960C}\x{960D}\x{960E}\x{960F}\x{9610}\x{9611}\x{9612}' .
+'\x{9613}\x{9614}\x{9615}\x{9616}\x{9617}\x{9618}\x{9619}\x{961A}\x{961B}' .
+'\x{961C}\x{961D}\x{961E}\x{961F}\x{9620}\x{9621}\x{9622}\x{9623}\x{9624}' .
+'\x{9627}\x{9628}\x{962A}\x{962B}\x{962C}\x{962D}\x{962E}\x{962F}\x{9630}' .
+'\x{9631}\x{9632}\x{9633}\x{9634}\x{9635}\x{9636}\x{9637}\x{9638}\x{9639}' .
+'\x{963A}\x{963B}\x{963C}\x{963D}\x{963F}\x{9640}\x{9641}\x{9642}\x{9643}' .
+'\x{9644}\x{9645}\x{9646}\x{9647}\x{9648}\x{9649}\x{964A}\x{964B}\x{964C}' .
+'\x{964D}\x{964E}\x{964F}\x{9650}\x{9651}\x{9652}\x{9653}\x{9654}\x{9655}' .
+'\x{9658}\x{9659}\x{965A}\x{965B}\x{965C}\x{965D}\x{965E}\x{965F}\x{9660}' .
+'\x{9661}\x{9662}\x{9663}\x{9664}\x{9666}\x{9667}\x{9668}\x{9669}\x{966A}' .
+'\x{966B}\x{966C}\x{966D}\x{966E}\x{966F}\x{9670}\x{9671}\x{9672}\x{9673}' .
+'\x{9674}\x{9675}\x{9676}\x{9677}\x{9678}\x{967C}\x{967D}\x{967E}\x{9680}' .
+'\x{9683}\x{9684}\x{9685}\x{9686}\x{9687}\x{9688}\x{9689}\x{968A}\x{968B}' .
+'\x{968D}\x{968E}\x{968F}\x{9690}\x{9691}\x{9692}\x{9693}\x{9694}\x{9695}' .
+'\x{9697}\x{9698}\x{9699}\x{969B}\x{969C}\x{969E}\x{96A0}\x{96A1}\x{96A2}' .
+'\x{96A3}\x{96A4}\x{96A5}\x{96A6}\x{96A7}\x{96A8}\x{96A9}\x{96AA}\x{96AC}' .
+'\x{96AD}\x{96AE}\x{96B0}\x{96B1}\x{96B3}\x{96B4}\x{96B6}\x{96B7}\x{96B8}' .
+'\x{96B9}\x{96BA}\x{96BB}\x{96BC}\x{96BD}\x{96BE}\x{96BF}\x{96C0}\x{96C1}' .
+'\x{96C2}\x{96C3}\x{96C4}\x{96C5}\x{96C6}\x{96C7}\x{96C8}\x{96C9}\x{96CA}' .
+'\x{96CB}\x{96CC}\x{96CD}\x{96CE}\x{96CF}\x{96D0}\x{96D1}\x{96D2}\x{96D3}' .
+'\x{96D4}\x{96D5}\x{96D6}\x{96D7}\x{96D8}\x{96D9}\x{96DA}\x{96DB}\x{96DC}' .
+'\x{96DD}\x{96DE}\x{96DF}\x{96E0}\x{96E1}\x{96E2}\x{96E3}\x{96E5}\x{96E8}' .
+'\x{96E9}\x{96EA}\x{96EB}\x{96EC}\x{96ED}\x{96EE}\x{96EF}\x{96F0}\x{96F1}' .
+'\x{96F2}\x{96F3}\x{96F4}\x{96F5}\x{96F6}\x{96F7}\x{96F8}\x{96F9}\x{96FA}' .
+'\x{96FB}\x{96FD}\x{96FE}\x{96FF}\x{9700}\x{9701}\x{9702}\x{9703}\x{9704}' .
+'\x{9705}\x{9706}\x{9707}\x{9708}\x{9709}\x{970A}\x{970B}\x{970C}\x{970D}' .
+'\x{970E}\x{970F}\x{9710}\x{9711}\x{9712}\x{9713}\x{9715}\x{9716}\x{9718}' .
+'\x{9719}\x{971C}\x{971D}\x{971E}\x{971F}\x{9720}\x{9721}\x{9722}\x{9723}' .
+'\x{9724}\x{9725}\x{9726}\x{9727}\x{9728}\x{9729}\x{972A}\x{972B}\x{972C}' .
+'\x{972D}\x{972E}\x{972F}\x{9730}\x{9731}\x{9732}\x{9735}\x{9736}\x{9738}' .
+'\x{9739}\x{973A}\x{973B}\x{973C}\x{973D}\x{973E}\x{973F}\x{9742}\x{9743}' .
+'\x{9744}\x{9745}\x{9746}\x{9747}\x{9748}\x{9749}\x{974A}\x{974B}\x{974C}' .
+'\x{974E}\x{974F}\x{9750}\x{9751}\x{9752}\x{9753}\x{9754}\x{9755}\x{9756}' .
+'\x{9758}\x{9759}\x{975A}\x{975B}\x{975C}\x{975D}\x{975E}\x{975F}\x{9760}' .
+'\x{9761}\x{9762}\x{9765}\x{9766}\x{9767}\x{9768}\x{9769}\x{976A}\x{976B}' .
+'\x{976C}\x{976D}\x{976E}\x{976F}\x{9770}\x{9772}\x{9773}\x{9774}\x{9776}' .
+'\x{9777}\x{9778}\x{9779}\x{977A}\x{977B}\x{977C}\x{977D}\x{977E}\x{977F}' .
+'\x{9780}\x{9781}\x{9782}\x{9783}\x{9784}\x{9785}\x{9786}\x{9788}\x{978A}' .
+'\x{978B}\x{978C}\x{978D}\x{978E}\x{978F}\x{9790}\x{9791}\x{9792}\x{9793}' .
+'\x{9794}\x{9795}\x{9796}\x{9797}\x{9798}\x{9799}\x{979A}\x{979C}\x{979D}' .
+'\x{979E}\x{979F}\x{97A0}\x{97A1}\x{97A2}\x{97A3}\x{97A4}\x{97A5}\x{97A6}' .
+'\x{97A7}\x{97A8}\x{97AA}\x{97AB}\x{97AC}\x{97AD}\x{97AE}\x{97AF}\x{97B2}' .
+'\x{97B3}\x{97B4}\x{97B6}\x{97B7}\x{97B8}\x{97B9}\x{97BA}\x{97BB}\x{97BC}' .
+'\x{97BD}\x{97BF}\x{97C1}\x{97C2}\x{97C3}\x{97C4}\x{97C5}\x{97C6}\x{97C7}' .
+'\x{97C8}\x{97C9}\x{97CA}\x{97CB}\x{97CC}\x{97CD}\x{97CE}\x{97CF}\x{97D0}' .
+'\x{97D1}\x{97D3}\x{97D4}\x{97D5}\x{97D6}\x{97D7}\x{97D8}\x{97D9}\x{97DA}' .
+'\x{97DB}\x{97DC}\x{97DD}\x{97DE}\x{97DF}\x{97E0}\x{97E1}\x{97E2}\x{97E3}' .
+'\x{97E4}\x{97E5}\x{97E6}\x{97E7}\x{97E8}\x{97E9}\x{97EA}\x{97EB}\x{97EC}' .
+'\x{97ED}\x{97EE}\x{97EF}\x{97F0}\x{97F1}\x{97F2}\x{97F3}\x{97F4}\x{97F5}' .
+'\x{97F6}\x{97F7}\x{97F8}\x{97F9}\x{97FA}\x{97FB}\x{97FD}\x{97FE}\x{97FF}' .
+'\x{9800}\x{9801}\x{9802}\x{9803}\x{9804}\x{9805}\x{9806}\x{9807}\x{9808}' .
+'\x{9809}\x{980A}\x{980B}\x{980C}\x{980D}\x{980E}\x{980F}\x{9810}\x{9811}' .
+'\x{9812}\x{9813}\x{9814}\x{9815}\x{9816}\x{9817}\x{9818}\x{9819}\x{981A}' .
+'\x{981B}\x{981C}\x{981D}\x{981E}\x{9820}\x{9821}\x{9822}\x{9823}\x{9824}' .
+'\x{9826}\x{9827}\x{9828}\x{9829}\x{982B}\x{982D}\x{982E}\x{982F}\x{9830}' .
+'\x{9831}\x{9832}\x{9834}\x{9835}\x{9836}\x{9837}\x{9838}\x{9839}\x{983B}' .
+'\x{983C}\x{983D}\x{983F}\x{9840}\x{9841}\x{9843}\x{9844}\x{9845}\x{9846}' .
+'\x{9848}\x{9849}\x{984A}\x{984C}\x{984D}\x{984E}\x{984F}\x{9850}\x{9851}' .
+'\x{9852}\x{9853}\x{9854}\x{9855}\x{9857}\x{9858}\x{9859}\x{985A}\x{985B}' .
+'\x{985C}\x{985D}\x{985E}\x{985F}\x{9860}\x{9861}\x{9862}\x{9863}\x{9864}' .
+'\x{9865}\x{9867}\x{9869}\x{986A}\x{986B}\x{986C}\x{986D}\x{986E}\x{986F}' .
+'\x{9870}\x{9871}\x{9872}\x{9873}\x{9874}\x{9875}\x{9876}\x{9877}\x{9878}' .
+'\x{9879}\x{987A}\x{987B}\x{987C}\x{987D}\x{987E}\x{987F}\x{9880}\x{9881}' .
+'\x{9882}\x{9883}\x{9884}\x{9885}\x{9886}\x{9887}\x{9888}\x{9889}\x{988A}' .
+'\x{988B}\x{988C}\x{988D}\x{988E}\x{988F}\x{9890}\x{9891}\x{9892}\x{9893}' .
+'\x{9894}\x{9895}\x{9896}\x{9897}\x{9898}\x{9899}\x{989A}\x{989B}\x{989C}' .
+'\x{989D}\x{989E}\x{989F}\x{98A0}\x{98A1}\x{98A2}\x{98A3}\x{98A4}\x{98A5}' .
+'\x{98A6}\x{98A7}\x{98A8}\x{98A9}\x{98AA}\x{98AB}\x{98AC}\x{98AD}\x{98AE}' .
+'\x{98AF}\x{98B0}\x{98B1}\x{98B2}\x{98B3}\x{98B4}\x{98B5}\x{98B6}\x{98B8}' .
+'\x{98B9}\x{98BA}\x{98BB}\x{98BC}\x{98BD}\x{98BE}\x{98BF}\x{98C0}\x{98C1}' .
+'\x{98C2}\x{98C3}\x{98C4}\x{98C5}\x{98C6}\x{98C8}\x{98C9}\x{98CB}\x{98CC}' .
+'\x{98CD}\x{98CE}\x{98CF}\x{98D0}\x{98D1}\x{98D2}\x{98D3}\x{98D4}\x{98D5}' .
+'\x{98D6}\x{98D7}\x{98D8}\x{98D9}\x{98DA}\x{98DB}\x{98DC}\x{98DD}\x{98DE}' .
+'\x{98DF}\x{98E0}\x{98E2}\x{98E3}\x{98E5}\x{98E6}\x{98E7}\x{98E8}\x{98E9}' .
+'\x{98EA}\x{98EB}\x{98ED}\x{98EF}\x{98F0}\x{98F2}\x{98F3}\x{98F4}\x{98F5}' .
+'\x{98F6}\x{98F7}\x{98F9}\x{98FA}\x{98FC}\x{98FD}\x{98FE}\x{98FF}\x{9900}' .
+'\x{9901}\x{9902}\x{9903}\x{9904}\x{9905}\x{9906}\x{9907}\x{9908}\x{9909}' .
+'\x{990A}\x{990B}\x{990C}\x{990D}\x{990E}\x{990F}\x{9910}\x{9911}\x{9912}' .
+'\x{9913}\x{9914}\x{9915}\x{9916}\x{9917}\x{9918}\x{991A}\x{991B}\x{991C}' .
+'\x{991D}\x{991E}\x{991F}\x{9920}\x{9921}\x{9922}\x{9923}\x{9924}\x{9925}' .
+'\x{9926}\x{9927}\x{9928}\x{9929}\x{992A}\x{992B}\x{992C}\x{992D}\x{992E}' .
+'\x{992F}\x{9930}\x{9931}\x{9932}\x{9933}\x{9934}\x{9935}\x{9936}\x{9937}' .
+'\x{9938}\x{9939}\x{993A}\x{993C}\x{993D}\x{993E}\x{993F}\x{9940}\x{9941}' .
+'\x{9942}\x{9943}\x{9945}\x{9946}\x{9947}\x{9948}\x{9949}\x{994A}\x{994B}' .
+'\x{994C}\x{994E}\x{994F}\x{9950}\x{9951}\x{9952}\x{9953}\x{9954}\x{9955}' .
+'\x{9956}\x{9957}\x{9958}\x{9959}\x{995B}\x{995C}\x{995E}\x{995F}\x{9960}' .
+'\x{9961}\x{9962}\x{9963}\x{9964}\x{9965}\x{9966}\x{9967}\x{9968}\x{9969}' .
+'\x{996A}\x{996B}\x{996C}\x{996D}\x{996E}\x{996F}\x{9970}\x{9971}\x{9972}' .
+'\x{9973}\x{9974}\x{9975}\x{9976}\x{9977}\x{9978}\x{9979}\x{997A}\x{997B}' .
+'\x{997C}\x{997D}\x{997E}\x{997F}\x{9980}\x{9981}\x{9982}\x{9983}\x{9984}' .
+'\x{9985}\x{9986}\x{9987}\x{9988}\x{9989}\x{998A}\x{998B}\x{998C}\x{998D}' .
+'\x{998E}\x{998F}\x{9990}\x{9991}\x{9992}\x{9993}\x{9994}\x{9995}\x{9996}' .
+'\x{9997}\x{9998}\x{9999}\x{999A}\x{999B}\x{999C}\x{999D}\x{999E}\x{999F}' .
+'\x{99A0}\x{99A1}\x{99A2}\x{99A3}\x{99A4}\x{99A5}\x{99A6}\x{99A7}\x{99A8}' .
+'\x{99A9}\x{99AA}\x{99AB}\x{99AC}\x{99AD}\x{99AE}\x{99AF}\x{99B0}\x{99B1}' .
+'\x{99B2}\x{99B3}\x{99B4}\x{99B5}\x{99B6}\x{99B7}\x{99B8}\x{99B9}\x{99BA}' .
+'\x{99BB}\x{99BC}\x{99BD}\x{99BE}\x{99C0}\x{99C1}\x{99C2}\x{99C3}\x{99C4}' .
+'\x{99C6}\x{99C7}\x{99C8}\x{99C9}\x{99CA}\x{99CB}\x{99CC}\x{99CD}\x{99CE}' .
+'\x{99CF}\x{99D0}\x{99D1}\x{99D2}\x{99D3}\x{99D4}\x{99D5}\x{99D6}\x{99D7}' .
+'\x{99D8}\x{99D9}\x{99DA}\x{99DB}\x{99DC}\x{99DD}\x{99DE}\x{99DF}\x{99E1}' .
+'\x{99E2}\x{99E3}\x{99E4}\x{99E5}\x{99E7}\x{99E8}\x{99E9}\x{99EA}\x{99EC}' .
+'\x{99ED}\x{99EE}\x{99EF}\x{99F0}\x{99F1}\x{99F2}\x{99F3}\x{99F4}\x{99F6}' .
+'\x{99F7}\x{99F8}\x{99F9}\x{99FA}\x{99FB}\x{99FC}\x{99FD}\x{99FE}\x{99FF}' .
+'\x{9A00}\x{9A01}\x{9A02}\x{9A03}\x{9A04}\x{9A05}\x{9A06}\x{9A07}\x{9A08}' .
+'\x{9A09}\x{9A0A}\x{9A0B}\x{9A0C}\x{9A0D}\x{9A0E}\x{9A0F}\x{9A11}\x{9A14}' .
+'\x{9A15}\x{9A16}\x{9A19}\x{9A1A}\x{9A1B}\x{9A1C}\x{9A1D}\x{9A1E}\x{9A1F}' .
+'\x{9A20}\x{9A21}\x{9A22}\x{9A23}\x{9A24}\x{9A25}\x{9A26}\x{9A27}\x{9A29}' .
+'\x{9A2A}\x{9A2B}\x{9A2C}\x{9A2D}\x{9A2E}\x{9A2F}\x{9A30}\x{9A31}\x{9A32}' .
+'\x{9A33}\x{9A34}\x{9A35}\x{9A36}\x{9A37}\x{9A38}\x{9A39}\x{9A3A}\x{9A3C}' .
+'\x{9A3D}\x{9A3E}\x{9A3F}\x{9A40}\x{9A41}\x{9A42}\x{9A43}\x{9A44}\x{9A45}' .
+'\x{9A46}\x{9A47}\x{9A48}\x{9A49}\x{9A4A}\x{9A4B}\x{9A4C}\x{9A4D}\x{9A4E}' .
+'\x{9A4F}\x{9A50}\x{9A52}\x{9A53}\x{9A54}\x{9A55}\x{9A56}\x{9A57}\x{9A59}' .
+'\x{9A5A}\x{9A5B}\x{9A5C}\x{9A5E}\x{9A5F}\x{9A60}\x{9A61}\x{9A62}\x{9A64}' .
+'\x{9A65}\x{9A66}\x{9A67}\x{9A68}\x{9A69}\x{9A6A}\x{9A6B}\x{9A6C}\x{9A6D}' .
+'\x{9A6E}\x{9A6F}\x{9A70}\x{9A71}\x{9A72}\x{9A73}\x{9A74}\x{9A75}\x{9A76}' .
+'\x{9A77}\x{9A78}\x{9A79}\x{9A7A}\x{9A7B}\x{9A7C}\x{9A7D}\x{9A7E}\x{9A7F}' .
+'\x{9A80}\x{9A81}\x{9A82}\x{9A83}\x{9A84}\x{9A85}\x{9A86}\x{9A87}\x{9A88}' .
+'\x{9A89}\x{9A8A}\x{9A8B}\x{9A8C}\x{9A8D}\x{9A8E}\x{9A8F}\x{9A90}\x{9A91}' .
+'\x{9A92}\x{9A93}\x{9A94}\x{9A95}\x{9A96}\x{9A97}\x{9A98}\x{9A99}\x{9A9A}' .
+'\x{9A9B}\x{9A9C}\x{9A9D}\x{9A9E}\x{9A9F}\x{9AA0}\x{9AA1}\x{9AA2}\x{9AA3}' .
+'\x{9AA4}\x{9AA5}\x{9AA6}\x{9AA7}\x{9AA8}\x{9AAA}\x{9AAB}\x{9AAC}\x{9AAD}' .
+'\x{9AAE}\x{9AAF}\x{9AB0}\x{9AB1}\x{9AB2}\x{9AB3}\x{9AB4}\x{9AB5}\x{9AB6}' .
+'\x{9AB7}\x{9AB8}\x{9AB9}\x{9ABA}\x{9ABB}\x{9ABC}\x{9ABE}\x{9ABF}\x{9AC0}' .
+'\x{9AC1}\x{9AC2}\x{9AC3}\x{9AC4}\x{9AC5}\x{9AC6}\x{9AC7}\x{9AC9}\x{9ACA}' .
+'\x{9ACB}\x{9ACC}\x{9ACD}\x{9ACE}\x{9ACF}\x{9AD0}\x{9AD1}\x{9AD2}\x{9AD3}' .
+'\x{9AD4}\x{9AD5}\x{9AD6}\x{9AD8}\x{9AD9}\x{9ADA}\x{9ADB}\x{9ADC}\x{9ADD}' .
+'\x{9ADE}\x{9ADF}\x{9AE1}\x{9AE2}\x{9AE3}\x{9AE5}\x{9AE6}\x{9AE7}\x{9AEA}' .
+'\x{9AEB}\x{9AEC}\x{9AED}\x{9AEE}\x{9AEF}\x{9AF1}\x{9AF2}\x{9AF3}\x{9AF4}' .
+'\x{9AF5}\x{9AF6}\x{9AF7}\x{9AF8}\x{9AF9}\x{9AFA}\x{9AFB}\x{9AFC}\x{9AFD}' .
+'\x{9AFE}\x{9AFF}\x{9B01}\x{9B03}\x{9B04}\x{9B05}\x{9B06}\x{9B07}\x{9B08}' .
+'\x{9B0A}\x{9B0B}\x{9B0C}\x{9B0D}\x{9B0E}\x{9B0F}\x{9B10}\x{9B11}\x{9B12}' .
+'\x{9B13}\x{9B15}\x{9B16}\x{9B17}\x{9B18}\x{9B19}\x{9B1A}\x{9B1C}\x{9B1D}' .
+'\x{9B1E}\x{9B1F}\x{9B20}\x{9B21}\x{9B22}\x{9B23}\x{9B24}\x{9B25}\x{9B26}' .
+'\x{9B27}\x{9B28}\x{9B29}\x{9B2A}\x{9B2B}\x{9B2C}\x{9B2D}\x{9B2E}\x{9B2F}' .
+'\x{9B30}\x{9B31}\x{9B32}\x{9B33}\x{9B35}\x{9B36}\x{9B37}\x{9B38}\x{9B39}' .
+'\x{9B3A}\x{9B3B}\x{9B3C}\x{9B3E}\x{9B3F}\x{9B41}\x{9B42}\x{9B43}\x{9B44}' .
+'\x{9B45}\x{9B46}\x{9B47}\x{9B48}\x{9B49}\x{9B4A}\x{9B4B}\x{9B4C}\x{9B4D}' .
+'\x{9B4E}\x{9B4F}\x{9B51}\x{9B52}\x{9B53}\x{9B54}\x{9B55}\x{9B56}\x{9B58}' .
+'\x{9B59}\x{9B5A}\x{9B5B}\x{9B5C}\x{9B5D}\x{9B5E}\x{9B5F}\x{9B60}\x{9B61}' .
+'\x{9B63}\x{9B64}\x{9B65}\x{9B66}\x{9B67}\x{9B68}\x{9B69}\x{9B6A}\x{9B6B}' .
+'\x{9B6C}\x{9B6D}\x{9B6E}\x{9B6F}\x{9B70}\x{9B71}\x{9B73}\x{9B74}\x{9B75}' .
+'\x{9B76}\x{9B77}\x{9B78}\x{9B79}\x{9B7A}\x{9B7B}\x{9B7C}\x{9B7D}\x{9B7E}' .
+'\x{9B7F}\x{9B80}\x{9B81}\x{9B82}\x{9B83}\x{9B84}\x{9B85}\x{9B86}\x{9B87}' .
+'\x{9B88}\x{9B8A}\x{9B8B}\x{9B8D}\x{9B8E}\x{9B8F}\x{9B90}\x{9B91}\x{9B92}' .
+'\x{9B93}\x{9B94}\x{9B95}\x{9B96}\x{9B97}\x{9B98}\x{9B9A}\x{9B9B}\x{9B9C}' .
+'\x{9B9D}\x{9B9E}\x{9B9F}\x{9BA0}\x{9BA1}\x{9BA2}\x{9BA3}\x{9BA4}\x{9BA5}' .
+'\x{9BA6}\x{9BA7}\x{9BA8}\x{9BA9}\x{9BAA}\x{9BAB}\x{9BAC}\x{9BAD}\x{9BAE}' .
+'\x{9BAF}\x{9BB0}\x{9BB1}\x{9BB2}\x{9BB3}\x{9BB4}\x{9BB5}\x{9BB6}\x{9BB7}' .
+'\x{9BB8}\x{9BB9}\x{9BBA}\x{9BBB}\x{9BBC}\x{9BBD}\x{9BBE}\x{9BBF}\x{9BC0}' .
+'\x{9BC1}\x{9BC3}\x{9BC4}\x{9BC5}\x{9BC6}\x{9BC7}\x{9BC8}\x{9BC9}\x{9BCA}' .
+'\x{9BCB}\x{9BCC}\x{9BCD}\x{9BCE}\x{9BCF}\x{9BD0}\x{9BD1}\x{9BD2}\x{9BD3}' .
+'\x{9BD4}\x{9BD5}\x{9BD6}\x{9BD7}\x{9BD8}\x{9BD9}\x{9BDA}\x{9BDB}\x{9BDC}' .
+'\x{9BDD}\x{9BDE}\x{9BDF}\x{9BE0}\x{9BE1}\x{9BE2}\x{9BE3}\x{9BE4}\x{9BE5}' .
+'\x{9BE6}\x{9BE7}\x{9BE8}\x{9BE9}\x{9BEA}\x{9BEB}\x{9BEC}\x{9BED}\x{9BEE}' .
+'\x{9BEF}\x{9BF0}\x{9BF1}\x{9BF2}\x{9BF3}\x{9BF4}\x{9BF5}\x{9BF7}\x{9BF8}' .
+'\x{9BF9}\x{9BFA}\x{9BFB}\x{9BFC}\x{9BFD}\x{9BFE}\x{9BFF}\x{9C02}\x{9C05}' .
+'\x{9C06}\x{9C07}\x{9C08}\x{9C09}\x{9C0A}\x{9C0B}\x{9C0C}\x{9C0D}\x{9C0E}' .
+'\x{9C0F}\x{9C10}\x{9C11}\x{9C12}\x{9C13}\x{9C14}\x{9C15}\x{9C16}\x{9C17}' .
+'\x{9C18}\x{9C19}\x{9C1A}\x{9C1B}\x{9C1C}\x{9C1D}\x{9C1E}\x{9C1F}\x{9C20}' .
+'\x{9C21}\x{9C22}\x{9C23}\x{9C24}\x{9C25}\x{9C26}\x{9C27}\x{9C28}\x{9C29}' .
+'\x{9C2A}\x{9C2B}\x{9C2C}\x{9C2D}\x{9C2F}\x{9C30}\x{9C31}\x{9C32}\x{9C33}' .
+'\x{9C34}\x{9C35}\x{9C36}\x{9C37}\x{9C38}\x{9C39}\x{9C3A}\x{9C3B}\x{9C3C}' .
+'\x{9C3D}\x{9C3E}\x{9C3F}\x{9C40}\x{9C41}\x{9C43}\x{9C44}\x{9C45}\x{9C46}' .
+'\x{9C47}\x{9C48}\x{9C49}\x{9C4A}\x{9C4B}\x{9C4C}\x{9C4D}\x{9C4E}\x{9C50}' .
+'\x{9C52}\x{9C53}\x{9C54}\x{9C55}\x{9C56}\x{9C57}\x{9C58}\x{9C59}\x{9C5A}' .
+'\x{9C5B}\x{9C5C}\x{9C5D}\x{9C5E}\x{9C5F}\x{9C60}\x{9C62}\x{9C63}\x{9C65}' .
+'\x{9C66}\x{9C67}\x{9C68}\x{9C69}\x{9C6A}\x{9C6B}\x{9C6C}\x{9C6D}\x{9C6E}' .
+'\x{9C6F}\x{9C70}\x{9C71}\x{9C72}\x{9C73}\x{9C74}\x{9C75}\x{9C77}\x{9C78}' .
+'\x{9C79}\x{9C7A}\x{9C7C}\x{9C7D}\x{9C7E}\x{9C7F}\x{9C80}\x{9C81}\x{9C82}' .
+'\x{9C83}\x{9C84}\x{9C85}\x{9C86}\x{9C87}\x{9C88}\x{9C89}\x{9C8A}\x{9C8B}' .
+'\x{9C8C}\x{9C8D}\x{9C8E}\x{9C8F}\x{9C90}\x{9C91}\x{9C92}\x{9C93}\x{9C94}' .
+'\x{9C95}\x{9C96}\x{9C97}\x{9C98}\x{9C99}\x{9C9A}\x{9C9B}\x{9C9C}\x{9C9D}' .
+'\x{9C9E}\x{9C9F}\x{9CA0}\x{9CA1}\x{9CA2}\x{9CA3}\x{9CA4}\x{9CA5}\x{9CA6}' .
+'\x{9CA7}\x{9CA8}\x{9CA9}\x{9CAA}\x{9CAB}\x{9CAC}\x{9CAD}\x{9CAE}\x{9CAF}' .
+'\x{9CB0}\x{9CB1}\x{9CB2}\x{9CB3}\x{9CB4}\x{9CB5}\x{9CB6}\x{9CB7}\x{9CB8}' .
+'\x{9CB9}\x{9CBA}\x{9CBB}\x{9CBC}\x{9CBD}\x{9CBE}\x{9CBF}\x{9CC0}\x{9CC1}' .
+'\x{9CC2}\x{9CC3}\x{9CC4}\x{9CC5}\x{9CC6}\x{9CC7}\x{9CC8}\x{9CC9}\x{9CCA}' .
+'\x{9CCB}\x{9CCC}\x{9CCD}\x{9CCE}\x{9CCF}\x{9CD0}\x{9CD1}\x{9CD2}\x{9CD3}' .
+'\x{9CD4}\x{9CD5}\x{9CD6}\x{9CD7}\x{9CD8}\x{9CD9}\x{9CDA}\x{9CDB}\x{9CDC}' .
+'\x{9CDD}\x{9CDE}\x{9CDF}\x{9CE0}\x{9CE1}\x{9CE2}\x{9CE3}\x{9CE4}\x{9CE5}' .
+'\x{9CE6}\x{9CE7}\x{9CE8}\x{9CE9}\x{9CEA}\x{9CEB}\x{9CEC}\x{9CED}\x{9CEE}' .
+'\x{9CEF}\x{9CF0}\x{9CF1}\x{9CF2}\x{9CF3}\x{9CF4}\x{9CF5}\x{9CF6}\x{9CF7}' .
+'\x{9CF8}\x{9CF9}\x{9CFA}\x{9CFB}\x{9CFC}\x{9CFD}\x{9CFE}\x{9CFF}\x{9D00}' .
+'\x{9D01}\x{9D02}\x{9D03}\x{9D04}\x{9D05}\x{9D06}\x{9D07}\x{9D08}\x{9D09}' .
+'\x{9D0A}\x{9D0B}\x{9D0F}\x{9D10}\x{9D12}\x{9D13}\x{9D14}\x{9D15}\x{9D16}' .
+'\x{9D17}\x{9D18}\x{9D19}\x{9D1A}\x{9D1B}\x{9D1C}\x{9D1D}\x{9D1E}\x{9D1F}' .
+'\x{9D20}\x{9D21}\x{9D22}\x{9D23}\x{9D24}\x{9D25}\x{9D26}\x{9D28}\x{9D29}' .
+'\x{9D2B}\x{9D2D}\x{9D2E}\x{9D2F}\x{9D30}\x{9D31}\x{9D32}\x{9D33}\x{9D34}' .
+'\x{9D36}\x{9D37}\x{9D38}\x{9D39}\x{9D3A}\x{9D3B}\x{9D3D}\x{9D3E}\x{9D3F}' .
+'\x{9D40}\x{9D41}\x{9D42}\x{9D43}\x{9D45}\x{9D46}\x{9D47}\x{9D48}\x{9D49}' .
+'\x{9D4A}\x{9D4B}\x{9D4C}\x{9D4D}\x{9D4E}\x{9D4F}\x{9D50}\x{9D51}\x{9D52}' .
+'\x{9D53}\x{9D54}\x{9D55}\x{9D56}\x{9D57}\x{9D58}\x{9D59}\x{9D5A}\x{9D5B}' .
+'\x{9D5C}\x{9D5D}\x{9D5E}\x{9D5F}\x{9D60}\x{9D61}\x{9D62}\x{9D63}\x{9D64}' .
+'\x{9D65}\x{9D66}\x{9D67}\x{9D68}\x{9D69}\x{9D6A}\x{9D6B}\x{9D6C}\x{9D6E}' .
+'\x{9D6F}\x{9D70}\x{9D71}\x{9D72}\x{9D73}\x{9D74}\x{9D75}\x{9D76}\x{9D77}' .
+'\x{9D78}\x{9D79}\x{9D7A}\x{9D7B}\x{9D7C}\x{9D7D}\x{9D7E}\x{9D7F}\x{9D80}' .
+'\x{9D81}\x{9D82}\x{9D83}\x{9D84}\x{9D85}\x{9D86}\x{9D87}\x{9D88}\x{9D89}' .
+'\x{9D8A}\x{9D8B}\x{9D8C}\x{9D8D}\x{9D8E}\x{9D90}\x{9D91}\x{9D92}\x{9D93}' .
+'\x{9D94}\x{9D96}\x{9D97}\x{9D98}\x{9D99}\x{9D9A}\x{9D9B}\x{9D9C}\x{9D9D}' .
+'\x{9D9E}\x{9D9F}\x{9DA0}\x{9DA1}\x{9DA2}\x{9DA3}\x{9DA4}\x{9DA5}\x{9DA6}' .
+'\x{9DA7}\x{9DA8}\x{9DA9}\x{9DAA}\x{9DAB}\x{9DAC}\x{9DAD}\x{9DAF}\x{9DB0}' .
+'\x{9DB1}\x{9DB2}\x{9DB3}\x{9DB4}\x{9DB5}\x{9DB6}\x{9DB7}\x{9DB8}\x{9DB9}' .
+'\x{9DBA}\x{9DBB}\x{9DBC}\x{9DBE}\x{9DBF}\x{9DC1}\x{9DC2}\x{9DC3}\x{9DC4}' .
+'\x{9DC5}\x{9DC7}\x{9DC8}\x{9DC9}\x{9DCA}\x{9DCB}\x{9DCC}\x{9DCD}\x{9DCE}' .
+'\x{9DCF}\x{9DD0}\x{9DD1}\x{9DD2}\x{9DD3}\x{9DD4}\x{9DD5}\x{9DD6}\x{9DD7}' .
+'\x{9DD8}\x{9DD9}\x{9DDA}\x{9DDB}\x{9DDC}\x{9DDD}\x{9DDE}\x{9DDF}\x{9DE0}' .
+'\x{9DE1}\x{9DE2}\x{9DE3}\x{9DE4}\x{9DE5}\x{9DE6}\x{9DE7}\x{9DE8}\x{9DE9}' .
+'\x{9DEB}\x{9DEC}\x{9DED}\x{9DEE}\x{9DEF}\x{9DF0}\x{9DF1}\x{9DF2}\x{9DF3}' .
+'\x{9DF4}\x{9DF5}\x{9DF6}\x{9DF7}\x{9DF8}\x{9DF9}\x{9DFA}\x{9DFB}\x{9DFD}' .
+'\x{9DFE}\x{9DFF}\x{9E00}\x{9E01}\x{9E02}\x{9E03}\x{9E04}\x{9E05}\x{9E06}' .
+'\x{9E07}\x{9E08}\x{9E09}\x{9E0A}\x{9E0B}\x{9E0C}\x{9E0D}\x{9E0F}\x{9E10}' .
+'\x{9E11}\x{9E12}\x{9E13}\x{9E14}\x{9E15}\x{9E17}\x{9E18}\x{9E19}\x{9E1A}' .
+'\x{9E1B}\x{9E1D}\x{9E1E}\x{9E1F}\x{9E20}\x{9E21}\x{9E22}\x{9E23}\x{9E24}' .
+'\x{9E25}\x{9E26}\x{9E27}\x{9E28}\x{9E29}\x{9E2A}\x{9E2B}\x{9E2C}\x{9E2D}' .
+'\x{9E2E}\x{9E2F}\x{9E30}\x{9E31}\x{9E32}\x{9E33}\x{9E34}\x{9E35}\x{9E36}' .
+'\x{9E37}\x{9E38}\x{9E39}\x{9E3A}\x{9E3B}\x{9E3C}\x{9E3D}\x{9E3E}\x{9E3F}' .
+'\x{9E40}\x{9E41}\x{9E42}\x{9E43}\x{9E44}\x{9E45}\x{9E46}\x{9E47}\x{9E48}' .
+'\x{9E49}\x{9E4A}\x{9E4B}\x{9E4C}\x{9E4D}\x{9E4E}\x{9E4F}\x{9E50}\x{9E51}' .
+'\x{9E52}\x{9E53}\x{9E54}\x{9E55}\x{9E56}\x{9E57}\x{9E58}\x{9E59}\x{9E5A}' .
+'\x{9E5B}\x{9E5C}\x{9E5D}\x{9E5E}\x{9E5F}\x{9E60}\x{9E61}\x{9E62}\x{9E63}' .
+'\x{9E64}\x{9E65}\x{9E66}\x{9E67}\x{9E68}\x{9E69}\x{9E6A}\x{9E6B}\x{9E6C}' .
+'\x{9E6D}\x{9E6E}\x{9E6F}\x{9E70}\x{9E71}\x{9E72}\x{9E73}\x{9E74}\x{9E75}' .
+'\x{9E76}\x{9E77}\x{9E79}\x{9E7A}\x{9E7C}\x{9E7D}\x{9E7E}\x{9E7F}\x{9E80}' .
+'\x{9E81}\x{9E82}\x{9E83}\x{9E84}\x{9E85}\x{9E86}\x{9E87}\x{9E88}\x{9E89}' .
+'\x{9E8A}\x{9E8B}\x{9E8C}\x{9E8D}\x{9E8E}\x{9E91}\x{9E92}\x{9E93}\x{9E94}' .
+'\x{9E96}\x{9E97}\x{9E99}\x{9E9A}\x{9E9B}\x{9E9C}\x{9E9D}\x{9E9F}\x{9EA0}' .
+'\x{9EA1}\x{9EA3}\x{9EA4}\x{9EA5}\x{9EA6}\x{9EA7}\x{9EA8}\x{9EA9}\x{9EAA}' .
+'\x{9EAD}\x{9EAE}\x{9EAF}\x{9EB0}\x{9EB2}\x{9EB3}\x{9EB4}\x{9EB5}\x{9EB6}' .
+'\x{9EB7}\x{9EB8}\x{9EBB}\x{9EBC}\x{9EBD}\x{9EBE}\x{9EBF}\x{9EC0}\x{9EC1}' .
+'\x{9EC2}\x{9EC3}\x{9EC4}\x{9EC5}\x{9EC6}\x{9EC7}\x{9EC8}\x{9EC9}\x{9ECA}' .
+'\x{9ECB}\x{9ECC}\x{9ECD}\x{9ECE}\x{9ECF}\x{9ED0}\x{9ED1}\x{9ED2}\x{9ED3}' .
+'\x{9ED4}\x{9ED5}\x{9ED6}\x{9ED7}\x{9ED8}\x{9ED9}\x{9EDA}\x{9EDB}\x{9EDC}' .
+'\x{9EDD}\x{9EDE}\x{9EDF}\x{9EE0}\x{9EE1}\x{9EE2}\x{9EE3}\x{9EE4}\x{9EE5}' .
+'\x{9EE6}\x{9EE7}\x{9EE8}\x{9EE9}\x{9EEA}\x{9EEB}\x{9EED}\x{9EEE}\x{9EEF}' .
+'\x{9EF0}\x{9EF2}\x{9EF3}\x{9EF4}\x{9EF5}\x{9EF6}\x{9EF7}\x{9EF8}\x{9EF9}' .
+'\x{9EFA}\x{9EFB}\x{9EFC}\x{9EFD}\x{9EFE}\x{9EFF}\x{9F00}\x{9F01}\x{9F02}' .
+'\x{9F04}\x{9F05}\x{9F06}\x{9F07}\x{9F08}\x{9F09}\x{9F0A}\x{9F0B}\x{9F0C}' .
+'\x{9F0D}\x{9F0E}\x{9F0F}\x{9F10}\x{9F12}\x{9F13}\x{9F15}\x{9F16}\x{9F17}' .
+'\x{9F18}\x{9F19}\x{9F1A}\x{9F1B}\x{9F1C}\x{9F1D}\x{9F1E}\x{9F1F}\x{9F20}' .
+'\x{9F22}\x{9F23}\x{9F24}\x{9F25}\x{9F27}\x{9F28}\x{9F29}\x{9F2A}\x{9F2B}' .
+'\x{9F2C}\x{9F2D}\x{9F2E}\x{9F2F}\x{9F30}\x{9F31}\x{9F32}\x{9F33}\x{9F34}' .
+'\x{9F35}\x{9F36}\x{9F37}\x{9F38}\x{9F39}\x{9F3A}\x{9F3B}\x{9F3C}\x{9F3D}' .
+'\x{9F3E}\x{9F3F}\x{9F40}\x{9F41}\x{9F42}\x{9F43}\x{9F44}\x{9F46}\x{9F47}' .
+'\x{9F48}\x{9F49}\x{9F4A}\x{9F4B}\x{9F4C}\x{9F4D}\x{9F4E}\x{9F4F}\x{9F50}' .
+'\x{9F51}\x{9F52}\x{9F54}\x{9F55}\x{9F56}\x{9F57}\x{9F58}\x{9F59}\x{9F5A}' .
+'\x{9F5B}\x{9F5C}\x{9F5D}\x{9F5E}\x{9F5F}\x{9F60}\x{9F61}\x{9F63}\x{9F64}' .
+'\x{9F65}\x{9F66}\x{9F67}\x{9F68}\x{9F69}\x{9F6A}\x{9F6B}\x{9F6C}\x{9F6E}' .
+'\x{9F6F}\x{9F70}\x{9F71}\x{9F72}\x{9F73}\x{9F74}\x{9F75}\x{9F76}\x{9F77}' .
+'\x{9F78}\x{9F79}\x{9F7A}\x{9F7B}\x{9F7C}\x{9F7D}\x{9F7E}\x{9F7F}\x{9F80}' .
+'\x{9F81}\x{9F82}\x{9F83}\x{9F84}\x{9F85}\x{9F86}\x{9F87}\x{9F88}\x{9F89}' .
+'\x{9F8A}\x{9F8B}\x{9F8C}\x{9F8D}\x{9F8E}\x{9F8F}\x{9F90}\x{9F91}\x{9F92}' .
+'\x{9F93}\x{9F94}\x{9F95}\x{9F96}\x{9F97}\x{9F98}\x{9F99}\x{9F9A}\x{9F9B}' .
+'\x{9F9C}\x{9F9D}\x{9F9E}\x{9F9F}\x{9FA0}\x{9FA2}\x{9FA4}\x{9FA5}]{1,20}$/iu');
Index: trunk/library/Mail/Mail/mock.php
===================================================================
--- trunk/library/Mail/Mail/mock.php	(revision 5146)
+++ trunk/library/Mail/Mail/mock.php	(revision 5146)
@@ -0,0 +1,143 @@
+<?php
+/**
+ * Mock implementation
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2010 Chuck Hagenbuch
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category    Mail
+ * @package     Mail
+ * @author      Chuck Hagenbuch <chuck@horde.org> 
+ * @copyright   2010 Chuck Hagenbuch
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id: mock.php 294747 2010-02-08 08:18:33Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ */
+
+/**
+ * Mock implementation of the PEAR Mail:: interface for testing.
+ * @access public
+ * @package Mail
+ * @version $Revision: 294747 $
+ */
+class Mail_mock extends Mail {
+
+    /**
+     * Array of messages that have been sent with the mock.
+     *
+     * @var array
+     * @access public
+     */
+    var $sentMessages = array();
+
+    /**
+     * Callback before sending mail.
+     *
+     * @var callback
+     */
+    var $_preSendCallback;
+
+    /**
+     * Callback after sending mai.
+     *
+     * @var callback
+     */
+    var $_postSendCallback;
+
+    /**
+     * Constructor.
+     *
+     * Instantiates a new Mail_mock:: object based on the parameters
+     * passed in. It looks for the following parameters, both optional:
+     *     preSendCallback   Called before an email would be sent.
+     *     postSendCallback  Called after an email would have been sent.
+     *
+     * @param array Hash containing any parameters.
+     * @access public
+     */
+    function Mail_mock($params)
+    {
+        if (isset($params['preSendCallback']) &&
+            is_callable($params['preSendCallback'])) {
+            $this->_preSendCallback = $params['preSendCallback'];
+        }
+
+        if (isset($params['postSendCallback']) &&
+            is_callable($params['postSendCallback'])) {
+            $this->_postSendCallback = $params['postSendCallback'];
+        }
+    }
+
+    /**
+     * Implements Mail_mock::send() function. Silently discards all
+     * mail.
+     *
+     * @param mixed $recipients Either a comma-seperated list of recipients
+     *              (RFC822 compliant), or an array of recipients,
+     *              each RFC822 valid. This may contain recipients not
+     *              specified in the headers, for Bcc:, resending
+     *              messages, etc.
+     *
+     * @param array $headers The array of headers to send with the mail, in an
+     *              associative array, where the array key is the
+     *              header name (ie, 'Subject'), and the array value
+     *              is the header value (ie, 'test'). The header
+     *              produced from those values would be 'Subject:
+     *              test'.
+     *
+     * @param string $body The full text of the message body, including any
+     *               Mime parts, etc.
+     *
+     * @return mixed Returns true on success, or a PEAR_Error
+     *               containing a descriptive error message on
+     *               failure.
+     * @access public
+     */
+    function send($recipients, $headers, $body)
+    {
+        if ($this->_preSendCallback) {
+            call_user_func_array($this->_preSendCallback,
+                                 array(&$this, $recipients, $headers, $body));
+        }
+
+        $entry = array('recipients' => $recipients, 'headers' => $headers, 'body' => $body);
+        $this->sentMessages[] = $entry;
+
+        if ($this->_postSendCallback) {
+            call_user_func_array($this->_postSendCallback,
+                                 array(&$this, $recipients, $headers, $body));
+        }
+
+        return true;
+    }
+
+}
Index: trunk/library/Mail/Mail/null.php
===================================================================
--- trunk/library/Mail/Mail/null.php	(revision 5146)
+++ trunk/library/Mail/Mail/null.php	(revision 5146)
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Null implementation of the PEAR Mail interface
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2010 Phil Kernick
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category    Mail
+ * @package     Mail
+ * @author      Phil Kernick <philk@rotfl.com.au>
+ * @copyright   2010 Phil Kernick
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id: null.php 294747 2010-02-08 08:18:33Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ */
+
+/**
+ * Null implementation of the PEAR Mail:: interface.
+ * @access public
+ * @package Mail
+ * @version $Revision: 294747 $
+ */
+class Mail_null extends Mail {
+
+    /**
+     * Implements Mail_null::send() function. Silently discards all
+     * mail.
+     *
+     * @param mixed $recipients Either a comma-seperated list of recipients
+     *              (RFC822 compliant), or an array of recipients,
+     *              each RFC822 valid. This may contain recipients not
+     *              specified in the headers, for Bcc:, resending
+     *              messages, etc.
+     *
+     * @param array $headers The array of headers to send with the mail, in an
+     *              associative array, where the array key is the
+     *              header name (ie, 'Subject'), and the array value
+     *              is the header value (ie, 'test'). The header
+     *              produced from those values would be 'Subject:
+     *              test'.
+     *
+     * @param string $body The full text of the message body, including any
+     *               Mime parts, etc.
+     *
+     * @return mixed Returns true on success, or a PEAR_Error
+     *               containing a descriptive error message on
+     *               failure.
+     * @access public
+     */
+    function send($recipients, $headers, $body)
+    {
+        return true;
+    }
+
+}
Index: trunk/library/Mail/Mail/sendmail.php
===================================================================
--- trunk/library/Mail/Mail/sendmail.php	(revision 5146)
+++ trunk/library/Mail/Mail/sendmail.php	(revision 5146)
@@ -0,0 +1,171 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Chuck Hagenbuch <chuck@horde.org>                            |
+// +----------------------------------------------------------------------+
+
+/**
+ * Sendmail implementation of the PEAR Mail:: interface.
+ * @access public
+ * @package Mail
+ * @version $Revision: 294744 $
+ */
+class Mail_sendmail extends Mail {
+
+    /**
+     * The location of the sendmail or sendmail wrapper binary on the
+     * filesystem.
+     * @var string
+     */
+    var $sendmail_path = '/usr/sbin/sendmail';
+
+    /**
+     * Any extra command-line parameters to pass to the sendmail or
+     * sendmail wrapper binary.
+     * @var string
+     */
+    var $sendmail_args = '-i';
+
+    /**
+     * Constructor.
+     *
+     * Instantiates a new Mail_sendmail:: object based on the parameters
+     * passed in. It looks for the following parameters:
+     *     sendmail_path    The location of the sendmail binary on the
+     *                      filesystem. Defaults to '/usr/sbin/sendmail'.
+     *
+     *     sendmail_args    Any extra parameters to pass to the sendmail
+     *                      or sendmail wrapper binary.
+     *
+     * If a parameter is present in the $params array, it replaces the
+     * default.
+     *
+     * @param array $params Hash containing any parameters different from the
+     *              defaults.
+     * @access public
+     */
+    function Mail_sendmail($params)
+    {
+        if (isset($params['sendmail_path'])) {
+            $this->sendmail_path = $params['sendmail_path'];
+        }
+        if (isset($params['sendmail_args'])) {
+            $this->sendmail_args = $params['sendmail_args'];
+        }
+
+        /*
+         * Because we need to pass message headers to the sendmail program on
+         * the commandline, we can't guarantee the use of the standard "\r\n"
+         * separator.  Instead, we use the system's native line separator.
+         */
+        if (defined('PHP_EOL')) {
+            $this->sep = PHP_EOL;
+        } else {
+            $this->sep = (strpos(PHP_OS, 'WIN') === false) ? "\n" : "\r\n";
+        }
+    }
+
+    /**
+     * Implements Mail::send() function using the sendmail
+     * command-line binary.
+     *
+     * @param mixed $recipients Either a comma-seperated list of recipients
+     *              (RFC822 compliant), or an array of recipients,
+     *              each RFC822 valid. This may contain recipients not
+     *              specified in the headers, for Bcc:, resending
+     *              messages, etc.
+     *
+     * @param array $headers The array of headers to send with the mail, in an
+     *              associative array, where the array key is the
+     *              header name (ie, 'Subject'), and the array value
+     *              is the header value (ie, 'test'). The header
+     *              produced from those values would be 'Subject:
+     *              test'.
+     *
+     * @param string $body The full text of the message body, including any
+     *               Mime parts, etc.
+     *
+     * @return mixed Returns true on success, or a PEAR_Error
+     *               containing a descriptive error message on
+     *               failure.
+     * @access public
+     */
+    function send($recipients, $headers, $body)
+    {
+        if (!is_array($headers)) {
+            return PEAR::raiseError('$headers must be an array');
+        }
+
+        $result = $this->_sanitizeHeaders($headers);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $recipients = $this->parseRecipients($recipients);
+        if (is_a($recipients, 'PEAR_Error')) {
+            return $recipients;
+        }
+        $recipients = implode(' ', array_map('escapeshellarg', $recipients));
+
+        $headerElements = $this->prepareHeaders($headers);
+        if (is_a($headerElements, 'PEAR_Error')) {
+            return $headerElements;
+        }
+        list($from, $text_headers) = $headerElements;
+
+        /* Since few MTAs are going to allow this header to be forged
+         * unless it's in the MAIL FROM: exchange, we'll use
+         * Return-Path instead of From: if it's set. */
+        if (!empty($headers['Return-Path'])) {
+            $from = $headers['Return-Path'];
+        }
+
+        if (!isset($from)) {
+            return PEAR::raiseError('No from address given.');
+        } elseif (strpos($from, ' ') !== false ||
+                  strpos($from, ';') !== false ||
+                  strpos($from, '&') !== false ||
+                  strpos($from, '`') !== false) {
+            return PEAR::raiseError('From address specified with dangerous characters.');
+        }
+
+        $from = escapeshellarg($from); // Security bug #16200
+
+        $mail = @popen($this->sendmail_path . (!empty($this->sendmail_args) ? ' ' . $this->sendmail_args : '') . " -f$from -- $recipients", 'w');
+        if (!$mail) {
+            return PEAR::raiseError('Failed to open sendmail [' . $this->sendmail_path . '] for execution.');
+        }
+
+        // Write the headers following by two newlines: one to end the headers
+        // section and a second to separate the headers block from the body.
+        fputs($mail, $text_headers . $this->sep . $this->sep);
+
+        fputs($mail, $body);
+        $result = pclose($mail);
+        if (version_compare(phpversion(), '4.2.3') == -1) {
+            // With older php versions, we need to shift the pclose
+            // result to get the exit code.
+            $result = $result >> 8 & 0xFF;
+        }
+
+        if ($result != 0) {
+            return PEAR::raiseError('sendmail returned error code ' . $result,
+                                    $result);
+        }
+
+        return true;
+    }
+
+}
Index: trunk/library/Mail/Mail/mail.php
===================================================================
--- trunk/library/Mail/Mail/mail.php	(revision 5146)
+++ trunk/library/Mail/Mail/mail.php	(revision 5146)
@@ -0,0 +1,168 @@
+<?php
+/**
+ * internal PHP-mail() implementation of the PEAR Mail:: interface.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2010 Chuck Hagenbuch
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category    Mail
+ * @package     Mail
+ * @author      Chuck Hagenbuch <chuck@horde.org> 
+ * @copyright   2010 Chuck Hagenbuch
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id: mail.php 294747 2010-02-08 08:18:33Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ */
+
+/**
+ * internal PHP-mail() implementation of the PEAR Mail:: interface.
+ * @package Mail
+ * @version $Revision: 294747 $
+ */
+class Mail_mail extends Mail {
+
+    /**
+     * Any arguments to pass to the mail() function.
+     * @var string
+     */
+    var $_params = '';
+
+    /**
+     * Constructor.
+     *
+     * Instantiates a new Mail_mail:: object based on the parameters
+     * passed in.
+     *
+     * @param array $params Extra arguments for the mail() function.
+     */
+    function Mail_mail($params = null)
+    {
+        // The other mail implementations accept parameters as arrays.
+        // In the interest of being consistent, explode an array into
+        // a string of parameter arguments.
+        if (is_array($params)) {
+            $this->_params = join(' ', $params);
+        } else {
+            $this->_params = $params;
+        }
+
+        /* Because the mail() function may pass headers as command
+         * line arguments, we can't guarantee the use of the standard
+         * "\r\n" separator.  Instead, we use the system's native line
+         * separator. */
+        if (defined('PHP_EOL')) {
+            $this->sep = PHP_EOL;
+        } else {
+            $this->sep = (strpos(PHP_OS, 'WIN') === false) ? "\n" : "\r\n";
+        }
+    }
+
+    /**
+     * Implements Mail_mail::send() function using php's built-in mail()
+     * command.
+     *
+     * @param mixed $recipients Either a comma-seperated list of recipients
+     *              (RFC822 compliant), or an array of recipients,
+     *              each RFC822 valid. This may contain recipients not
+     *              specified in the headers, for Bcc:, resending
+     *              messages, etc.
+     *
+     * @param array $headers The array of headers to send with the mail, in an
+     *              associative array, where the array key is the
+     *              header name (ie, 'Subject'), and the array value
+     *              is the header value (ie, 'test'). The header
+     *              produced from those values would be 'Subject:
+     *              test'.
+     *
+     * @param string $body The full text of the message body, including any
+     *               Mime parts, etc.
+     *
+     * @return mixed Returns true on success, or a PEAR_Error
+     *               containing a descriptive error message on
+     *               failure.
+     *
+     * @access public
+     */
+    function send($recipients, $headers, $body)
+    {
+        if (!is_array($headers)) {
+            return PEAR::raiseError('$headers must be an array');
+        }
+
+        $result = $this->_sanitizeHeaders($headers);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        // If we're passed an array of recipients, implode it.
+        if (is_array($recipients)) {
+            $recipients = implode(', ', $recipients);
+        }
+
+        // Get the Subject out of the headers array so that we can
+        // pass it as a seperate argument to mail().
+        $subject = '';
+        if (isset($headers['Subject'])) {
+            $subject = $headers['Subject'];
+            unset($headers['Subject']);
+        }
+
+        // Also remove the To: header.  The mail() function will add its own
+        // To: header based on the contents of $recipients.
+        unset($headers['To']);
+
+        // Flatten the headers out.
+        $headerElements = $this->prepareHeaders($headers);
+        if (is_a($headerElements, 'PEAR_Error')) {
+            return $headerElements;
+        }
+        list(, $text_headers) = $headerElements;
+
+        // We only use mail()'s optional fifth parameter if the additional
+        // parameters have been provided and we're not running in safe mode.
+        if (empty($this->_params) || ini_get('safe_mode')) {
+            $result = mail($recipients, $subject, $body, $text_headers);
+        } else {
+            $result = mail($recipients, $subject, $body, $text_headers,
+                           $this->_params);
+        }
+
+        // If the mail() function returned failure, we need to create a
+        // PEAR_Error object and return it instead of the boolean result.
+        if ($result === false) {
+            $result = PEAR::raiseError('mail() returned failure');
+        }
+
+        return $result;
+    }
+
+}
Index: trunk/library/Mail/Mail/smtp.php
===================================================================
--- trunk/library/Mail/Mail/smtp.php	(revision 5146)
+++ trunk/library/Mail/Mail/smtp.php	(revision 5146)
@@ -0,0 +1,444 @@
+<?php
+/**
+ * SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2010, Chuck Hagenbuch
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category    HTTP
+ * @package     HTTP_Request
+ * @author      Jon Parise <jon@php.net> 
+ * @author      Chuck Hagenbuch <chuck@horde.org>
+ * @copyright   2010 Chuck Hagenbuch
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id: smtp.php 294747 2010-02-08 08:18:33Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ */
+
+/** Error: Failed to create a Net_SMTP object */
+define('PEAR_MAIL_SMTP_ERROR_CREATE', 10000);
+
+/** Error: Failed to connect to SMTP server */
+define('PEAR_MAIL_SMTP_ERROR_CONNECT', 10001);
+
+/** Error: SMTP authentication failure */
+define('PEAR_MAIL_SMTP_ERROR_AUTH', 10002);
+
+/** Error: No From: address has been provided */
+define('PEAR_MAIL_SMTP_ERROR_FROM', 10003);
+
+/** Error: Failed to set sender */
+define('PEAR_MAIL_SMTP_ERROR_SENDER', 10004);
+
+/** Error: Failed to add recipient */
+define('PEAR_MAIL_SMTP_ERROR_RECIPIENT', 10005);
+
+/** Error: Failed to send data */
+define('PEAR_MAIL_SMTP_ERROR_DATA', 10006);
+
+/**
+ * SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class.
+ * @access public
+ * @package Mail
+ * @version $Revision: 294747 $
+ */
+class Mail_smtp extends Mail {
+
+    /**
+     * SMTP connection object.
+     *
+     * @var object
+     * @access private
+     */
+    var $_smtp = null;
+
+    /**
+     * The list of service extension parameters to pass to the Net_SMTP
+     * mailFrom() command.
+     * @var array
+     */
+    var $_extparams = array();
+
+    /**
+     * The SMTP host to connect to.
+     * @var string
+     */
+    var $host = 'localhost';
+
+    /**
+     * The port the SMTP server is on.
+     * @var integer
+     */
+    var $port = 25;
+
+    /**
+     * Should SMTP authentication be used?
+     *
+     * This value may be set to true, false or the name of a specific
+     * authentication method.
+     *
+     * If the value is set to true, the Net_SMTP package will attempt to use
+     * the best authentication method advertised by the remote SMTP server.
+     *
+     * @var mixed
+     */
+    var $auth = false;
+
+    /**
+     * The username to use if the SMTP server requires authentication.
+     * @var string
+     */
+    var $username = '';
+
+    /**
+     * The password to use if the SMTP server requires authentication.
+     * @var string
+     */
+    var $password = '';
+
+    /**
+     * Hostname or domain that will be sent to the remote SMTP server in the
+     * HELO / EHLO message.
+     *
+     * @var string
+     */
+    var $localhost = 'localhost';
+
+    /**
+     * SMTP connection timeout value.  NULL indicates no timeout.
+     *
+     * @var integer
+     */
+    var $timeout = null;
+
+    /**
+     * Turn on Net_SMTP debugging?
+     *
+     * @var boolean $debug
+     */
+    var $debug = false;
+
+    /**
+     * Indicates whether or not the SMTP connection should persist over
+     * multiple calls to the send() method.
+     *
+     * @var boolean
+     */
+    var $persist = false;
+
+    /**
+     * Use SMTP command pipelining (specified in RFC 2920) if the SMTP server
+     * supports it. This speeds up delivery over high-latency connections. By
+     * default, use the default value supplied by Net_SMTP.
+     * @var bool
+     */
+    var $pipelining;
+
+    /**
+     * Constructor.
+     *
+     * Instantiates a new Mail_smtp:: object based on the parameters
+     * passed in. It looks for the following parameters:
+     *     host        The server to connect to. Defaults to localhost.
+     *     port        The port to connect to. Defaults to 25.
+     *     auth        SMTP authentication.  Defaults to none.
+     *     username    The username to use for SMTP auth. No default.
+     *     password    The password to use for SMTP auth. No default.
+     *     localhost   The local hostname / domain. Defaults to localhost.
+     *     timeout     The SMTP connection timeout. Defaults to none.
+     *     verp        Whether to use VERP or not. Defaults to false.
+     *                 DEPRECATED as of 1.2.0 (use setMailParams()).
+     *     debug       Activate SMTP debug mode? Defaults to false.
+     *     persist     Should the SMTP connection persist?
+     *     pipelining  Use SMTP command pipelining
+     *
+     * If a parameter is present in the $params array, it replaces the
+     * default.
+     *
+     * @param array Hash containing any parameters different from the
+     *              defaults.
+     * @access public
+     */
+    function Mail_smtp($params)
+    {
+        if (isset($params['host'])) $this->host = $params['host'];
+        if (isset($params['port'])) $this->port = $params['port'];
+        if (isset($params['auth'])) $this->auth = $params['auth'];
+        if (isset($params['username'])) $this->username = $params['username'];
+        if (isset($params['password'])) $this->password = $params['password'];
+        if (isset($params['localhost'])) $this->localhost = $params['localhost'];
+        if (isset($params['timeout'])) $this->timeout = $params['timeout'];
+        if (isset($params['debug'])) $this->debug = (bool)$params['debug'];
+        if (isset($params['persist'])) $this->persist = (bool)$params['persist'];
+        if (isset($params['pipelining'])) $this->pipelining = (bool)$params['pipelining'];
+
+        // Deprecated options
+        if (isset($params['verp'])) {
+            $this->addServiceExtensionParameter('XVERP', is_bool($params['verp']) ? null : $params['verp']);
+        }
+
+        register_shutdown_function(array(&$this, '_Mail_smtp'));
+    }
+
+    /**
+     * Destructor implementation to ensure that we disconnect from any
+     * potentially-alive persistent SMTP connections.
+     */
+    function _Mail_smtp()
+    {
+        $this->disconnect();
+    }
+
+    /**
+     * Implements Mail::send() function using SMTP.
+     *
+     * @param mixed $recipients Either a comma-seperated list of recipients
+     *              (RFC822 compliant), or an array of recipients,
+     *              each RFC822 valid. This may contain recipients not
+     *              specified in the headers, for Bcc:, resending
+     *              messages, etc.
+     *
+     * @param array $headers The array of headers to send with the mail, in an
+     *              associative array, where the array key is the
+     *              header name (e.g., 'Subject'), and the array value
+     *              is the header value (e.g., 'test'). The header
+     *              produced from those values would be 'Subject:
+     *              test'.
+     *
+     * @param string $body The full text of the message body, including any
+     *               MIME parts, etc.
+     *
+     * @return mixed Returns true on success, or a PEAR_Error
+     *               containing a descriptive error message on
+     *               failure.
+     * @access public
+     */
+    function send($recipients, $headers, $body)
+    {
+        /* If we don't already have an SMTP object, create one. */
+        $result = &$this->getSMTPObject();
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        if (!is_array($headers)) {
+            return PEAR::raiseError('$headers must be an array');
+        }
+
+        $this->_sanitizeHeaders($headers);
+
+        $headerElements = $this->prepareHeaders($headers);
+        if (is_a($headerElements, 'PEAR_Error')) {
+            $this->_smtp->rset();
+            return $headerElements;
+        }
+        list($from, $textHeaders) = $headerElements;
+
+        /* Since few MTAs are going to allow this header to be forged
+         * unless it's in the MAIL FROM: exchange, we'll use
+         * Return-Path instead of From: if it's set. */
+        if (!empty($headers['Return-Path'])) {
+            $from = $headers['Return-Path'];
+        }
+
+        if (!isset($from)) {
+            $this->_smtp->rset();
+            return PEAR::raiseError('No From: address has been provided',
+                                    PEAR_MAIL_SMTP_ERROR_FROM);
+        }
+
+        $params = null;
+        if (!empty($this->_extparams)) {
+            foreach ($this->_extparams as $key => $val) {
+                $params .= ' ' . $key . (is_null($val) ? '' : '=' . $val);
+            }
+        }
+        if (PEAR::isError($res = $this->_smtp->mailFrom($from, ltrim($params)))) {
+            $error = $this->_error("Failed to set sender: $from", $res);
+            $this->_smtp->rset();
+            return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_SENDER);
+        }
+
+        $recipients = $this->parseRecipients($recipients);
+        if (is_a($recipients, 'PEAR_Error')) {
+            $this->_smtp->rset();
+            return $recipients;
+        }
+
+        foreach ($recipients as $recipient) {
+            $res = $this->_smtp->rcptTo($recipient);
+            if (is_a($res, 'PEAR_Error')) {
+                $error = $this->_error("Failed to add recipient: $recipient", $res);
+                $this->_smtp->rset();
+                return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_RECIPIENT);
+            }
+        }
+
+        /* Send the message's headers and the body as SMTP data. */
+        $res = $this->_smtp->data($textHeaders . "\r\n\r\n" . $body);
+		list(,$args) = $this->_smtp->getResponse();
+
+		if (preg_match("/Ok: queued as (.*)/", $args, $queued)) {
+			$this->queued_as = $queued[1];
+		}
+
+		/* we need the greeting; from it we can extract the authorative name of the mail server we've really connected to.
+		 * ideal if we're connecting to a round-robin of relay servers and need to track which exact one took the email */
+		$this->greeting = $this->_smtp->getGreeting();
+
+        if (is_a($res, 'PEAR_Error')) {
+            $error = $this->_error('Failed to send data', $res);
+            $this->_smtp->rset();
+            return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_DATA);
+        }
+
+        /* If persistent connections are disabled, destroy our SMTP object. */
+        if ($this->persist === false) {
+            $this->disconnect();
+        }
+
+        return true;
+    }
+
+    /**
+     * Connect to the SMTP server by instantiating a Net_SMTP object.
+     *
+     * @return mixed Returns a reference to the Net_SMTP object on success, or
+     *               a PEAR_Error containing a descriptive error message on
+     *               failure.
+     *
+     * @since  1.2.0
+     * @access public
+     */
+    function &getSMTPObject()
+    {
+        if (is_object($this->_smtp) !== false) {
+            return $this->_smtp;
+        }
+
+        include_once $_SESSION['rootPath'].'/library/Net/SMTP.php';
+        $this->_smtp = &new Net_SMTP($this->host,
+                                     $this->port,
+                                     $this->localhost);
+
+        /* If we still don't have an SMTP object at this point, fail. */
+        if (is_object($this->_smtp) === false) {
+            return PEAR::raiseError('Failed to create a Net_SMTP object',
+                                    PEAR_MAIL_SMTP_ERROR_CREATE);
+        }
+
+        /* Configure the SMTP connection. */
+        if ($this->debug) {
+            $this->_smtp->setDebug(true);
+        }
+
+        /* Attempt to connect to the configured SMTP server. */
+        if (PEAR::isError($res = $this->_smtp->connect($this->timeout))) {
+            $error = $this->_error('Failed to connect to ' .
+                                   $this->host . ':' . $this->port,
+                                   $res);
+            return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_CONNECT);
+        }
+
+        /* Attempt to authenticate if authentication has been enabled. */
+        if ($this->auth) {
+            $method = is_string($this->auth) ? $this->auth : '';
+
+            if (PEAR::isError($res = $this->_smtp->auth($this->username,
+                                                        $this->password,
+                                                        $method))) {
+                $error = $this->_error("$method authentication failure",
+                                       $res);
+                $this->_smtp->rset();
+                return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_AUTH);
+            }
+        }
+
+        return $this->_smtp;
+    }
+
+    /**
+     * Add parameter associated with a SMTP service extension.
+     *
+     * @param string Extension keyword.
+     * @param string Any value the keyword needs.
+     *
+     * @since 1.2.0
+     * @access public
+     */
+    function addServiceExtensionParameter($keyword, $value = null)
+    {
+        $this->_extparams[$keyword] = $value;
+    }
+
+    /**
+     * Disconnect and destroy the current SMTP connection.
+     *
+     * @return boolean True if the SMTP connection no longer exists.
+     *
+     * @since  1.1.9
+     * @access public
+     */
+    function disconnect()
+    {
+        /* If we have an SMTP object, disconnect and destroy it. */
+        if (is_object($this->_smtp) && $this->_smtp->disconnect()) {
+            $this->_smtp = null;
+        }
+
+        /* We are disconnected if we no longer have an SMTP object. */
+        return ($this->_smtp === null);
+    }
+
+    /**
+     * Build a standardized string describing the current SMTP error.
+     *
+     * @param string $text  Custom string describing the error context.
+     * @param object $error Reference to the current PEAR_Error object.
+     *
+     * @return string       A string describing the current SMTP error.
+     *
+     * @since  1.1.7
+     * @access private
+     */
+    function _error($text, &$error)
+    {
+        /* Split the SMTP response into a code and a response string. */
+        list($code, $response) = $this->_smtp->getResponse();
+
+        /* Build our standardized error string. */
+        return $text
+            . ' [SMTP: ' . $error->getMessage()
+            . " (code: $code, response: $response)]";
+    }
+
+}
Index: trunk/library/Mail/Mail/RFC822.php
===================================================================
--- trunk/library/Mail/Mail/RFC822.php	(revision 5146)
+++ trunk/library/Mail/Mail/RFC822.php	(revision 5146)
@@ -0,0 +1,951 @@
+<?php
+/**
+ * RFC 822 Email address list validation Utility
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2001-2010, Richard Heyes
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category    Mail
+ * @package     Mail
+ * @author      Richard Heyes <richard@phpguru.org>
+ * @author      Chuck Hagenbuch <chuck@horde.org
+ * @copyright   2001-2010 Richard Heyes
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id: RFC822.php 294749 2010-02-08 08:22:25Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ */
+
+/**
+ * RFC 822 Email address list validation Utility
+ *
+ * What is it?
+ *
+ * This class will take an address string, and parse it into it's consituent
+ * parts, be that either addresses, groups, or combinations. Nested groups
+ * are not supported. The structure it returns is pretty straight forward,
+ * and is similar to that provided by the imap_rfc822_parse_adrlist(). Use
+ * print_r() to view the structure.
+ *
+ * How do I use it?
+ *
+ * $address_string = 'My Group: "Richard" <richard@localhost> (A comment), ted@example.com (Ted Bloggs), Barney;';
+ * $structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true)
+ * print_r($structure);
+ *
+ * @author  Richard Heyes <richard@phpguru.org>
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @version $Revision: 294749 $
+ * @license BSD
+ * @package Mail
+ */
+class Mail_RFC822 {
+
+    /**
+     * The address being parsed by the RFC822 object.
+     * @var string $address
+     */
+    var $address = '';
+
+    /**
+     * The default domain to use for unqualified addresses.
+     * @var string $default_domain
+     */
+    var $default_domain = 'localhost';
+
+    /**
+     * Should we return a nested array showing groups, or flatten everything?
+     * @var boolean $nestGroups
+     */
+    var $nestGroups = true;
+
+    /**
+     * Whether or not to validate atoms for non-ascii characters.
+     * @var boolean $validate
+     */
+    var $validate = true;
+
+    /**
+     * The array of raw addresses built up as we parse.
+     * @var array $addresses
+     */
+    var $addresses = array();
+
+    /**
+     * The final array of parsed address information that we build up.
+     * @var array $structure
+     */
+    var $structure = array();
+
+    /**
+     * The current error message, if any.
+     * @var string $error
+     */
+    var $error = null;
+
+    /**
+     * An internal counter/pointer.
+     * @var integer $index
+     */
+    var $index = null;
+
+    /**
+     * The number of groups that have been found in the address list.
+     * @var integer $num_groups
+     * @access public
+     */
+    var $num_groups = 0;
+
+    /**
+     * A variable so that we can tell whether or not we're inside a
+     * Mail_RFC822 object.
+     * @var boolean $mailRFC822
+     */
+    var $mailRFC822 = true;
+
+    /**
+    * A limit after which processing stops
+    * @var int $limit
+    */
+    var $limit = null;
+
+    /**
+     * Sets up the object. The address must either be set here or when
+     * calling parseAddressList(). One or the other.
+     *
+     * @access public
+     * @param string  $address         The address(es) to validate.
+     * @param string  $default_domain  Default domain/host etc. If not supplied, will be set to localhost.
+     * @param boolean $nest_groups     Whether to return the structure with groups nested for easier viewing.
+     * @param boolean $validate        Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
+     *
+     * @return object Mail_RFC822 A new Mail_RFC822 object.
+     */
+    function Mail_RFC822($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
+    {
+        if (isset($address))        $this->address        = $address;
+        if (isset($default_domain)) $this->default_domain = $default_domain;
+        if (isset($nest_groups))    $this->nestGroups     = $nest_groups;
+        if (isset($validate))       $this->validate       = $validate;
+        if (isset($limit))          $this->limit          = $limit;
+    }
+
+    /**
+     * Starts the whole process. The address must either be set here
+     * or when creating the object. One or the other.
+     *
+     * @access public
+     * @param string  $address         The address(es) to validate.
+     * @param string  $default_domain  Default domain/host etc.
+     * @param boolean $nest_groups     Whether to return the structure with groups nested for easier viewing.
+     * @param boolean $validate        Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
+     *
+     * @return array A structured array of addresses.
+     */
+    function parseAddressList($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
+    {
+        if (!isset($this) || !isset($this->mailRFC822)) {
+            $obj = new Mail_RFC822($address, $default_domain, $nest_groups, $validate, $limit);
+            return $obj->parseAddressList();
+        }
+
+        if (isset($address))        $this->address        = $address;
+        if (isset($default_domain)) $this->default_domain = $default_domain;
+        if (isset($nest_groups))    $this->nestGroups     = $nest_groups;
+        if (isset($validate))       $this->validate       = $validate;
+        if (isset($limit))          $this->limit          = $limit;
+
+        $this->structure  = array();
+        $this->addresses  = array();
+        $this->error      = null;
+        $this->index      = null;
+
+        // Unfold any long lines in $this->address.
+        $this->address = preg_replace('/\r?\n/', "\r\n", $this->address);
+        $this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address);
+
+        while ($this->address = $this->_splitAddresses($this->address));
+
+        if ($this->address === false || isset($this->error)) {
+            require_once 'PEAR.php';
+            return PEAR::raiseError($this->error);
+        }
+
+        // Validate each address individually.  If we encounter an invalid
+        // address, stop iterating and return an error immediately.
+        foreach ($this->addresses as $address) {
+            $valid = $this->_validateAddress($address);
+
+            if ($valid === false || isset($this->error)) {
+                require_once 'PEAR.php';
+                return PEAR::raiseError($this->error);
+            }
+
+            if (!$this->nestGroups) {
+                $this->structure = array_merge($this->structure, $valid);
+            } else {
+                $this->structure[] = $valid;
+            }
+        }
+
+        return $this->structure;
+    }
+
+    /**
+     * Splits an address into separate addresses.
+     *
+     * @access private
+     * @param string $address The addresses to split.
+     * @return boolean Success or failure.
+     */
+    function _splitAddresses($address)
+    {
+        if (!empty($this->limit) && count($this->addresses) == $this->limit) {
+            return '';
+        }
+
+        if ($this->_isGroup($address) && !isset($this->error)) {
+            $split_char = ';';
+            $is_group   = true;
+        } elseif (!isset($this->error)) {
+            $split_char = ',';
+            $is_group   = false;
+        } elseif (isset($this->error)) {
+            return false;
+        }
+
+        // Split the string based on the above ten or so lines.
+        $parts  = explode($split_char, $address);
+        $string = $this->_splitCheck($parts, $split_char);
+
+        // If a group...
+        if ($is_group) {
+            // If $string does not contain a colon outside of
+            // brackets/quotes etc then something's fubar.
+
+            // First check there's a colon at all:
+            if (strpos($string, ':') === false) {
+                $this->error = 'Invalid address: ' . $string;
+                return false;
+            }
+
+            // Now check it's outside of brackets/quotes:
+            if (!$this->_splitCheck(explode(':', $string), ':')) {
+                return false;
+            }
+
+            // We must have a group at this point, so increase the counter:
+            $this->num_groups++;
+        }
+
+        // $string now contains the first full address/group.
+        // Add to the addresses array.
+        $this->addresses[] = array(
+                                   'address' => trim($string),
+                                   'group'   => $is_group
+                                   );
+
+        // Remove the now stored address from the initial line, the +1
+        // is to account for the explode character.
+        $address = trim(substr($address, strlen($string) + 1));
+
+        // If the next char is a comma and this was a group, then
+        // there are more addresses, otherwise, if there are any more
+        // chars, then there is another address.
+        if ($is_group && substr($address, 0, 1) == ','){
+            $address = trim(substr($address, 1));
+            return $address;
+
+        } elseif (strlen($address) > 0) {
+            return $address;
+
+        } else {
+            return '';
+        }
+
+        // If you got here then something's off
+        return false;
+    }
+
+    /**
+     * Checks for a group at the start of the string.
+     *
+     * @access private
+     * @param string $address The address to check.
+     * @return boolean Whether or not there is a group at the start of the string.
+     */
+    function _isGroup($address)
+    {
+        // First comma not in quotes, angles or escaped:
+        $parts  = explode(',', $address);
+        $string = $this->_splitCheck($parts, ',');
+
+        // Now we have the first address, we can reliably check for a
+        // group by searching for a colon that's not escaped or in
+        // quotes or angle brackets.
+        if (count($parts = explode(':', $string)) > 1) {
+            $string2 = $this->_splitCheck($parts, ':');
+            return ($string2 !== $string);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * A common function that will check an exploded string.
+     *
+     * @access private
+     * @param array $parts The exloded string.
+     * @param string $char  The char that was exploded on.
+     * @return mixed False if the string contains unclosed quotes/brackets, or the string on success.
+     */
+    function _splitCheck($parts, $char)
+    {
+        $string = $parts[0];
+
+        for ($i = 0; $i < count($parts); $i++) {
+            if ($this->_hasUnclosedQuotes($string)
+                || $this->_hasUnclosedBrackets($string, '<>')
+                || $this->_hasUnclosedBrackets($string, '[]')
+                || $this->_hasUnclosedBrackets($string, '()')
+                || substr($string, -1) == '\\') {
+                if (isset($parts[$i + 1])) {
+                    $string = $string . $char . $parts[$i + 1];
+                } else {
+                    $this->error = 'Invalid address spec. Unclosed bracket or quotes';
+                    return false;
+                }
+            } else {
+                $this->index = $i;
+                break;
+            }
+        }
+
+        return $string;
+    }
+
+    /**
+     * Checks if a string has unclosed quotes or not.
+     *
+     * @access private
+     * @param string $string  The string to check.
+     * @return boolean  True if there are unclosed quotes inside the string,
+     *                  false otherwise.
+     */
+    function _hasUnclosedQuotes($string)
+    {
+        $string = trim($string);
+        $iMax = strlen($string);
+        $in_quote = false;
+        $i = $slashes = 0;
+
+        for (; $i < $iMax; ++$i) {
+            switch ($string[$i]) {
+            case '\\':
+                ++$slashes;
+                break;
+
+            case '"':
+                if ($slashes % 2 == 0) {
+                    $in_quote = !$in_quote;
+                }
+                // Fall through to default action below.
+
+            default:
+                $slashes = 0;
+                break;
+            }
+        }
+
+        return $in_quote;
+    }
+
+    /**
+     * Checks if a string has an unclosed brackets or not. IMPORTANT:
+     * This function handles both angle brackets and square brackets;
+     *
+     * @access private
+     * @param string $string The string to check.
+     * @param string $chars  The characters to check for.
+     * @return boolean True if there are unclosed brackets inside the string, false otherwise.
+     */
+    function _hasUnclosedBrackets($string, $chars)
+    {
+        $num_angle_start = substr_count($string, $chars[0]);
+        $num_angle_end   = substr_count($string, $chars[1]);
+
+        $this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]);
+        $this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]);
+
+        if ($num_angle_start < $num_angle_end) {
+            $this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')';
+            return false;
+        } else {
+            return ($num_angle_start > $num_angle_end);
+        }
+    }
+
+    /**
+     * Sub function that is used only by hasUnclosedBrackets().
+     *
+     * @access private
+     * @param string $string The string to check.
+     * @param integer &$num    The number of occurences.
+     * @param string $char   The character to count.
+     * @return integer The number of occurences of $char in $string, adjusted for backslashes.
+     */
+    function _hasUnclosedBracketsSub($string, &$num, $char)
+    {
+        $parts = explode($char, $string);
+        for ($i = 0; $i < count($parts); $i++){
+            if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i]))
+                $num--;
+            if (isset($parts[$i + 1]))
+                $parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1];
+        }
+
+        return $num;
+    }
+
+    /**
+     * Function to begin checking the address.
+     *
+     * @access private
+     * @param string $address The address to validate.
+     * @return mixed False on failure, or a structured array of address information on success.
+     */
+    function _validateAddress($address)
+    {
+        $is_group = false;
+        $addresses = array();
+
+        if ($address['group']) {
+            $is_group = true;
+
+            // Get the group part of the name
+            $parts     = explode(':', $address['address']);
+            $groupname = $this->_splitCheck($parts, ':');
+            $structure = array();
+
+            // And validate the group part of the name.
+            if (!$this->_validatePhrase($groupname)){
+                $this->error = 'Group name did not validate.';
+                return false;
+            } else {
+                // Don't include groups if we are not nesting
+                // them. This avoids returning invalid addresses.
+                if ($this->nestGroups) {
+                    $structure = new stdClass;
+                    $structure->groupname = $groupname;
+                }
+            }
+
+            $address['address'] = ltrim(substr($address['address'], strlen($groupname . ':')));
+        }
+
+        // If a group then split on comma and put into an array.
+        // Otherwise, Just put the whole address in an array.
+        if ($is_group) {
+            while (strlen($address['address']) > 0) {
+                $parts       = explode(',', $address['address']);
+                $addresses[] = $this->_splitCheck($parts, ',');
+                $address['address'] = trim(substr($address['address'], strlen(end($addresses) . ',')));
+            }
+        } else {
+            $addresses[] = $address['address'];
+        }
+
+        // Check that $addresses is set, if address like this:
+        // Groupname:;
+        // Then errors were appearing.
+        if (!count($addresses)){
+            $this->error = 'Empty group.';
+            return false;
+        }
+
+        // Trim the whitespace from all of the address strings.
+        array_map('trim', $addresses);
+
+        // Validate each mailbox.
+        // Format could be one of: name <geezer@domain.com>
+        //                         geezer@domain.com
+        //                         geezer
+        // ... or any other format valid by RFC 822.
+        for ($i = 0; $i < count($addresses); $i++) {
+            if (!$this->validateMailbox($addresses[$i])) {
+                if (empty($this->error)) {
+                    $this->error = 'Validation failed for: ' . $addresses[$i];
+                }
+                return false;
+            }
+        }
+
+        // Nested format
+        if ($this->nestGroups) {
+            if ($is_group) {
+                $structure->addresses = $addresses;
+            } else {
+                $structure = $addresses[0];
+            }
+
+        // Flat format
+        } else {
+            if ($is_group) {
+                $structure = array_merge($structure, $addresses);
+            } else {
+                $structure = $addresses;
+            }
+        }
+
+        return $structure;
+    }
+
+    /**
+     * Function to validate a phrase.
+     *
+     * @access private
+     * @param string $phrase The phrase to check.
+     * @return boolean Success or failure.
+     */
+    function _validatePhrase($phrase)
+    {
+        // Splits on one or more Tab or space.
+        $parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY);
+
+        $phrase_parts = array();
+        while (count($parts) > 0){
+            $phrase_parts[] = $this->_splitCheck($parts, ' ');
+            for ($i = 0; $i < $this->index + 1; $i++)
+                array_shift($parts);
+        }
+
+        foreach ($phrase_parts as $part) {
+            // If quoted string:
+            if (substr($part, 0, 1) == '"') {
+                if (!$this->_validateQuotedString($part)) {
+                    return false;
+                }
+                continue;
+            }
+
+            // Otherwise it's an atom:
+            if (!$this->_validateAtom($part)) return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Function to validate an atom which from rfc822 is:
+     * atom = 1*<any CHAR except specials, SPACE and CTLs>
+     *
+     * If validation ($this->validate) has been turned off, then
+     * validateAtom() doesn't actually check anything. This is so that you
+     * can split a list of addresses up before encoding personal names
+     * (umlauts, etc.), for example.
+     *
+     * @access private
+     * @param string $atom The string to check.
+     * @return boolean Success or failure.
+     */
+    function _validateAtom($atom)
+    {
+        if (!$this->validate) {
+            // Validation has been turned off; assume the atom is okay.
+            return true;
+        }
+
+        // Check for any char from ASCII 0 - ASCII 127
+        if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) {
+            return false;
+        }
+
+        // Check for specials:
+        if (preg_match('/[][()<>@,;\\:". ]/', $atom)) {
+            return false;
+        }
+
+        // Check for control characters (ASCII 0-31):
+        if (preg_match('/[\\x00-\\x1F]+/', $atom)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Function to validate quoted string, which is:
+     * quoted-string = <"> *(qtext/quoted-pair) <">
+     *
+     * @access private
+     * @param string $qstring The string to check
+     * @return boolean Success or failure.
+     */
+    function _validateQuotedString($qstring)
+    {
+        // Leading and trailing "
+        $qstring = substr($qstring, 1, -1);
+
+        // Perform check, removing quoted characters first.
+        return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring));
+    }
+
+    /**
+     * Function to validate a mailbox, which is:
+     * mailbox =   addr-spec         ; simple address
+     *           / phrase route-addr ; name and route-addr
+     *
+     * @access public
+     * @param string &$mailbox The string to check.
+     * @return boolean Success or failure.
+     */
+    function validateMailbox(&$mailbox)
+    {
+        // A couple of defaults.
+        $phrase  = '';
+        $comment = '';
+        $comments = array();
+
+        // Catch any RFC822 comments and store them separately.
+        $_mailbox = $mailbox;
+        while (strlen(trim($_mailbox)) > 0) {
+            $parts = explode('(', $_mailbox);
+            $before_comment = $this->_splitCheck($parts, '(');
+            if ($before_comment != $_mailbox) {
+                // First char should be a (.
+                $comment    = substr(str_replace($before_comment, '', $_mailbox), 1);
+                $parts      = explode(')', $comment);
+                $comment    = $this->_splitCheck($parts, ')');
+                $comments[] = $comment;
+
+                // +2 is for the brackets
+                $_mailbox = substr($_mailbox, strpos($_mailbox, '('.$comment)+strlen($comment)+2);
+            } else {
+                break;
+            }
+        }
+
+        foreach ($comments as $comment) {
+            $mailbox = str_replace("($comment)", '', $mailbox);
+        }
+
+        $mailbox = trim($mailbox);
+
+        // Check for name + route-addr
+        if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') {
+            $parts  = explode('<', $mailbox);
+            $name   = $this->_splitCheck($parts, '<');
+
+            $phrase     = trim($name);
+            $route_addr = trim(substr($mailbox, strlen($name.'<'), -1));
+
+            if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) {
+                return false;
+            }
+
+        // Only got addr-spec
+        } else {
+            // First snip angle brackets if present.
+            if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') {
+                $addr_spec = substr($mailbox, 1, -1);
+            } else {
+                $addr_spec = $mailbox;
+            }
+
+            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
+                return false;
+            }
+        }
+
+        // Construct the object that will be returned.
+        $mbox = new stdClass();
+
+        // Add the phrase (even if empty) and comments
+        $mbox->personal = $phrase;
+        $mbox->comment  = isset($comments) ? $comments : array();
+
+        if (isset($route_addr)) {
+            $mbox->mailbox = $route_addr['local_part'];
+            $mbox->host    = $route_addr['domain'];
+            $route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : '';
+        } else {
+            $mbox->mailbox = $addr_spec['local_part'];
+            $mbox->host    = $addr_spec['domain'];
+        }
+
+        $mailbox = $mbox;
+        return true;
+    }
+
+    /**
+     * This function validates a route-addr which is:
+     * route-addr = "<" [route] addr-spec ">"
+     *
+     * Angle brackets have already been removed at the point of
+     * getting to this function.
+     *
+     * @access private
+     * @param string $route_addr The string to check.
+     * @return mixed False on failure, or an array containing validated address/route information on success.
+     */
+    function _validateRouteAddr($route_addr)
+    {
+        // Check for colon.
+        if (strpos($route_addr, ':') !== false) {
+            $parts = explode(':', $route_addr);
+            $route = $this->_splitCheck($parts, ':');
+        } else {
+            $route = $route_addr;
+        }
+
+        // If $route is same as $route_addr then the colon was in
+        // quotes or brackets or, of course, non existent.
+        if ($route === $route_addr){
+            unset($route);
+            $addr_spec = $route_addr;
+            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
+                return false;
+            }
+        } else {
+            // Validate route part.
+            if (($route = $this->_validateRoute($route)) === false) {
+                return false;
+            }
+
+            $addr_spec = substr($route_addr, strlen($route . ':'));
+
+            // Validate addr-spec part.
+            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
+                return false;
+            }
+        }
+
+        if (isset($route)) {
+            $return['adl'] = $route;
+        } else {
+            $return['adl'] = '';
+        }
+
+        $return = array_merge($return, $addr_spec);
+        return $return;
+    }
+
+    /**
+     * Function to validate a route, which is:
+     * route = 1#("@" domain) ":"
+     *
+     * @access private
+     * @param string $route The string to check.
+     * @return mixed False on failure, or the validated $route on success.
+     */
+    function _validateRoute($route)
+    {
+        // Split on comma.
+        $domains = explode(',', trim($route));
+
+        foreach ($domains as $domain) {
+            $domain = str_replace('@', '', trim($domain));
+            if (!$this->_validateDomain($domain)) return false;
+        }
+
+        return $route;
+    }
+
+    /**
+     * Function to validate a domain, though this is not quite what
+     * you expect of a strict internet domain.
+     *
+     * domain = sub-domain *("." sub-domain)
+     *
+     * @access private
+     * @param string $domain The string to check.
+     * @return mixed False on failure, or the validated domain on success.
+     */
+    function _validateDomain($domain)
+    {
+        // Note the different use of $subdomains and $sub_domains
+        $subdomains = explode('.', $domain);
+
+        while (count($subdomains) > 0) {
+            $sub_domains[] = $this->_splitCheck($subdomains, '.');
+            for ($i = 0; $i < $this->index + 1; $i++)
+                array_shift($subdomains);
+        }
+
+        foreach ($sub_domains as $sub_domain) {
+            if (!$this->_validateSubdomain(trim($sub_domain)))
+                return false;
+        }
+
+        // Managed to get here, so return input.
+        return $domain;
+    }
+
+    /**
+     * Function to validate a subdomain:
+     *   subdomain = domain-ref / domain-literal
+     *
+     * @access private
+     * @param string $subdomain The string to check.
+     * @return boolean Success or failure.
+     */
+    function _validateSubdomain($subdomain)
+    {
+        if (preg_match('|^\[(.*)]$|', $subdomain, $arr)){
+            if (!$this->_validateDliteral($arr[1])) return false;
+        } else {
+            if (!$this->_validateAtom($subdomain)) return false;
+        }
+
+        // Got here, so return successful.
+        return true;
+    }
+
+    /**
+     * Function to validate a domain literal:
+     *   domain-literal =  "[" *(dtext / quoted-pair) "]"
+     *
+     * @access private
+     * @param string $dliteral The string to check.
+     * @return boolean Success or failure.
+     */
+    function _validateDliteral($dliteral)
+    {
+        return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\';
+    }
+
+    /**
+     * Function to validate an addr-spec.
+     *
+     * addr-spec = local-part "@" domain
+     *
+     * @access private
+     * @param string $addr_spec The string to check.
+     * @return mixed False on failure, or the validated addr-spec on success.
+     */
+    function _validateAddrSpec($addr_spec)
+    {
+        $addr_spec = trim($addr_spec);
+
+        // Split on @ sign if there is one.
+        if (strpos($addr_spec, '@') !== false) {
+            $parts      = explode('@', $addr_spec);
+            $local_part = $this->_splitCheck($parts, '@');
+            $domain     = substr($addr_spec, strlen($local_part . '@'));
+
+        // No @ sign so assume the default domain.
+        } else {
+            $local_part = $addr_spec;
+            $domain     = $this->default_domain;
+        }
+
+        if (($local_part = $this->_validateLocalPart($local_part)) === false) return false;
+        if (($domain     = $this->_validateDomain($domain)) === false) return false;
+
+        // Got here so return successful.
+        return array('local_part' => $local_part, 'domain' => $domain);
+    }
+
+    /**
+     * Function to validate the local part of an address:
+     *   local-part = word *("." word)
+     *
+     * @access private
+     * @param string $local_part
+     * @return mixed False on failure, or the validated local part on success.
+     */
+    function _validateLocalPart($local_part)
+    {
+        $parts = explode('.', $local_part);
+        $words = array();
+
+        // Split the local_part into words.
+        while (count($parts) > 0){
+            $words[] = $this->_splitCheck($parts, '.');
+            for ($i = 0; $i < $this->index + 1; $i++) {
+                array_shift($parts);
+            }
+        }
+
+        // Validate each word.
+        foreach ($words as $word) {
+            // If this word contains an unquoted space, it is invalid. (6.2.4)
+            if (strpos($word, ' ') && $word[0] !== '"')
+            {
+                return false;
+            }
+
+            if ($this->_validatePhrase(trim($word)) === false) return false;
+        }
+
+        // Managed to get here, so return the input.
+        return $local_part;
+    }
+
+    /**
+     * Returns an approximate count of how many addresses are in the
+     * given string. This is APPROXIMATE as it only splits based on a
+     * comma which has no preceding backslash. Could be useful as
+     * large amounts of addresses will end up producing *large*
+     * structures when used with parseAddressList().
+     *
+     * @param  string $data Addresses to count
+     * @return int          Approximate count
+     */
+    function approximateCount($data)
+    {
+        return count(preg_split('/(?<!\\\\),/', $data));
+    }
+
+    /**
+     * This is a email validating function separate to the rest of the
+     * class. It simply validates whether an email is of the common
+     * internet form: <user>@<domain>. This can be sufficient for most
+     * people. Optional stricter mode can be utilised which restricts
+     * mailbox characters allowed to alphanumeric, full stop, hyphen
+     * and underscore.
+     *
+     * @param  string  $data   Address to check
+     * @param  boolean $strict Optional stricter mode
+     * @return mixed           False if it fails, an indexed array
+     *                         username/domain if it matches
+     */
+    function isValidInetAddress($data, $strict = false)
+    {
+        $regex = $strict ? '/^([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i' : '/^([*+!.&#$|\'\\%\/0-9a-z^_`{}=?~:-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i';
+        if (preg_match($regex, trim($data), $matches)) {
+            return array($matches[1], $matches[2]);
+        } else {
+            return false;
+        }
+    }
+
+}
Index: trunk/library/Mail/Mail/smtpmx.php
===================================================================
--- trunk/library/Mail/Mail/smtpmx.php	(revision 5146)
+++ trunk/library/Mail/Mail/smtpmx.php	(revision 5146)
@@ -0,0 +1,502 @@
+<?PHP
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * SMTP MX
+ *
+ * SMTP MX implementation of the PEAR Mail interface. Requires the Net_SMTP class.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2010, gERD Schaufelberger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Mail
+ * @package    Mail_smtpmx
+ * @author     gERD Schaufelberger <gerd@php-tools.net>
+ * @copyright  2010 gERD Schaufelberger
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: smtpmx.php 294747 2010-02-08 08:18:33Z clockwerx $
+ * @link       http://pear.php.net/package/Mail/
+ */
+
+require_once 'Net/SMTP.php';
+
+/**
+ * SMTP MX implementation of the PEAR Mail interface. Requires the Net_SMTP class.
+ *
+ *
+ * @access public
+ * @author  gERD Schaufelberger <gerd@php-tools.net>
+ * @package Mail
+ * @version $Revision: 294747 $
+ */
+class Mail_smtpmx extends Mail {
+
+    /**
+     * SMTP connection object.
+     *
+     * @var object
+     * @access private
+     */
+    var $_smtp = null;
+
+    /**
+     * The port the SMTP server is on.
+     * @var integer
+     * @see getservicebyname()
+     */
+    var $port = 25;
+
+    /**
+     * Hostname or domain that will be sent to the remote SMTP server in the
+     * HELO / EHLO message.
+     *
+     * @var string
+     * @see posix_uname()
+     */
+    var $mailname = 'localhost';
+
+    /**
+     * SMTP connection timeout value.  NULL indicates no timeout.
+     *
+     * @var integer
+     */
+    var $timeout = 10;
+
+    /**
+     * use either PEAR:Net_DNS or getmxrr
+     *
+     * @var boolean
+     */
+    var $withNetDns = true;
+
+    /**
+     * PEAR:Net_DNS_Resolver
+     *
+     * @var object
+     */
+    var $resolver;
+
+    /**
+     * Whether to use VERP or not. If not a boolean, the string value
+     * will be used as the VERP separators.
+     *
+     * @var mixed boolean or string
+     */
+    var $verp = false;
+
+    /**
+     * Whether to use VRFY or not.
+     *
+     * @var boolean $vrfy
+     */
+    var $vrfy = false;
+
+    /**
+     * Switch to test mode - don't send emails for real
+     *
+     * @var boolean $debug
+     */
+    var $test = false;
+
+    /**
+     * Turn on Net_SMTP debugging?
+     *
+     * @var boolean $peardebug
+     */
+    var $debug = false;
+
+    /**
+     * internal error codes
+     *
+     * translate internal error identifier to PEAR-Error codes and human
+     * readable messages.
+     *
+     * @var boolean $debug
+     * @todo as I need unique error-codes to identify what exactly went wrond
+     *       I did not use intergers as it should be. Instead I added a "namespace"
+     *       for each code. This avoids conflicts with error codes from different
+     *       classes. How can I use unique error codes and stay conform with PEAR?
+     */
+    var $errorCode = array(
+        'not_connected' => array(
+            'code'  => 1,
+            'msg'   => 'Could not connect to any mail server ({HOST}) at port {PORT} to send mail to {RCPT}.'
+        ),
+        'failed_vrfy_rcpt' => array(
+            'code'  => 2,
+            'msg'   => 'Recipient "{RCPT}" could not be veryfied.'
+        ),
+        'failed_set_from' => array(
+            'code'  => 3,
+            'msg'   => 'Failed to set sender: {FROM}.'
+        ),
+        'failed_set_rcpt' => array(
+            'code'  => 4,
+            'msg'   => 'Failed to set recipient: {RCPT}.'
+        ),
+        'failed_send_data' => array(
+            'code'  => 5,
+            'msg'   => 'Failed to send mail to: {RCPT}.'
+        ),
+        'no_from' => array(
+            'code'  => 5,
+            'msg'   => 'No from address has be provided.'
+        ),
+        'send_data' => array(
+            'code'  => 7,
+            'msg'   => 'Failed to create Net_SMTP object.'
+        ),
+        'no_mx' => array(
+            'code'  => 8,
+            'msg'   => 'No MX-record for {RCPT} found.'
+        ),
+        'no_resolver' => array(
+            'code'  => 9,
+            'msg'   => 'Could not start resolver! Install PEAR:Net_DNS or switch off "netdns"'
+        ),
+        'failed_rset' => array(
+            'code'  => 10,
+            'msg'   => 'RSET command failed, SMTP-connection corrupt.'
+        ),
+    );
+
+    /**
+     * Constructor.
+     *
+     * Instantiates a new Mail_smtp:: object based on the parameters
+     * passed in. It looks for the following parameters:
+     *     mailname    The name of the local mail system (a valid hostname which matches the reverse lookup)
+     *     port        smtp-port - the default comes from getservicebyname() and should work fine
+     *     timeout     The SMTP connection timeout. Defaults to 30 seconds.
+     *     vrfy        Whether to use VRFY or not. Defaults to false.
+     *     verp        Whether to use VERP or not. Defaults to false.
+     *     test        Activate test mode? Defaults to false.
+     *     debug       Activate SMTP and Net_DNS debug mode? Defaults to false.
+     *     netdns      whether to use PEAR:Net_DNS or the PHP build in function getmxrr, default is true
+     *
+     * If a parameter is present in the $params array, it replaces the
+     * default.
+     *
+     * @access public
+     * @param array Hash containing any parameters different from the
+     *              defaults.
+     * @see _Mail_smtpmx()
+     */
+    function __construct($params)
+    {
+        if (isset($params['mailname'])) {
+            $this->mailname = $params['mailname'];
+        } else {
+            // try to find a valid mailname
+            if (function_exists('posix_uname')) {
+                $uname = posix_uname();
+                $this->mailname = $uname['nodename'];
+            }
+        }
+
+        // port number
+        if (isset($params['port'])) {
+            $this->_port = $params['port'];
+        } else {
+            $this->_port = getservbyname('smtp', 'tcp');
+        }
+
+        if (isset($params['timeout'])) $this->timeout = $params['timeout'];
+        if (isset($params['verp'])) $this->verp = $params['verp'];
+        if (isset($params['test'])) $this->test = $params['test'];
+        if (isset($params['peardebug'])) $this->test = $params['peardebug'];
+        if (isset($params['netdns'])) $this->withNetDns = $params['netdns'];
+    }
+
+    /**
+     * Constructor wrapper for PHP4
+     *
+     * @access public
+     * @param array Hash containing any parameters different from the defaults
+     * @see __construct()
+     */
+    function Mail_smtpmx($params)
+    {
+        $this->__construct($params);
+        register_shutdown_function(array(&$this, '__destruct'));
+    }
+
+    /**
+     * Destructor implementation to ensure that we disconnect from any
+     * potentially-alive persistent SMTP connections.
+     */
+    function __destruct()
+    {
+        if (is_object($this->_smtp)) {
+            $this->_smtp->disconnect();
+            $this->_smtp = null;
+        }
+    }
+
+    /**
+     * Implements Mail::send() function using SMTP direct delivery
+     *
+     * @access public
+     * @param mixed $recipients in RFC822 style or array
+     * @param array $headers The array of headers to send with the mail.
+     * @param string $body The full text of the message body,
+     * @return mixed Returns true on success, or a PEAR_Error
+     */
+    function send($recipients, $headers, $body)
+    {
+        if (!is_array($headers)) {
+            return PEAR::raiseError('$headers must be an array');
+        }
+
+        $result = $this->_sanitizeHeaders($headers);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        // Prepare headers
+        $headerElements = $this->prepareHeaders($headers);
+        if (is_a($headerElements, 'PEAR_Error')) {
+            return $headerElements;
+        }
+        list($from, $textHeaders) = $headerElements;
+
+        // use 'Return-Path' if possible
+        if (!empty($headers['Return-Path'])) {
+            $from = $headers['Return-Path'];
+        }
+        if (!isset($from)) {
+            return $this->_raiseError('no_from');
+        }
+
+        // Prepare recipients
+        $recipients = $this->parseRecipients($recipients);
+        if (is_a($recipients, 'PEAR_Error')) {
+            return $recipients;
+        }
+
+        foreach ($recipients as $rcpt) {
+            list($user, $host) = explode('@', $rcpt);
+
+            $mx = $this->_getMx($host);
+            if (is_a($mx, 'PEAR_Error')) {
+                return $mx;
+            }
+
+            if (empty($mx)) {
+                $info = array('rcpt' => $rcpt);
+                return $this->_raiseError('no_mx', $info);
+            }
+
+            $connected = false;
+            foreach ($mx as $mserver => $mpriority) {
+                $this->_smtp = new Net_SMTP($mserver, $this->port, $this->mailname);
+
+                // configure the SMTP connection.
+                if ($this->debug) {
+                    $this->_smtp->setDebug(true);
+                }
+
+                // attempt to connect to the configured SMTP server.
+                $res = $this->_smtp->connect($this->timeout);
+                if (is_a($res, 'PEAR_Error')) {
+                    $this->_smtp = null;
+                    continue;
+                }
+
+                // connection established
+                if ($res) {
+                    $connected = true;
+                    break;
+                }
+            }
+
+            if (!$connected) {
+                $info = array(
+                    'host' => implode(', ', array_keys($mx)),
+                    'port' => $this->port,
+                    'rcpt' => $rcpt,
+                );
+                return $this->_raiseError('not_connected', $info);
+            }
+
+            // Verify recipient
+            if ($this->vrfy) {
+                $res = $this->_smtp->vrfy($rcpt);
+                if (is_a($res, 'PEAR_Error')) {
+                    $info = array('rcpt' => $rcpt);
+                    return $this->_raiseError('failed_vrfy_rcpt', $info);
+                }
+            }
+
+            // mail from:
+            $args['verp'] = $this->verp;
+            $res = $this->_smtp->mailFrom($from, $args);
+            if (is_a($res, 'PEAR_Error')) {
+                $info = array('from' => $from);
+                return $this->_raiseError('failed_set_from', $info);
+            }
+
+            // rcpt to:
+            $res = $this->_smtp->rcptTo($rcpt);
+            if (is_a($res, 'PEAR_Error')) {
+                $info = array('rcpt' => $rcpt);
+                return $this->_raiseError('failed_set_rcpt', $info);
+            }
+
+            // Don't send anything in test mode
+            if ($this->test) {
+                $result = $this->_smtp->rset();
+                $res = $this->_smtp->rset();
+                if (is_a($res, 'PEAR_Error')) {
+                    return $this->_raiseError('failed_rset');
+                }
+
+                $this->_smtp->disconnect();
+                $this->_smtp = null;
+                return true;
+            }
+
+            // Send data
+            $res = $this->_smtp->data("$textHeaders\r\n$body");
+            if (is_a($res, 'PEAR_Error')) {
+                $info = array('rcpt' => $rcpt);
+                return $this->_raiseError('failed_send_data', $info);
+            }
+
+            $this->_smtp->disconnect();
+            $this->_smtp = null;
+        }
+
+        return true;
+    }
+
+    /**
+     * Recieve mx rexords for a spciefied host
+     *
+     * The MX records
+     *
+     * @access private
+     * @param string $host mail host
+     * @return mixed sorted
+     */
+    function _getMx($host)
+    {
+        $mx = array();
+
+        if ($this->withNetDns) {
+            $res = $this->_loadNetDns();
+            if (is_a($res, 'PEAR_Error')) {
+                return $res;
+            }
+
+            $response = $this->resolver->query($host, 'MX');
+            if (!$response) {
+                return false;
+            }
+
+            foreach ($response->answer as $rr) {
+                if ($rr->type == 'MX') {
+                    $mx[$rr->exchange] = $rr->preference;
+                }
+            }
+        } else {
+            $mxHost = array();
+            $mxWeight = array();
+
+            if (!getmxrr($host, $mxHost, $mxWeight)) {
+                return false;
+            }
+            for ($i = 0; $i < count($mxHost); ++$i) {
+                $mx[$mxHost[$i]] = $mxWeight[$i];
+            }
+        }
+
+        asort($mx);
+        return $mx;
+    }
+
+    /**
+     * initialize PEAR:Net_DNS_Resolver
+     *
+     * @access private
+     * @return boolean true on success
+     */
+    function _loadNetDns()
+    {
+        if (is_object($this->resolver)) {
+            return true;
+        }
+
+        if (!include_once 'Net/DNS.php') {
+            return $this->_raiseError('no_resolver');
+        }
+
+        $this->resolver = new Net_DNS_Resolver();
+        if ($this->debug) {
+            $this->resolver->test = 1;
+        }
+
+        return true;
+    }
+
+    /**
+     * raise standardized error
+     *
+     * include additional information in error message
+     *
+     * @access private
+     * @param string $id maps error ids to codes and message
+     * @param array $info optional information in associative array
+     * @see _errorCode
+     */
+    function _raiseError($id, $info = array())
+    {
+        $code = $this->errorCode[$id]['code'];
+        $msg = $this->errorCode[$id]['msg'];
+
+        // include info to messages
+        if (!empty($info)) {
+            $search = array();
+            $replace = array();
+
+            foreach ($info as $key => $value) {
+                array_push($search, '{' . strtoupper($key) . '}');
+                array_push($replace, $value);
+            }
+
+            $msg = str_replace($search, $replace, $msg);
+        }
+
+        return PEAR::raiseError($msg, $code);
+    }
+
+}
Index: trunk/library/Mail/Mail.php
===================================================================
--- trunk/library/Mail/Mail.php	(revision 5146)
+++ trunk/library/Mail/Mail.php	(revision 5146)
@@ -0,0 +1,270 @@
+<?php
+/**
+ *  PEAR's Mail:: interface.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2002-2007, Richard Heyes
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category    Mail
+ * @package     Mail
+ * @author      Chuck Hagenbuch <chuck@horde.org>
+ * @copyright   1997-2010 Chuck Hagenbuch
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id: Mail.php 294747 2010-02-08 08:18:33Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ */
+
+require_once $_SESSION['rootPath'].'/library/PEAR/PEAR.php';
+
+/**
+ * PEAR's Mail:: interface. Defines the interface for implementing
+ * mailers under the PEAR hierarchy, and provides supporting functions
+ * useful in multiple mailer backends.
+ *
+ * @access public
+ * @version $Revision: 294747 $
+ * @package Mail
+ */
+class Mail
+{
+    /**
+     * Line terminator used for separating header lines.
+     * @var string
+     */
+    var $sep = "\r\n";
+
+    /**
+     * Provides an interface for generating Mail:: objects of various
+     * types
+     *
+     * @param string $driver The kind of Mail:: object to instantiate.
+     * @param array  $params The parameters to pass to the Mail:: object.
+     * @return object Mail a instance of the driver class or if fails a PEAR Error
+     * @access public
+     */
+    function &factory($driver, $params = array())
+    {
+        $driver = strtolower($driver);
+        @include_once 'Mail/' . $driver . '.php';
+        $class = 'Mail_' . $driver;
+        if (class_exists($class)) {
+            $mailer = new $class($params);
+            return $mailer;
+        } else {
+            return PEAR::raiseError('Unable to find class for driver ' . $driver);
+        }
+    }
+
+    /**
+     * Implements Mail::send() function using php's built-in mail()
+     * command.
+     *
+     * @param mixed $recipients Either a comma-seperated list of recipients
+     *              (RFC822 compliant), or an array of recipients,
+     *              each RFC822 valid. This may contain recipients not
+     *              specified in the headers, for Bcc:, resending
+     *              messages, etc.
+     *
+     * @param array $headers The array of headers to send with the mail, in an
+     *              associative array, where the array key is the
+     *              header name (ie, 'Subject'), and the array value
+     *              is the header value (ie, 'test'). The header
+     *              produced from those values would be 'Subject:
+     *              test'.
+     *
+     * @param string $body The full text of the message body, including any
+     *               Mime parts, etc.
+     *
+     * @return mixed Returns true on success, or a PEAR_Error
+     *               containing a descriptive error message on
+     *               failure.
+     *
+     * @access public
+     * @deprecated use Mail_mail::send instead
+     */
+    function send($recipients, $headers, $body)
+    {
+        if (!is_array($headers)) {
+            return PEAR::raiseError('$headers must be an array');
+        }
+
+        $result = $this->_sanitizeHeaders($headers);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        // if we're passed an array of recipients, implode it.
+        if (is_array($recipients)) {
+            $recipients = implode(', ', $recipients);
+        }
+
+        // get the Subject out of the headers array so that we can
+        // pass it as a seperate argument to mail().
+        $subject = '';
+        if (isset($headers['Subject'])) {
+            $subject = $headers['Subject'];
+            unset($headers['Subject']);
+        }
+
+        // flatten the headers out.
+        list(, $text_headers) = Mail::prepareHeaders($headers);
+
+        return mail($recipients, $subject, $body, $text_headers);
+    }
+
+    /**
+     * Sanitize an array of mail headers by removing any additional header
+     * strings present in a legitimate header's value.  The goal of this
+     * filter is to prevent mail injection attacks.
+     *
+     * @param array $headers The associative array of headers to sanitize.
+     *
+     * @access private
+     */
+    function _sanitizeHeaders(&$headers)
+    {
+        foreach ($headers as $key => $value) {
+            $headers[$key] =
+                preg_replace('=((<CR>|<LF>|0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i',
+                             null, $value);
+        }
+    }
+
+    /**
+     * Take an array of mail headers and return a string containing
+     * text usable in sending a message.
+     *
+     * @param array $headers The array of headers to prepare, in an associative
+     *              array, where the array key is the header name (ie,
+     *              'Subject'), and the array value is the header
+     *              value (ie, 'test'). The header produced from those
+     *              values would be 'Subject: test'.
+     *
+     * @return mixed Returns false if it encounters a bad address,
+     *               otherwise returns an array containing two
+     *               elements: Any From: address found in the headers,
+     *               and the plain text version of the headers.
+     * @access private
+     */
+    function prepareHeaders($headers)
+    {
+        $lines = array();
+        $from = null;
+
+        foreach ($headers as $key => $value) {
+            if (strcasecmp($key, 'From') === 0) {
+                include_once 'Mail/RFC822.php';
+                $parser = new Mail_RFC822();
+                $addresses = $parser->parseAddressList($value, 'localhost', false);
+                if (is_a($addresses, 'PEAR_Error')) {
+                    return $addresses;
+                }
+
+                $from = $addresses[0]->mailbox . '@' . $addresses[0]->host;
+
+                // Reject envelope From: addresses with spaces.
+                if (strstr($from, ' ')) {
+                    return false;
+                }
+
+                $lines[] = $key . ': ' . $value;
+            } elseif (strcasecmp($key, 'Received') === 0) {
+                $received = array();
+                if (is_array($value)) {
+                    foreach ($value as $line) {
+                        $received[] = $key . ': ' . $line;
+                    }
+                }
+                else {
+                    $received[] = $key . ': ' . $value;
+                }
+                // Put Received: headers at the top.  Spam detectors often
+                // flag messages with Received: headers after the Subject:
+                // as spam.
+                $lines = array_merge($received, $lines);
+            } else {
+                // If $value is an array (i.e., a list of addresses), convert
+                // it to a comma-delimited string of its elements (addresses).
+                if (is_array($value)) {
+                    $value = implode(', ', $value);
+                }
+                $lines[] = $key . ': ' . $value;
+            }
+        }
+
+        return array($from, join($this->sep, $lines));
+    }
+
+    /**
+     * Take a set of recipients and parse them, returning an array of
+     * bare addresses (forward paths) that can be passed to sendmail
+     * or an smtp server with the rcpt to: command.
+     *
+     * @param mixed Either a comma-seperated list of recipients
+     *              (RFC822 compliant), or an array of recipients,
+     *              each RFC822 valid.
+     *
+     * @return mixed An array of forward paths (bare addresses) or a PEAR_Error
+     *               object if the address list could not be parsed.
+     * @access private
+     */
+    function parseRecipients($recipients)
+    {
+        include_once 'Mail/RFC822.php';
+
+        // if we're passed an array, assume addresses are valid and
+        // implode them before parsing.
+        if (is_array($recipients)) {
+            $recipients = implode(', ', $recipients);
+        }
+
+        // Parse recipients, leaving out all personal info. This is
+        // for smtp recipients, etc. All relevant personal information
+        // should already be in the headers.
+        $addresses = Mail_RFC822::parseAddressList($recipients, 'localhost', false);
+
+        // If parseAddressList() returned a PEAR_Error object, just return it.
+        if (is_a($addresses, 'PEAR_Error')) {
+            return $addresses;
+        }
+
+        $recipients = array();
+        if (is_array($addresses)) {
+            foreach ($addresses as $ob) {
+                $recipients[] = $ob->mailbox . '@' . $ob->host;
+            }
+        }
+
+        return $recipients;
+    }
+
+}
Index: trunk/library/ckeditor/README
===================================================================
--- trunk/library/ckeditor/README	(revision 5146)
+++ trunk/library/ckeditor/README	(revision 5146)
@@ -0,0 +1,39 @@
+
+Versão atual do editor 3.62;
+
+________________________________________________________________________________
+
+Alterações feitas apartir da versão original:
+--------------------------------------------------------------------------------
+Arquivo: ckeditor.js
+Codigo Inserido:
+################################################################################
+# //Expresso Seta Fonte Padrão                                                 #
+# if( t === true && typeof(preferences) != 'undefined')                        #
+# {                                                                            #
+#    if(typeof(preferences.font_size_editor) != 'undefined')                   #
+#        w.setStyle( 'font-size' , preferences.font_size_editor);              #
+#    if(typeof(preferences.font_family_editor) != 'undefined')                 #
+#        w.setStyle( 'font-family' , preferences.font_family_editor);          #
+# }                                                                            #
+################################################################################
+
+Descrição: Altera a fonte padrão do editor para a fonte configurada nas preferencias do usuario.
+           Maiores Informações acessar > https://dev.prognus.com.br/expresso/ticket/1554
+--------------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+Arquivo: config.js
+Descrição: Configurações do editor com expresso.
+--------------------------------------------------------------------------------
+________________________________________________________________________________
+
+Plugins Adicionais:
+--------------------------------------------------------------------------------
+Nome: Expresso
+Descrição: Funcinalidades especificas do Expresso como inserir imagem e inserir assinatura.
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+Nome: Aspell
+Descrição: Corretor ortografico.
+--------------------------------------------------------------------------------
Index: trunk/library/Net/SMTP.php
===================================================================
--- trunk/library/Net/SMTP.php	(revision 5146)
+++ trunk/library/Net/SMTP.php	(revision 5146)
@@ -0,0 +1,1342 @@
+<?php
+/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Chuck Hagenbuch <chuck@horde.org>                           |
+// |          Jon Parise <jon@php.net>                                    |
+// |          Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>      |
+// +----------------------------------------------------------------------+
+//
+// $Id: SMTP.php 314875 2011-08-13 17:03:30Z jon $
+
+require_once $_SESSION['rootPath'].'/library/PEAR/PEAR.php';
+require_once $_SESSION['rootPath'].'/library/Net/Socket.php';
+
+/**
+ * Provides an implementation of the SMTP protocol using PEAR's
+ * Net_Socket:: class.
+ *
+ * @package Net_SMTP
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @author  Jon Parise <jon@php.net>
+ * @author  Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
+ *
+ * @example basic.php   A basic implementation of the Net_SMTP package.
+ */
+class Net_SMTP
+{
+    /**
+     * The server to connect to.
+     * @var string
+     * @access public
+     */
+    var $host = 'localhost';
+
+    /**
+     * The port to connect to.
+     * @var int
+     * @access public
+     */
+    var $port = 25;
+
+    /**
+     * The value to give when sending EHLO or HELO.
+     * @var string
+     * @access public
+     */
+    var $localhost = 'localhost';
+
+    /**
+     * List of supported authentication methods, in preferential order.
+     * @var array
+     * @access public
+     */
+    var $auth_methods = array();
+
+    /**
+     * Use SMTP command pipelining (specified in RFC 2920) if the SMTP
+     * server supports it.
+     *
+     * When pipeling is enabled, rcptTo(), mailFrom(), sendFrom(),
+     * somlFrom() and samlFrom() do not wait for a response from the
+     * SMTP server but return immediately.
+     *
+     * @var bool
+     * @access public
+     */
+    var $pipelining = false;
+
+    /**
+     * Number of pipelined commands.
+     * @var int
+     * @access private
+     */
+    var $_pipelined_commands = 0;
+
+    /**
+     * Should debugging output be enabled?
+     * @var boolean
+     * @access private
+     */
+    var $_debug = false;
+
+    /**
+     * Debug output handler.
+     * @var callback
+     * @access private
+     */
+    var $_debug_handler = null;
+
+    /**
+     * The socket resource being used to connect to the SMTP server.
+     * @var resource
+     * @access private
+     */
+    var $_socket = null;
+
+    /**
+     * Array of socket options that will be passed to Net_Socket::connect().
+     * @see stream_context_create()
+     * @var array
+     * @access private
+     */
+    var $_socket_options = null;
+
+    /**
+     * The socket I/O timeout value in seconds.
+     * @var int
+     * @access private
+     */
+    var $_timeout = 0;
+
+    /**
+     * The most recent server response code.
+     * @var int
+     * @access private
+     */
+    var $_code = -1;
+
+    /**
+     * The most recent server response arguments.
+     * @var array
+     * @access private
+     */
+    var $_arguments = array();
+
+    /**
+     * Stores the SMTP server's greeting string.
+     * @var string
+     * @access private
+     */
+    var $_greeting = null;
+
+    /**
+     * Stores detected features of the SMTP server.
+     * @var array
+     * @access private
+     */
+    var $_esmtp = array();
+
+    /**
+     * Instantiates a new Net_SMTP object, overriding any defaults
+     * with parameters that are passed in.
+     *
+     * If you have SSL support in PHP, you can connect to a server
+     * over SSL using an 'ssl://' prefix:
+     *
+     *   // 465 is a common smtps port.
+     *   $smtp = new Net_SMTP('ssl://mail.host.com', 465);
+     *   $smtp->connect();
+     *
+     * @param string  $host       The server to connect to.
+     * @param integer $port       The port to connect to.
+     * @param string  $localhost  The value to give when sending EHLO or HELO.
+     * @param boolean $pipeling   Use SMTP command pipelining
+     * @param integer $timeout    Socket I/O timeout in seconds.
+     * @param array   $socket_options Socket stream_context_create() options.
+     *
+     * @access  public
+     * @since   1.0
+     */
+    function Net_SMTP($host = null, $port = null, $localhost = null,
+        $pipelining = false, $timeout = 0, $socket_options = null)
+    {
+        if (isset($host)) {
+            $this->host = $host;
+        }
+        if (isset($port)) {
+            $this->port = $port;
+        }
+        if (isset($localhost)) {
+            $this->localhost = $localhost;
+        }
+        $this->pipelining = $pipelining;
+
+        $this->_socket = new Net_Socket();
+        $this->_socket_options = $socket_options;
+        $this->_timeout = $timeout;
+
+        /* Include the Auth_SASL package.  If the package is available, we 
+         * enable the authentication methods that depend upon it. */
+        if (@include_once 'Auth/SASL.php') {
+            $this->setAuthMethod('CRAM-MD5', array($this, '_authCram_MD5'));
+            $this->setAuthMethod('DIGEST-MD5', array($this, '_authDigest_MD5'));
+        }
+
+        /* These standard authentication methods are always available. */
+        $this->setAuthMethod('LOGIN', array($this, '_authLogin'), false);
+        $this->setAuthMethod('PLAIN', array($this, '_authPlain'), false);
+    }
+
+    /**
+     * Set the socket I/O timeout value in seconds plus microseconds.
+     *
+     * @param   integer $seconds        Timeout value in seconds.
+     * @param   integer $microseconds   Additional value in microseconds.
+     *
+     * @access  public
+     * @since   1.5.0
+     */
+    function setTimeout($seconds, $microseconds = 0) {
+        return $this->_socket->setTimeout($seconds, $microseconds);
+    }
+
+    /**
+     * Set the value of the debugging flag.
+     *
+     * @param   boolean $debug      New value for the debugging flag.
+     *
+     * @access  public
+     * @since   1.1.0
+     */
+    function setDebug($debug, $handler = null)
+    {
+        $this->_debug = $debug;
+        $this->_debug_handler = $handler;
+    }
+
+    /**
+     * Write the given debug text to the current debug output handler.
+     *
+     * @param   string  $message    Debug mesage text.
+     *
+     * @access  private
+     * @since   1.3.3
+     */
+    function _debug($message)
+    {
+        if ($this->_debug) {
+            if ($this->_debug_handler) {
+                call_user_func_array($this->_debug_handler,
+                                     array(&$this, $message));
+            } else {
+                echo "DEBUG: $message\n";
+            }
+        }
+    }
+
+    /**
+     * Send the given string of data to the server.
+     *
+     * @param   string  $data       The string of data to send.
+     *
+     * @return  mixed   The number of bytes that were actually written,
+     *                  or a PEAR_Error object on failure.
+     *
+     * @access  private
+     * @since   1.1.0
+     */
+    function _send($data)
+    {
+        $this->_debug("Send: $data");
+
+        $result = $this->_socket->write($data);
+        if (!$result || PEAR::isError($result)) {
+            $msg = ($result) ? $result->getMessage() : "unknown error";
+            return PEAR::raiseError("Failed to write to socket: $msg",
+                                    null, PEAR_ERROR_RETURN);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Send a command to the server with an optional string of
+     * arguments.  A carriage return / linefeed (CRLF) sequence will
+     * be appended to each command string before it is sent to the
+     * SMTP server - an error will be thrown if the command string
+     * already contains any newline characters. Use _send() for
+     * commands that must contain newlines.
+     *
+     * @param   string  $command    The SMTP command to send to the server.
+     * @param   string  $args       A string of optional arguments to append
+     *                              to the command.
+     *
+     * @return  mixed   The result of the _send() call.
+     *
+     * @access  private
+     * @since   1.1.0
+     */
+    function _put($command, $args = '')
+    {
+        if (!empty($args)) {
+            $command .= ' ' . $args;
+        }
+
+        if (strcspn($command, "\r\n") !== strlen($command)) {
+            return PEAR::raiseError('Commands cannot contain newlines',
+                                    null, PEAR_ERROR_RETURN);
+        }
+
+        return $this->_send($command . "\r\n");
+    }
+
+    /**
+     * Read a reply from the SMTP server.  The reply consists of a response
+     * code and a response message.
+     *
+     * @param   mixed   $valid      The set of valid response codes.  These
+     *                              may be specified as an array of integer
+     *                              values or as a single integer value.
+     * @param   bool    $later      Do not parse the response now, but wait
+     *                              until the last command in the pipelined
+     *                              command group
+     *
+     * @return  mixed   True if the server returned a valid response code or
+     *                  a PEAR_Error object is an error condition is reached.
+     *
+     * @access  private
+     * @since   1.1.0
+     *
+     * @see     getResponse
+     */
+    function _parseResponse($valid, $later = false)
+    {
+        $this->_code = -1;
+        $this->_arguments = array();
+
+        if ($later) {
+            $this->_pipelined_commands++;
+            return true;
+        }
+
+        for ($i = 0; $i <= $this->_pipelined_commands; $i++) {
+            while ($line = $this->_socket->readLine()) {
+                $this->_debug("Recv: $line");
+
+                /* If we receive an empty line, the connection was closed. */
+                if (empty($line)) {
+                    $this->disconnect();
+                    return PEAR::raiseError('Connection was closed',
+                                            null, PEAR_ERROR_RETURN);
+                }
+
+                /* Read the code and store the rest in the arguments array. */
+                $code = substr($line, 0, 3);
+                $this->_arguments[] = trim(substr($line, 4));
+
+                /* Check the syntax of the response code. */
+                if (is_numeric($code)) {
+                    $this->_code = (int)$code;
+                } else {
+                    $this->_code = -1;
+                    break;
+                }
+
+                /* If this is not a multiline response, we're done. */
+                if (substr($line, 3, 1) != '-') {
+                    break;
+                }
+            }
+        }
+
+        $this->_pipelined_commands = 0;
+
+        /* Compare the server's response code with the valid code/codes. */
+        if (is_int($valid) && ($this->_code === $valid)) {
+            return true;
+        } elseif (is_array($valid) && in_array($this->_code, $valid, true)) {
+            return true;
+        }
+
+        return PEAR::raiseError('Invalid response code received from server',
+                                $this->_code, PEAR_ERROR_RETURN);
+    }
+
+    /**
+     * Issue an SMTP command and verify its response.
+     *
+     * @param   string  $command    The SMTP command string or data.
+     * @param   mixed   $valid      The set of valid response codes.  These
+     *                              may be specified as an array of integer
+     *                              values or as a single integer value.
+     *
+     * @return  mixed   True on success or a PEAR_Error object on failure.
+     *
+     * @access  public
+     * @since   1.6.0
+     */
+    function command($command, $valid)
+    {
+        if (PEAR::isError($error = $this->_put($command))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse($valid))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Return a 2-tuple containing the last response from the SMTP server.
+     *
+     * @return  array   A two-element array: the first element contains the
+     *                  response code as an integer and the second element
+     *                  contains the response's arguments as a string.
+     *
+     * @access  public
+     * @since   1.1.0
+     */
+    function getResponse()
+    {
+        return array($this->_code, join("\n", $this->_arguments));
+    }
+
+    /**
+     * Return the SMTP server's greeting string.
+     *
+     * @return  string  A string containing the greeting string, or null if a 
+     *                  greeting has not been received.
+     *
+     * @access  public
+     * @since   1.3.3
+     */
+    function getGreeting()
+    {
+        return $this->_greeting;
+    }
+
+    /**
+     * Attempt to connect to the SMTP server.
+     *
+     * @param   int     $timeout    The timeout value (in seconds) for the
+     *                              socket connection attempt.
+     * @param   bool    $persistent Should a persistent socket connection
+     *                              be used?
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.0
+     */
+    function connect($timeout = null, $persistent = false)
+    {
+        $this->_greeting = null;
+        $result = $this->_socket->connect($this->host, $this->port,
+                                          $persistent, $timeout,
+                                          $this->_socket_options);
+        if (PEAR::isError($result)) {
+            return PEAR::raiseError('Failed to connect socket: ' .
+                                    $result->getMessage());
+        }
+
+        /*
+         * Now that we're connected, reset the socket's timeout value for 
+         * future I/O operations.  This allows us to have different socket 
+         * timeout values for the initial connection (our $timeout parameter) 
+         * and all other socket operations.
+         */
+        if ($this->_timeout > 0) {
+            if (PEAR::isError($error = $this->setTimeout($this->_timeout))) {
+                return $error;
+            }
+        }
+
+        if (PEAR::isError($error = $this->_parseResponse(220))) {
+            return $error;
+        }
+
+        /* Extract and store a copy of the server's greeting string. */
+        list(, $this->_greeting) = $this->getResponse();
+
+        if (PEAR::isError($error = $this->_negotiate())) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Attempt to disconnect from the SMTP server.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.0
+     */
+    function disconnect()
+    {
+        if (PEAR::isError($error = $this->_put('QUIT'))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(221))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_socket->disconnect())) {
+            return PEAR::raiseError('Failed to disconnect socket: ' .
+                                    $error->getMessage());
+        }
+
+        return true;
+    }
+
+    /**
+     * Attempt to send the EHLO command and obtain a list of ESMTP
+     * extensions available, and failing that just send HELO.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     *
+     * @access private
+     * @since  1.1.0
+     */
+    function _negotiate()
+    {
+        if (PEAR::isError($error = $this->_put('EHLO', $this->localhost))) {
+            return $error;
+        }
+
+        if (PEAR::isError($this->_parseResponse(250))) {
+            /* If we receive a 503 response, we're already authenticated. */
+            if ($this->_code === 503) {
+                return true;
+            }
+
+            /* If the EHLO failed, try the simpler HELO command. */
+            if (PEAR::isError($error = $this->_put('HELO', $this->localhost))) {
+                return $error;
+            }
+            if (PEAR::isError($this->_parseResponse(250))) {
+                return PEAR::raiseError('HELO was not accepted: ', $this->_code,
+                                        PEAR_ERROR_RETURN);
+            }
+
+            return true;
+        }
+
+        foreach ($this->_arguments as $argument) {
+            $verb = strtok($argument, ' ');
+            $arguments = substr($argument, strlen($verb) + 1,
+                                strlen($argument) - strlen($verb) - 1);
+            $this->_esmtp[$verb] = $arguments;
+        }
+
+        if (!isset($this->_esmtp['PIPELINING'])) {
+            $this->pipelining = false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the name of the best authentication method that the server
+     * has advertised.
+     *
+     * @return mixed    Returns a string containing the name of the best
+     *                  supported authentication method or a PEAR_Error object
+     *                  if a failure condition is encountered.
+     * @access private
+     * @since  1.1.0
+     */
+    function _getBestAuthMethod()
+    {
+        $available_methods = explode(' ', $this->_esmtp['AUTH']);
+
+        foreach ($this->auth_methods as $method => $callback) {
+            if (in_array($method, $available_methods)) {
+                return $method;
+            }
+        }
+
+        return PEAR::raiseError('No supported authentication methods',
+                                null, PEAR_ERROR_RETURN);
+    }
+
+    /**
+     * Attempt to do SMTP authentication.
+     *
+     * @param string The userid to authenticate as.
+     * @param string The password to authenticate with.
+     * @param string The requested authentication method.  If none is
+     *               specified, the best supported method will be used.
+     * @param bool   Flag indicating whether or not TLS should be attempted.
+     * @param string An optional authorization identifier.  If specified, this
+     *               identifier will be used as the authorization proxy.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.0
+     */
+    function auth($uid, $pwd , $method = '', $tls = true, $authz = '')
+    {
+        /* We can only attempt a TLS connection if one has been requested,
+         * we're running PHP 5.1.0 or later, have access to the OpenSSL 
+         * extension, are connected to an SMTP server which supports the 
+         * STARTTLS extension, and aren't already connected over a secure 
+         * (SSL) socket connection. */
+        if ($tls && version_compare(PHP_VERSION, '5.1.0', '>=') &&
+            extension_loaded('openssl') && isset($this->_esmtp['STARTTLS']) &&
+            strncasecmp($this->host, 'ssl://', 6) !== 0) {
+            /* Start the TLS connection attempt. */
+            if (PEAR::isError($result = $this->_put('STARTTLS'))) {
+                return $result;
+            }
+            if (PEAR::isError($result = $this->_parseResponse(220))) {
+                return $result;
+            }
+            if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) {
+                return $result;
+            } elseif ($result !== true) {
+                return PEAR::raiseError('STARTTLS failed');
+            }
+
+            /* Send EHLO again to recieve the AUTH string from the
+             * SMTP server. */
+            $this->_negotiate();
+        }
+
+        if (empty($this->_esmtp['AUTH'])) {
+            return PEAR::raiseError('SMTP server does not support authentication');
+        }
+
+        /* If no method has been specified, get the name of the best
+         * supported method advertised by the SMTP server. */
+        if (empty($method)) {
+            if (PEAR::isError($method = $this->_getBestAuthMethod())) {
+                /* Return the PEAR_Error object from _getBestAuthMethod(). */
+                return $method;
+            }
+        } else {
+            $method = strtoupper($method);
+            if (!array_key_exists($method, $this->auth_methods)) {
+                return PEAR::raiseError("$method is not a supported authentication method");
+            }
+        }
+
+        if (!isset($this->auth_methods[$method])) {
+            return PEAR::raiseError("$method is not a supported authentication method");
+        }
+
+        if (!is_callable($this->auth_methods[$method], false)) {
+            return PEAR::raiseError("$method authentication method cannot be called");
+        }
+
+        if (is_array($this->auth_methods[$method])) {
+            list($object, $method) = $this->auth_methods[$method];
+            $result = $object->{$method}($uid, $pwd, $authz, $this);
+        } else {
+            $func =  $this->auth_methods[$method];
+            $result = $func($uid, $pwd, $authz, $this);
+         }
+
+        /* If an error was encountered, return the PEAR_Error object. */
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        return true;
+    }
+
+    /**
+     * Add a new authentication method.
+     *
+     * @param string    The authentication method name (e.g. 'PLAIN')
+     * @param mixed     The authentication callback (given as the name of a 
+     *                  function or as an (object, method name) array).
+     * @param bool      Should the new method be prepended to the list of 
+     *                  available methods?  This is the default behavior, 
+     *                  giving the new method the highest priority.
+     *
+     * @return  mixed   True on success or a PEAR_Error object on failure.
+     *
+     * @access public
+     * @since  1.6.0
+     */
+    function setAuthMethod($name, $callback, $prepend = true)
+    {
+        if (!is_string($name)) {
+            return PEAR::raiseError('Method name is not a string');
+        }
+
+        if (!is_string($callback) && !is_array($callback)) {
+            return PEAR::raiseError('Method callback must be string or array');
+        }
+
+        if (is_array($callback)) {
+            if (!is_object($callback[0]) || !is_string($callback[1]))
+                return PEAR::raiseError('Bad mMethod callback array');
+        }
+
+        if ($prepend) {
+            $this->auth_methods = array_merge(array($name => $callback),
+                                              $this->auth_methods);
+        } else {
+            $this->auth_methods[$name] = $callback;
+        }
+
+        return true;
+    }
+
+    /**
+     * Authenticates the user using the DIGEST-MD5 method.
+     *
+     * @param string The userid to authenticate as.
+     * @param string The password to authenticate with.
+     * @param string The optional authorization proxy identifier.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access private
+     * @since  1.1.0
+     */
+    function _authDigest_MD5($uid, $pwd, $authz = '')
+    {
+        if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) {
+            return $error;
+        }
+        /* 334: Continue authentication request */
+        if (PEAR::isError($error = $this->_parseResponse(334))) {
+            /* 503: Error: already authenticated */
+            if ($this->_code === 503) {
+                return true;
+            }
+            return $error;
+        }
+
+        $challenge = base64_decode($this->_arguments[0]);
+        $digest = &Auth_SASL::factory('digestmd5');
+        $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,
+                                                       $this->host, "smtp",
+                                                       $authz));
+
+        if (PEAR::isError($error = $this->_put($auth_str))) {
+            return $error;
+        }
+        /* 334: Continue authentication request */
+        if (PEAR::isError($error = $this->_parseResponse(334))) {
+            return $error;
+        }
+
+        /* We don't use the protocol's third step because SMTP doesn't
+         * allow subsequent authentication, so we just silently ignore
+         * it. */
+        if (PEAR::isError($error = $this->_put(''))) {
+            return $error;
+        }
+        /* 235: Authentication successful */
+        if (PEAR::isError($error = $this->_parseResponse(235))) {
+            return $error;
+        }
+    }
+
+    /**
+     * Authenticates the user using the CRAM-MD5 method.
+     *
+     * @param string The userid to authenticate as.
+     * @param string The password to authenticate with.
+     * @param string The optional authorization proxy identifier.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access private
+     * @since  1.1.0
+     */
+    function _authCRAM_MD5($uid, $pwd, $authz = '')
+    {
+        if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) {
+            return $error;
+        }
+        /* 334: Continue authentication request */
+        if (PEAR::isError($error = $this->_parseResponse(334))) {
+            /* 503: Error: already authenticated */
+            if ($this->_code === 503) {
+                return true;
+            }
+            return $error;
+        }
+
+        $challenge = base64_decode($this->_arguments[0]);
+        $cram = &Auth_SASL::factory('crammd5');
+        $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge));
+
+        if (PEAR::isError($error = $this->_put($auth_str))) {
+            return $error;
+        }
+
+        /* 235: Authentication successful */
+        if (PEAR::isError($error = $this->_parseResponse(235))) {
+            return $error;
+        }
+    }
+
+    /**
+     * Authenticates the user using the LOGIN method.
+     *
+     * @param string The userid to authenticate as.
+     * @param string The password to authenticate with.
+     * @param string The optional authorization proxy identifier.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access private
+     * @since  1.1.0
+     */
+    function _authLogin($uid, $pwd, $authz = '')
+    {
+        if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) {
+            return $error;
+        }
+        /* 334: Continue authentication request */
+        if (PEAR::isError($error = $this->_parseResponse(334))) {
+            /* 503: Error: already authenticated */
+            if ($this->_code === 503) {
+                return true;
+            }
+            return $error;
+        }
+
+        if (PEAR::isError($error = $this->_put(base64_encode($uid)))) {
+            return $error;
+        }
+        /* 334: Continue authentication request */
+        if (PEAR::isError($error = $this->_parseResponse(334))) {
+            return $error;
+        }
+
+        if (PEAR::isError($error = $this->_put(base64_encode($pwd)))) {
+            return $error;
+        }
+
+        /* 235: Authentication successful */
+        if (PEAR::isError($error = $this->_parseResponse(235))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Authenticates the user using the PLAIN method.
+     *
+     * @param string The userid to authenticate as.
+     * @param string The password to authenticate with.
+     * @param string The optional authorization proxy identifier.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access private
+     * @since  1.1.0
+     */
+    function _authPlain($uid, $pwd, $authz = '')
+    {
+        if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) {
+            return $error;
+        }
+        /* 334: Continue authentication request */
+        if (PEAR::isError($error = $this->_parseResponse(334))) {
+            /* 503: Error: already authenticated */
+            if ($this->_code === 503) {
+                return true;
+            }
+            return $error;
+        }
+
+        $auth_str = base64_encode($authz . chr(0) . $uid . chr(0) . $pwd);
+
+        if (PEAR::isError($error = $this->_put($auth_str))) {
+            return $error;
+        }
+
+        /* 235: Authentication successful */
+        if (PEAR::isError($error = $this->_parseResponse(235))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Send the HELO command.
+     *
+     * @param string The domain name to say we are.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.0
+     */
+    function helo($domain)
+    {
+        if (PEAR::isError($error = $this->_put('HELO', $domain))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(250))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Return the list of SMTP service extensions advertised by the server.
+     *
+     * @return array The list of SMTP service extensions.
+     * @access public
+     * @since 1.3
+     */
+    function getServiceExtensions()
+    {
+        return $this->_esmtp;
+    }
+
+    /**
+     * Send the MAIL FROM: command.
+     *
+     * @param string $sender    The sender (reverse path) to set.
+     * @param string $params    String containing additional MAIL parameters,
+     *                          such as the NOTIFY flags defined by RFC 1891
+     *                          or the VERP protocol.
+     *
+     *                          If $params is an array, only the 'verp' option
+     *                          is supported.  If 'verp' is true, the XVERP
+     *                          parameter is appended to the MAIL command.  If
+     *                          the 'verp' value is a string, the full
+     *                          XVERP=value parameter is appended.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.0
+     */
+    function mailFrom($sender, $params = null)
+    {
+        $args = "FROM:<$sender>";
+
+        /* Support the deprecated array form of $params. */
+        if (is_array($params) && isset($params['verp'])) {
+            /* XVERP */
+            if ($params['verp'] === true) {
+                $args .= ' XVERP';
+
+            /* XVERP=something */
+            } elseif (trim($params['verp'])) {
+                $args .= ' XVERP=' . $params['verp'];
+            }
+        } elseif (is_string($params) && !empty($params)) {
+            $args .= ' ' . $params;
+        }
+
+        if (PEAR::isError($error = $this->_put('MAIL', $args))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Send the RCPT TO: command.
+     *
+     * @param string $recipient The recipient (forward path) to add.
+     * @param string $params    String containing additional RCPT parameters,
+     *                          such as the NOTIFY flags defined by RFC 1891.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     *
+     * @access public
+     * @since  1.0
+     */
+    function rcptTo($recipient, $params = null)
+    {
+        $args = "TO:<$recipient>";
+        if (is_string($params)) {
+            $args .= ' ' . $params;
+        }
+
+        if (PEAR::isError($error = $this->_put('RCPT', $args))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(array(250, 251), $this->pipelining))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Quote the data so that it meets SMTP standards.
+     *
+     * This is provided as a separate public function to facilitate
+     * easier overloading for the cases where it is desirable to
+     * customize the quoting behavior.
+     *
+     * @param string $data  The message text to quote. The string must be passed
+     *                      by reference, and the text will be modified in place.
+     *
+     * @access public
+     * @since  1.2
+     */
+    function quotedata(&$data)
+    {
+        /* Change Unix (\n) and Mac (\r) linefeeds into
+         * Internet-standard CRLF (\r\n) linefeeds. */
+        $data = preg_replace(array('/(?<!\r)\n/','/\r(?!\n)/'), "\r\n", $data);
+
+        /* Because a single leading period (.) signifies an end to the
+         * data, legitimate leading periods need to be "doubled"
+         * (e.g. '..'). */
+        $data = str_replace("\n.", "\n..", $data);
+    }
+
+    /**
+     * Send the DATA command.
+     *
+     * @param mixed $data     The message data, either as a string or an open
+     *                        file resource.
+     * @param string $headers The message headers.  If $headers is provided,
+     *                        $data is assumed to contain only body data.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.0
+     */
+    function data($data, $headers = null)
+    {
+        /* Verify that $data is a supported type. */
+        if (!is_string($data) && !is_resource($data)) {
+            return PEAR::raiseError('Expected a string or file resource');
+        }
+
+        /* Start by considering the size of the optional headers string.  We
+         * also account for the addition 4 character "\r\n\r\n" separator
+         * sequence. */
+        $size = (is_null($headers)) ? 0 : strlen($headers) + 4;
+
+        if (is_resource($data)) {
+            $stat = fstat($data);
+            if ($stat === false) {
+                return PEAR::raiseError('Failed to get file size');
+            }
+            $size += $stat['size'];
+        } else {
+            $size += strlen($data);
+        }
+
+        /* RFC 1870, section 3, subsection 3 states "a value of zero indicates
+         * that no fixed maximum message size is in force".  Furthermore, it
+         * says that if "the parameter is omitted no information is conveyed
+         * about the server's fixed maximum message size". */
+        $limit = (isset($this->_esmtp['SIZE'])) ? $this->_esmtp['SIZE'] : 0;
+        if ($limit > 0 && $size >= $limit) {
+            $this->disconnect();
+            return PEAR::raiseError('Message size exceeds server limit');
+        }
+
+        /* Initiate the DATA command. */
+        if (PEAR::isError($error = $this->_put('DATA'))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(354))) {
+            return $error;
+        }
+
+        /* If we have a separate headers string, send it first. */
+        if (!is_null($headers)) {
+            $this->quotedata($headers);
+            if (PEAR::isError($result = $this->_send($headers . "\r\n\r\n"))) {
+                return $result;
+            }
+        }
+
+        /* Now we can send the message body data. */
+        if (is_resource($data)) {
+            /* Stream the contents of the file resource out over our socket 
+             * connection, line by line.  Each line must be run through the 
+             * quoting routine. */
+            while (strlen($line = fread($data, 8192)) > 0) {
+                /* If the last character is an newline, we need to grab the
+                 * next character to check to see if it is a period. */
+                while (!feof($data)) {
+                    $char = fread($data, 1);
+                    $line .= $char;
+                    if ($char != "\n") {
+                        break;
+                    }
+                }
+                $this->quotedata($line);
+                if (PEAR::isError($result = $this->_send($line))) {
+                    return $result;
+                }
+            }
+        } else {
+            /*
+             * Break up the data by sending one chunk (up to 512k) at a time.  
+             * This approach reduces our peak memory usage.
+             */
+            for ($offset = 0; $offset < $size;) {
+                $end = $offset + 512000;
+
+                /*
+                 * Ensure we don't read beyond our data size or span multiple 
+                 * lines.  quotedata() can't properly handle character data 
+                 * that's split across two line break boundaries.
+                 */
+                if ($end >= $size) {
+                    $end = $size;
+                } else {
+                    for (; $end < $size; $end++) {
+                        if ($data[$end] != "\n") {
+                            break;
+                        }
+                    }
+                }
+
+                /* Extract our chunk and run it through the quoting routine. */
+                $chunk = substr($data, $offset, $end - $offset);
+                $this->quotedata($chunk);
+
+                /* If we run into a problem along the way, abort. */
+                if (PEAR::isError($result = $this->_send($chunk))) {
+                    return $result;
+                }
+
+                /* Advance the offset to the end of this chunk. */
+                $offset = $end;
+            }
+        }
+
+        /* Finally, send the DATA terminator sequence. */
+        if (PEAR::isError($result = $this->_send("\r\n.\r\n"))) {
+            return $result;
+        }
+
+        /* Verify that the data was successfully received by the server. */
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Send the SEND FROM: command.
+     *
+     * @param string The reverse path to send.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.2.6
+     */
+    function sendFrom($path)
+    {
+        if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Backwards-compatibility wrapper for sendFrom().
+     *
+     * @param string The reverse path to send.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     *
+     * @access      public
+     * @since       1.0
+     * @deprecated  1.2.6
+     */
+    function send_from($path)
+    {
+        return sendFrom($path);
+    }
+
+    /**
+     * Send the SOML FROM: command.
+     *
+     * @param string The reverse path to send.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.2.6
+     */
+    function somlFrom($path)
+    {
+        if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Backwards-compatibility wrapper for somlFrom().
+     *
+     * @param string The reverse path to send.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     *
+     * @access      public
+     * @since       1.0
+     * @deprecated  1.2.6
+     */
+    function soml_from($path)
+    {
+        return somlFrom($path);
+    }
+
+    /**
+     * Send the SAML FROM: command.
+     *
+     * @param string The reverse path to send.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.2.6
+     */
+    function samlFrom($path)
+    {
+        if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Backwards-compatibility wrapper for samlFrom().
+     *
+     * @param string The reverse path to send.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     *
+     * @access      public
+     * @since       1.0
+     * @deprecated  1.2.6
+     */
+    function saml_from($path)
+    {
+        return samlFrom($path);
+    }
+
+    /**
+     * Send the RSET command.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.0
+     */
+    function rset()
+    {
+        if (PEAR::isError($error = $this->_put('RSET'))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Send the VRFY command.
+     *
+     * @param string The string to verify
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.0
+     */
+    function vrfy($string)
+    {
+        /* Note: 251 is also a valid response code */
+        if (PEAR::isError($error = $this->_put('VRFY', $string))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(array(250, 252)))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Send the NOOP command.
+     *
+     * @return mixed Returns a PEAR_Error with an error message on any
+     *               kind of failure, or true on success.
+     * @access public
+     * @since  1.0
+     */
+    function noop()
+    {
+        if (PEAR::isError($error = $this->_put('NOOP'))) {
+            return $error;
+        }
+        if (PEAR::isError($error = $this->_parseResponse(250))) {
+            return $error;
+        }
+
+        return true;
+    }
+
+    /**
+     * Backwards-compatibility method.  identifySender()'s functionality is
+     * now handled internally.
+     *
+     * @return  boolean     This method always return true.
+     *
+     * @access  public
+     * @since   1.0
+     */
+    function identifySender()
+    {
+        return true;
+    }
+
+}
Index: trunk/library/Net/Socket.php
===================================================================
--- trunk/library/Net/Socket.php	(revision 5146)
+++ trunk/library/Net/Socket.php	(revision 5146)
@@ -0,0 +1,653 @@
+<?php
+/**
+ * Net_Socket
+ *
+ * PHP Version 4
+ *
+ * Copyright (c) 1997-2003 The PHP Group
+ *
+ * This source file is subject to version 2.0 of the PHP license,
+ * that is bundled with this package in the file LICENSE, and is
+ * available at through the world-wide-web at
+ * http://www.php.net/license/2_02.txt.
+ * If you did not receive a copy of the PHP license and are unable to
+ * obtain it through the world-wide-web, please send a note to
+ * license@php.net so we can mail you a copy immediately.
+ *
+ * Authors: Stig Bakken <ssb@php.net>
+ *          Chuck Hagenbuch <chuck@horde.org>
+ *
+ * @category  Net
+ * @package   Net_Socket
+ * @author    Stig Bakken <ssb@php.net>
+ * @author    Chuck Hagenbuch <chuck@horde.org>
+ * @copyright 1997-2003 The PHP Group
+ * @license   http://www.php.net/license/2_02.txt PHP 2.02
+ * @version   CVS: $Id: Socket.php 304428 2010-10-15 13:51:46Z clockwerx $
+ * @link      http://pear.php.net/packages/Net_Socket
+ */
+
+require_once $_SESSION['rootPath'].'/library/PEAR/PEAR.php';
+
+define('NET_SOCKET_READ', 1);
+define('NET_SOCKET_WRITE', 2);
+define('NET_SOCKET_ERROR', 4);
+
+/**
+ * Generalized Socket class.
+ *
+ * @category  Net
+ * @package   Net_Socket
+ * @author    Stig Bakken <ssb@php.net>
+ * @author    Chuck Hagenbuch <chuck@horde.org>
+ * @copyright 1997-2003 The PHP Group
+ * @license   http://www.php.net/license/2_02.txt PHP 2.02
+ * @link      http://pear.php.net/packages/Net_Socket
+ */
+class Net_Socket extends PEAR
+{
+    /**
+     * Socket file pointer.
+     * @var resource $fp
+     */
+    var $fp = null;
+
+    /**
+     * Whether the socket is blocking. Defaults to true.
+     * @var boolean $blocking
+     */
+    var $blocking = true;
+
+    /**
+     * Whether the socket is persistent. Defaults to false.
+     * @var boolean $persistent
+     */
+    var $persistent = false;
+
+    /**
+     * The IP address to connect to.
+     * @var string $addr
+     */
+    var $addr = '';
+
+    /**
+     * The port number to connect to.
+     * @var integer $port
+     */
+    var $port = 0;
+
+    /**
+     * Number of seconds to wait on socket connections before assuming
+     * there's no more data. Defaults to no timeout.
+     * @var integer $timeout
+     */
+    var $timeout = false;
+
+    /**
+     * Number of bytes to read at a time in readLine() and
+     * readAll(). Defaults to 2048.
+     * @var integer $lineLength
+     */
+    var $lineLength = 2048;
+
+    /**
+     * The string to use as a newline terminator. Usually "\r\n" or "\n".
+     * @var string $newline
+     */
+    var $newline = "\r\n";
+
+    /**
+     * Connect to the specified port. If called when the socket is
+     * already connected, it disconnects and connects again.
+     *
+     * @param string  $addr       IP address or host name.
+     * @param integer $port       TCP port number.
+     * @param boolean $persistent (optional) Whether the connection is
+     *                            persistent (kept open between requests
+     *                            by the web server).
+     * @param integer $timeout    (optional) How long to wait for data.
+     * @param array   $options    See options for stream_context_create.
+     *
+     * @access public
+     *
+     * @return boolean | PEAR_Error  True on success or a PEAR_Error on failure.
+     */
+    function connect($addr, $port = 0, $persistent = null,
+                     $timeout = null, $options = null)
+    {
+        if (is_resource($this->fp)) {
+            @fclose($this->fp);
+            $this->fp = null;
+        }
+
+        if (!$addr) {
+            return $this->raiseError('$addr cannot be empty');
+        } elseif (strspn($addr, '.0123456789') == strlen($addr) ||
+                  strstr($addr, '/') !== false) {
+            $this->addr = $addr;
+        } else {
+            $this->addr = @gethostbyname($addr);
+        }
+
+        $this->port = $port % 65536;
+
+        if ($persistent !== null) {
+            $this->persistent = $persistent;
+        }
+
+        if ($timeout !== null) {
+            $this->timeout = $timeout;
+        }
+
+        $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
+        $errno    = 0;
+        $errstr   = '';
+
+        $old_track_errors = @ini_set('track_errors', 1);
+
+        if ($options && function_exists('stream_context_create')) {
+            if ($this->timeout) {
+                $timeout = $this->timeout;
+            } else {
+                $timeout = 0;
+            }
+            $context = stream_context_create($options);
+
+            // Since PHP 5 fsockopen doesn't allow context specification
+            if (function_exists('stream_socket_client')) {
+                $flags = STREAM_CLIENT_CONNECT;
+
+                if ($this->persistent) {
+                    $flags = STREAM_CLIENT_PERSISTENT;
+                }
+
+                $addr = $this->addr . ':' . $this->port;
+                $fp   = stream_socket_client($addr, $errno, $errstr,
+                                             $timeout, $flags, $context);
+            } else {
+                $fp = @$openfunc($this->addr, $this->port, $errno,
+                                 $errstr, $timeout, $context);
+            }
+        } else {
+            if ($this->timeout) {
+                $fp = @$openfunc($this->addr, $this->port, $errno,
+                                 $errstr, $this->timeout);
+            } else {
+                $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
+            }
+        }
+
+        if (!$fp) {
+            if ($errno == 0 && !strlen($errstr) && isset($php_errormsg)) {
+                $errstr = $php_errormsg;
+            }
+            @ini_set('track_errors', $old_track_errors);
+            return $this->raiseError($errstr, $errno);
+        }
+
+        @ini_set('track_errors', $old_track_errors);
+        $this->fp = $fp;
+
+        return $this->setBlocking($this->blocking);
+    }
+
+    /**
+     * Disconnects from the peer, closes the socket.
+     *
+     * @access public
+     * @return mixed true on success or a PEAR_Error instance otherwise
+     */
+    function disconnect()
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        @fclose($this->fp);
+        $this->fp = null;
+        return true;
+    }
+
+    /**
+     * Set the newline character/sequence to use.
+     *
+     * @param string $newline  Newline character(s)
+     * @return boolean True
+     */
+    function setNewline($newline)
+    {
+        $this->newline = $newline;
+        return true;
+    }
+
+    /**
+     * Find out if the socket is in blocking mode.
+     *
+     * @access public
+     * @return boolean  The current blocking mode.
+     */
+    function isBlocking()
+    {
+        return $this->blocking;
+    }
+
+    /**
+     * Sets whether the socket connection should be blocking or
+     * not. A read call to a non-blocking socket will return immediately
+     * if there is no data available, whereas it will block until there
+     * is data for blocking sockets.
+     *
+     * @param boolean $mode True for blocking sockets, false for nonblocking.
+     *
+     * @access public
+     * @return mixed true on success or a PEAR_Error instance otherwise
+     */
+    function setBlocking($mode)
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        $this->blocking = $mode;
+        stream_set_blocking($this->fp, (int)$this->blocking);
+        return true;
+    }
+
+    /**
+     * Sets the timeout value on socket descriptor,
+     * expressed in the sum of seconds and microseconds
+     *
+     * @param integer $seconds      Seconds.
+     * @param integer $microseconds Microseconds.
+     *
+     * @access public
+     * @return mixed true on success or a PEAR_Error instance otherwise
+     */
+    function setTimeout($seconds, $microseconds)
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        return socket_set_timeout($this->fp, $seconds, $microseconds);
+    }
+
+    /**
+     * Sets the file buffering size on the stream.
+     * See php's stream_set_write_buffer for more information.
+     *
+     * @param integer $size Write buffer size.
+     *
+     * @access public
+     * @return mixed on success or an PEAR_Error object otherwise
+     */
+    function setWriteBuffer($size)
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        $returned = stream_set_write_buffer($this->fp, $size);
+        if ($returned == 0) {
+            return true;
+        }
+        return $this->raiseError('Cannot set write buffer.');
+    }
+
+    /**
+     * Returns information about an existing socket resource.
+     * Currently returns four entries in the result array:
+     *
+     * <p>
+     * timed_out (bool) - The socket timed out waiting for data<br>
+     * blocked (bool) - The socket was blocked<br>
+     * eof (bool) - Indicates EOF event<br>
+     * unread_bytes (int) - Number of bytes left in the socket buffer<br>
+     * </p>
+     *
+     * @access public
+     * @return mixed Array containing information about existing socket
+     *               resource or a PEAR_Error instance otherwise
+     */
+    function getStatus()
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        return socket_get_status($this->fp);
+    }
+
+    /**
+     * Get a specified line of data
+     *
+     * @param int $size ??
+     *
+     * @access public
+     * @return $size bytes of data from the socket, or a PEAR_Error if
+     *         not connected.
+     */
+    function gets($size = null)
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        if (is_null($size)) {
+            return @fgets($this->fp);
+        } else {
+            return @fgets($this->fp, $size);
+        }
+    }
+
+    /**
+     * Read a specified amount of data. This is guaranteed to return,
+     * and has the added benefit of getting everything in one fread()
+     * chunk; if you know the size of the data you're getting
+     * beforehand, this is definitely the way to go.
+     *
+     * @param integer $size The number of bytes to read from the socket.
+     *
+     * @access public
+     * @return $size bytes of data from the socket, or a PEAR_Error if
+     *         not connected.
+     */
+    function read($size)
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        return @fread($this->fp, $size);
+    }
+
+    /**
+     * Write a specified amount of data.
+     *
+     * @param string  $data      Data to write.
+     * @param integer $blocksize Amount of data to write at once.
+     *                           NULL means all at once.
+     *
+     * @access public
+     * @return mixed If the socket is not connected, returns an instance of
+     *               PEAR_Error
+     *               If the write succeeds, returns the number of bytes written
+     *               If the write fails, returns false.
+     */
+    function write($data, $blocksize = null)
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        if (is_null($blocksize) && !OS_WINDOWS) {
+            return @fwrite($this->fp, $data);
+        } else {
+            if (is_null($blocksize)) {
+                $blocksize = 1024;
+            }
+
+            $pos  = 0;
+            $size = strlen($data);
+            while ($pos < $size) {
+                $written = @fwrite($this->fp, substr($data, $pos, $blocksize));
+                if (!$written) {
+                    return $written;
+                }
+                $pos += $written;
+            }
+
+            return $pos;
+        }
+    }
+
+    /**
+     * Write a line of data to the socket, followed by a trailing newline.
+     *
+     * @param string $data Data to write
+     *
+     * @access public
+     * @return mixed fputs result, or an error
+     */
+    function writeLine($data)
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        return fwrite($this->fp, $data . $this->newline);
+    }
+
+    /**
+     * Tests for end-of-file on a socket descriptor.
+     *
+     * Also returns true if the socket is disconnected.
+     *
+     * @access public
+     * @return bool
+     */
+    function eof()
+    {
+        return (!is_resource($this->fp) || feof($this->fp));
+    }
+
+    /**
+     * Reads a byte of data
+     *
+     * @access public
+     * @return 1 byte of data from the socket, or a PEAR_Error if
+     *         not connected.
+     */
+    function readByte()
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        return ord(@fread($this->fp, 1));
+    }
+
+    /**
+     * Reads a word of data
+     *
+     * @access public
+     * @return 1 word of data from the socket, or a PEAR_Error if
+     *         not connected.
+     */
+    function readWord()
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        $buf = @fread($this->fp, 2);
+        return (ord($buf[0]) + (ord($buf[1]) << 8));
+    }
+
+    /**
+     * Reads an int of data
+     *
+     * @access public
+     * @return integer  1 int of data from the socket, or a PEAR_Error if
+     *                  not connected.
+     */
+    function readInt()
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        $buf = @fread($this->fp, 4);
+        return (ord($buf[0]) + (ord($buf[1]) << 8) +
+                (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
+    }
+
+    /**
+     * Reads a zero-terminated string of data
+     *
+     * @access public
+     * @return string, or a PEAR_Error if
+     *         not connected.
+     */
+    function readString()
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        $string = '';
+        while (($char = @fread($this->fp, 1)) != "\x00") {
+            $string .= $char;
+        }
+        return $string;
+    }
+
+    /**
+     * Reads an IP Address and returns it in a dot formatted string
+     *
+     * @access public
+     * @return Dot formatted string, or a PEAR_Error if
+     *         not connected.
+     */
+    function readIPAddress()
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        $buf = @fread($this->fp, 4);
+        return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]),
+                       ord($buf[2]), ord($buf[3]));
+    }
+
+    /**
+     * Read until either the end of the socket or a newline, whichever
+     * comes first. Strips the trailing newline from the returned data.
+     *
+     * @access public
+     * @return All available data up to a newline, without that
+     *         newline, or until the end of the socket, or a PEAR_Error if
+     *         not connected.
+     */
+    function readLine()
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        $line = '';
+
+        $timeout = time() + $this->timeout;
+
+        while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
+            $line .= @fgets($this->fp, $this->lineLength);
+            if (substr($line, -1) == "\n") {
+                return rtrim($line, $this->newline);
+            }
+        }
+        return $line;
+    }
+
+    /**
+     * Read until the socket closes, or until there is no more data in
+     * the inner PHP buffer. If the inner buffer is empty, in blocking
+     * mode we wait for at least 1 byte of data. Therefore, in
+     * blocking mode, if there is no data at all to be read, this
+     * function will never exit (unless the socket is closed on the
+     * remote end).
+     *
+     * @access public
+     *
+     * @return string  All data until the socket closes, or a PEAR_Error if
+     *                 not connected.
+     */
+    function readAll()
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        $data = '';
+        while (!feof($this->fp)) {
+            $data .= @fread($this->fp, $this->lineLength);
+        }
+        return $data;
+    }
+
+    /**
+     * Runs the equivalent of the select() system call on the socket
+     * with a timeout specified by tv_sec and tv_usec.
+     *
+     * @param integer $state   Which of read/write/error to check for.
+     * @param integer $tv_sec  Number of seconds for timeout.
+     * @param integer $tv_usec Number of microseconds for timeout.
+     *
+     * @access public
+     * @return False if select fails, integer describing which of read/write/error
+     *         are ready, or PEAR_Error if not connected.
+     */
+    function select($state, $tv_sec, $tv_usec = 0)
+    {
+        if (!is_resource($this->fp)) {
+            return $this->raiseError('not connected');
+        }
+
+        $read   = null;
+        $write  = null;
+        $except = null;
+        if ($state & NET_SOCKET_READ) {
+            $read[] = $this->fp;
+        }
+        if ($state & NET_SOCKET_WRITE) {
+            $write[] = $this->fp;
+        }
+        if ($state & NET_SOCKET_ERROR) {
+            $except[] = $this->fp;
+        }
+        if (false === ($sr = stream_select($read, $write, $except,
+                                          $tv_sec, $tv_usec))) {
+            return false;
+        }
+
+        $result = 0;
+        if (count($read)) {
+            $result |= NET_SOCKET_READ;
+        }
+        if (count($write)) {
+            $result |= NET_SOCKET_WRITE;
+        }
+        if (count($except)) {
+            $result |= NET_SOCKET_ERROR;
+        }
+        return $result;
+    }
+
+    /**
+     * Turns encryption on/off on a connected socket.
+     *
+     * @param bool    $enabled Set this parameter to true to enable encryption
+     *                         and false to disable encryption.
+     * @param integer $type    Type of encryption. See stream_socket_enable_crypto()
+     *                         for values.
+     *
+     * @see    http://se.php.net/manual/en/function.stream-socket-enable-crypto.php
+     * @access public
+     * @return false on error, true on success and 0 if there isn't enough data
+     *         and the user should try again (non-blocking sockets only).
+     *         A PEAR_Error object is returned if the socket is not
+     *         connected
+     */
+    function enableCrypto($enabled, $type)
+    {
+        if (version_compare(phpversion(), "5.1.0", ">=")) {
+            if (!is_resource($this->fp)) {
+                return $this->raiseError('not connected');
+            }
+            return @stream_socket_enable_crypto($this->fp, $enabled, $type);
+        } else {
+            $msg = 'Net_Socket::enableCrypto() requires php version >= 5.1.0';
+            return $this->raiseError($msg);
+        }
+    }
+
+}
Index: trunk/library/PEAR/OS/Guess.php
===================================================================
--- trunk/library/PEAR/OS/Guess.php	(revision 5146)
+++ trunk/library/PEAR/OS/Guess.php	(revision 5146)
@@ -0,0 +1,338 @@
+<?php
+/**
+ * The OS_Guess class
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Gregory Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Guess.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since PEAR 0.1
+ */
+
+// {{{ uname examples
+
+// php_uname() without args returns the same as 'uname -a', or a PHP-custom
+// string for Windows.
+// PHP versions prior to 4.3 return the uname of the host where PHP was built,
+// as of 4.3 it returns the uname of the host running the PHP code.
+//
+// PC RedHat Linux 7.1:
+// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown
+//
+// PC Debian Potato:
+// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown
+//
+// PC FreeBSD 3.3:
+// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000     root@example.com:/usr/src/sys/compile/CONFIG  i386
+//
+// PC FreeBSD 4.3:
+// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001     root@example.com:/usr/src/sys/compile/CONFIG  i386
+//
+// PC FreeBSD 4.5:
+// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb  6 23:59:23 CET 2002     root@example.com:/usr/src/sys/compile/CONFIG  i386
+//
+// PC FreeBSD 4.5 w/uname from GNU shellutils:
+// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb  i386 unknown
+//
+// HP 9000/712 HP-UX 10:
+// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license
+//
+// HP 9000/712 HP-UX 10 w/uname from GNU shellutils:
+// HP-UX host B.10.10 A 9000/712 unknown
+//
+// IBM RS6000/550 AIX 4.3:
+// AIX host 3 4 000003531C00
+//
+// AIX 4.3 w/uname from GNU shellutils:
+// AIX host 3 4 000003531C00 unknown
+//
+// SGI Onyx IRIX 6.5 w/uname from GNU shellutils:
+// IRIX64 host 6.5 01091820 IP19 mips
+//
+// SGI Onyx IRIX 6.5:
+// IRIX64 host 6.5 01091820 IP19
+//
+// SparcStation 20 Solaris 8 w/uname from GNU shellutils:
+// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc
+//
+// SparcStation 20 Solaris 8:
+// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20
+//
+// Mac OS X (Darwin)
+// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug  5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC  Power Macintosh
+//
+// Mac OS X early versions
+//
+
+// }}}
+
+/* TODO:
+ * - define endianness, to allow matchSignature("bigend") etc.
+ */
+
+/**
+ * Retrieves information about the current operating system
+ *
+ * This class uses php_uname() to grok information about the current OS
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Gregory Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class OS_Guess
+{
+    var $sysname;
+    var $nodename;
+    var $cpu;
+    var $release;
+    var $extra;
+
+    function OS_Guess($uname = null)
+    {
+        list($this->sysname,
+             $this->release,
+             $this->cpu,
+             $this->extra,
+             $this->nodename) = $this->parseSignature($uname);
+    }
+
+    function parseSignature($uname = null)
+    {
+        static $sysmap = array(
+            'HP-UX' => 'hpux',
+            'IRIX64' => 'irix',
+        );
+        static $cpumap = array(
+            'i586' => 'i386',
+            'i686' => 'i386',
+            'ppc' => 'powerpc',
+        );
+        if ($uname === null) {
+            $uname = php_uname();
+        }
+        $parts = preg_split('/\s+/', trim($uname));
+        $n = count($parts);
+
+        $release  = $machine = $cpu = '';
+        $sysname  = $parts[0];
+        $nodename = $parts[1];
+        $cpu      = $parts[$n-1];
+        $extra = '';
+        if ($cpu == 'unknown') {
+            $cpu = $parts[$n - 2];
+        }
+
+        switch ($sysname) {
+            case 'AIX' :
+                $release = "$parts[3].$parts[2]";
+                break;
+            case 'Windows' :
+                switch ($parts[1]) {
+                    case '95/98':
+                        $release = '9x';
+                        break;
+                    default:
+                        $release = $parts[1];
+                        break;
+                }
+                $cpu = 'i386';
+                break;
+            case 'Linux' :
+                $extra = $this->_detectGlibcVersion();
+                // use only the first two digits from the kernel version
+                $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]);
+                break;
+            case 'Mac' :
+                $sysname = 'darwin';
+                $nodename = $parts[2];
+                $release = $parts[3];
+                if ($cpu == 'Macintosh') {
+                    if ($parts[$n - 2] == 'Power') {
+                        $cpu = 'powerpc';
+                    }
+                }
+                break;
+            case 'Darwin' :
+                if ($cpu == 'Macintosh') {
+                    if ($parts[$n - 2] == 'Power') {
+                        $cpu = 'powerpc';
+                    }
+                }
+                $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]);
+                break;
+            default:
+                $release = preg_replace('/-.*/', '', $parts[2]);
+                break;
+        }
+
+        if (isset($sysmap[$sysname])) {
+            $sysname = $sysmap[$sysname];
+        } else {
+            $sysname = strtolower($sysname);
+        }
+        if (isset($cpumap[$cpu])) {
+            $cpu = $cpumap[$cpu];
+        }
+        return array($sysname, $release, $cpu, $extra, $nodename);
+    }
+
+    function _detectGlibcVersion()
+    {
+        static $glibc = false;
+        if ($glibc !== false) {
+            return $glibc; // no need to run this multiple times
+        }
+        $major = $minor = 0;
+        include_once "System.php";
+        // Use glibc's <features.h> header file to
+        // get major and minor version number:
+        if (@file_exists('/usr/include/features.h') &&
+              @is_readable('/usr/include/features.h')) {
+            if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) {
+                $features_file = fopen('/usr/include/features.h', 'rb');
+                while (!feof($features_file)) {
+                    $line = fgets($features_file, 8192);
+                    if (!$line || (strpos($line, '#define') === false)) {
+                        continue;
+                    }
+                    if (strpos($line, '__GLIBC__')) {
+                        // major version number #define __GLIBC__ version
+                        $line = preg_split('/\s+/', $line);
+                        $glibc_major = trim($line[2]);
+                        if (isset($glibc_minor)) {
+                            break;
+                        }
+                        continue;
+                    }
+
+                    if (strpos($line, '__GLIBC_MINOR__'))  {
+                        // got the minor version number
+                        // #define __GLIBC_MINOR__ version
+                        $line = preg_split('/\s+/', $line);
+                        $glibc_minor = trim($line[2]);
+                        if (isset($glibc_major)) {
+                            break;
+                        }
+                        continue;
+                    }
+                }
+                fclose($features_file);
+                if (!isset($glibc_major) || !isset($glibc_minor)) {
+                    return $glibc = '';
+                }
+                return $glibc = 'glibc' . trim($glibc_major) . "." . trim($glibc_minor) ;
+            } // no cpp
+
+            $tmpfile = System::mktemp("glibctest");
+            $fp = fopen($tmpfile, "w");
+            fwrite($fp, "#include <features.h>\n__GLIBC__ __GLIBC_MINOR__\n");
+            fclose($fp);
+            $cpp = popen("/usr/bin/cpp $tmpfile", "r");
+            while ($line = fgets($cpp, 1024)) {
+                if ($line{0} == '#' || trim($line) == '') {
+                    continue;
+                }
+
+                if (list($major, $minor) = explode(' ', trim($line))) {
+                    break;
+                }
+            }
+            pclose($cpp);
+            unlink($tmpfile);
+        } // features.h
+
+        if (!($major && $minor) && @is_link('/lib/libc.so.6')) {
+            // Let's try reading the libc.so.6 symlink
+            if (preg_match('/^libc-(.*)\.so$/', basename(readlink('/lib/libc.so.6')), $matches)) {
+                list($major, $minor) = explode('.', $matches[1]);
+            }
+        }
+
+        if (!($major && $minor)) {
+            return $glibc = '';
+        }
+
+        return $glibc = "glibc{$major}.{$minor}";
+    }
+
+    function getSignature()
+    {
+        if (empty($this->extra)) {
+            return "{$this->sysname}-{$this->release}-{$this->cpu}";
+        }
+        return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}";
+    }
+
+    function getSysname()
+    {
+        return $this->sysname;
+    }
+
+    function getNodename()
+    {
+        return $this->nodename;
+    }
+
+    function getCpu()
+    {
+        return $this->cpu;
+    }
+
+    function getRelease()
+    {
+        return $this->release;
+    }
+
+    function getExtra()
+    {
+        return $this->extra;
+    }
+
+    function matchSignature($match)
+    {
+        $fragments = is_array($match) ? $match : explode('-', $match);
+        $n = count($fragments);
+        $matches = 0;
+        if ($n > 0) {
+            $matches += $this->_matchFragment($fragments[0], $this->sysname);
+        }
+        if ($n > 1) {
+            $matches += $this->_matchFragment($fragments[1], $this->release);
+        }
+        if ($n > 2) {
+            $matches += $this->_matchFragment($fragments[2], $this->cpu);
+        }
+        if ($n > 3) {
+            $matches += $this->_matchFragment($fragments[3], $this->extra);
+        }
+        return ($matches == $n);
+    }
+
+    function _matchFragment($fragment, $value)
+    {
+        if (strcspn($fragment, '*?') < strlen($fragment)) {
+            $reg = '/^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '\\z/';
+            return preg_match($reg, $value);
+        }
+        return ($fragment == '*' || !strcasecmp($fragment, $value));
+    }
+
+}
+/*
+ * Local Variables:
+ * indent-tabs-mode: nil
+ * c-basic-offset: 4
+ * End:
+ */
Index: trunk/library/PEAR/LICENSE
===================================================================
--- trunk/library/PEAR/LICENSE	(revision 5146)
+++ trunk/library/PEAR/LICENSE	(revision 5146)
@@ -0,0 +1,27 @@
+Copyright (c) 1997-2009,
+ Stig Bakken <ssb@php.net>,
+ Gregory Beaver <cellog@php.net>,
+ Helgi Ãormar ÃorbjÃ¶rnsson <helgi@php.net>,
+ Tomas V.V.Cox <cox@idecnet.com>,
+ Martin Jansen <mj@php.net>.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Index: trunk/library/PEAR/PEAR5.php
===================================================================
--- trunk/library/PEAR/PEAR5.php	(revision 5146)
+++ trunk/library/PEAR/PEAR5.php	(revision 5146)
@@ -0,0 +1,33 @@
+<?php
+/**
+ * This is only meant for PHP 5 to get rid of certain strict warning
+ * that doesn't get hidden since it's in the shutdown function
+ */
+class PEAR5
+{
+    /**
+    * If you have a class that's mostly/entirely static, and you need static
+    * properties, you can use this method to simulate them. Eg. in your method(s)
+    * do this: $myVar = &PEAR5::getStaticProperty('myclass', 'myVar');
+    * You MUST use a reference, or they will not persist!
+    *
+    * @access public
+    * @param  string $class  The calling classname, to prevent clashes
+    * @param  string $var    The variable to retrieve.
+    * @return mixed   A reference to the variable. If not set it will be
+    *                 auto initialised to NULL.
+    */
+    static function &getStaticProperty($class, $var)
+    {
+        static $properties;
+        if (!isset($properties[$class])) {
+            $properties[$class] = array();
+        }
+
+        if (!array_key_exists($var, $properties[$class])) {
+            $properties[$class][$var] = null;
+        }
+
+        return $properties[$class][$var];
+    }
+}
Index: trunk/library/PEAR/template.spec
===================================================================
--- trunk/library/PEAR/template.spec	(revision 5146)
+++ trunk/library/PEAR/template.spec	(revision 5146)
@@ -0,0 +1,72 @@
+Summary: PEAR: @summary@
+Name: @rpm_package@
+Version: @version@
+Release: 1
+License: @release_license@
+Group: Development/Libraries
+Source: http://@master_server@/get/@package@-%{version}.tgz
+BuildRoot: %{_tmppath}/%{name}-root
+URL: http://@master_server@/package/@package@
+Prefix: %{_prefix}
+BuildArchitectures: @arch@
+@extra_headers@
+
+%description
+@description@
+
+%prep
+rm -rf %{buildroot}/*
+%setup -c -T
+# XXX Source files location is missing here in pear cmd
+pear -v -c %{buildroot}/pearrc \
+        -d php_dir=%{_libdir}/php/pear \
+        -d doc_dir=/docs \
+        -d bin_dir=%{_bindir} \
+        -d data_dir=%{_libdir}/php/pear/data \
+        -d test_dir=%{_libdir}/php/pear/tests \
+        -d ext_dir=%{_libdir} \@extra_config@
+        -s
+
+%build
+echo BuildRoot=%{buildroot}
+
+%postun
+# if refcount = 0 then package has been removed (not upgraded)
+if [ "$1" -eq "0" ]; then
+    pear uninstall --nodeps -r @possible_channel@@package@
+    rm @rpm_xml_dir@/@package@.xml
+fi
+
+
+%post
+# if refcount = 2 then package has been upgraded
+if [ "$1" -ge "2" ]; then
+    pear upgrade --nodeps -r @rpm_xml_dir@/@package@.xml
+else
+    pear install --nodeps -r @rpm_xml_dir@/@package@.xml
+fi
+
+%install
+pear -c %{buildroot}/pearrc install --nodeps -R %{buildroot} \
+        $RPM_SOURCE_DIR/@package@-%{version}.tgz
+rm %{buildroot}/pearrc
+rm %{buildroot}/%{_libdir}/php/pear/.filemap
+rm %{buildroot}/%{_libdir}/php/pear/.lock
+rm -rf %{buildroot}/%{_libdir}/php/pear/.registry
+if [ "@doc_files@" != "" ]; then
+     mv %{buildroot}/docs/@package@/* .
+     rm -rf %{buildroot}/docs
+fi
+mkdir -p %{buildroot}@rpm_xml_dir@
+tar -xzf $RPM_SOURCE_DIR/@package@-%{version}.tgz package@package2xml@.xml
+cp -p package@package2xml@.xml %{buildroot}@rpm_xml_dir@/@package@.xml
+
+#rm -rf %{buildroot}/*
+#pear -q install -R %{buildroot} -n package@package2xml@.xml
+#mkdir -p %{buildroot}@rpm_xml_dir@
+#cp -p package@package2xml@.xml %{buildroot}@rpm_xml_dir@/@package@.xml
+
+%files
+    %defattr(-,root,root)
+    %doc @doc_files@
+    /
Index: trunk/library/PEAR/package.dtd
===================================================================
--- trunk/library/PEAR/package.dtd	(revision 5146)
+++ trunk/library/PEAR/package.dtd	(revision 5146)
@@ -0,0 +1,103 @@
+<!--
+     $Id: package.dtd,v 1.38 2005-11-12 02:23:07 cellog Exp $
+
+     This is the PEAR package description, version 1.0.
+     It should be used with the informal public identifier:
+
+         "-//PHP Group//DTD PEAR Package 1.0//EN//XML"
+
+     Copyright (c) 1997-2005 The PHP Group             
+
+     This source file is subject to version 3.00 of the PHP license,
+     that is bundled with this package in the file LICENSE, and is
+     available at through the world-wide-web at
+     http://www.php.net/license/3_0.txt. 
+     If you did not receive a copy of the PHP license and are unable to
+     obtain it through the world-wide-web, please send a note to
+     license@php.net so we can mail you a copy immediately.
+
+     Authors:
+         Stig S. Bakken <ssb@fast.no>
+         Gregory Beaver <cellog@php.net>
+
+  -->
+<!ENTITY % NUMBER "CDATA">
+<!ELEMENT package (name,summary,description,license?,maintainers,release,changelog?)>
+<!ATTLIST package type    (source|binary|empty) "empty"
+                  version CDATA                 #REQUIRED
+                  packagerversion CDATA         #IMPLIED>
+
+<!ELEMENT name (#PCDATA)>
+
+<!ELEMENT summary (#PCDATA)>
+
+<!ELEMENT license (#PCDATA)>
+
+<!ELEMENT description (#PCDATA)>
+
+<!ELEMENT maintainers (maintainer)+>
+
+<!ELEMENT maintainer (user|role|name|email)+>
+
+<!ELEMENT user (#PCDATA)>
+
+<!ELEMENT role (#PCDATA)>
+
+<!ELEMENT email (#PCDATA)>
+
+<!ELEMENT changelog (release)+>
+
+<!ELEMENT release (version,date,license,state,notes,warnings?,provides*,deps?,configureoptions?,filelist?)>
+
+<!ELEMENT version (#PCDATA)>
+
+<!ELEMENT date (#PCDATA)>
+
+<!ELEMENT state (#PCDATA)>
+
+<!ELEMENT notes (#PCDATA)>
+
+<!ELEMENT warnings (#PCDATA)>
+
+<!ELEMENT deps (dep*)>
+
+<!ELEMENT dep (#PCDATA)>
+<!ATTLIST dep type    (pkg|ext|php) #REQUIRED
+	                       rel     (has|eq|lt|le|gt|ge)                          #IMPLIED
+	                       version CDATA                                     #IMPLIED
+	                       optional (yes|no)     'no'>
+
+<!ELEMENT configureoptions (configureoption)+>
+
+<!ELEMENT configureoption EMPTY>
+<!ATTLIST configureoption name CDATA #REQUIRED
+                                           default CDATA #IMPLIED
+                                           prompt CDATA #REQUIRED>
+
+<!ELEMENT provides EMPTY>
+<!ATTLIST provides type (ext|prog|class|function|feature|api) #REQUIRED
+                                name CDATA #REQUIRED
+                                extends CDATA #IMPLIED>
+<!ELEMENT filelist (dir|file)+>
+
+<!ELEMENT dir (dir|file)+>
+<!ATTLIST dir name           CDATA #REQUIRED
+              role           (php|ext|src|test|doc|data|script) 'php'
+              baseinstalldir CDATA #IMPLIED>
+
+<!ELEMENT file (replace*)>
+<!ATTLIST file role           (php|ext|src|test|doc|data|script) 'php'
+               debug          (na|on|off)        'na'
+               format         CDATA              #IMPLIED
+               baseinstalldir CDATA              #IMPLIED
+               platform       CDATA              #IMPLIED
+               md5sum         CDATA              #IMPLIED
+               name           CDATA              #REQUIRED
+               install-as     CDATA              #IMPLIED>
+
+<!ELEMENT replace EMPTY>
+<!ATTLIST replace type (php-const|pear-config|package-info) #REQUIRED
+                              from CDATA #REQUIRED
+                              to CDATA #REQUIRED>
+
+
Index: trunk/library/PEAR/PEAR/Validate.php
===================================================================
--- trunk/library/PEAR/PEAR/Validate.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Validate.php	(revision 5146)
@@ -0,0 +1,629 @@
+<?php
+/**
+ * PEAR_Validate
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Validate.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**#@+
+ * Constants for install stage
+ */
+define('PEAR_VALIDATE_INSTALLING', 1);
+define('PEAR_VALIDATE_UNINSTALLING', 2); // this is not bit-mapped like the others
+define('PEAR_VALIDATE_NORMAL', 3);
+define('PEAR_VALIDATE_DOWNLOADING', 4); // this is not bit-mapped like the others
+define('PEAR_VALIDATE_PACKAGING', 7);
+/**#@-*/
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Validator/PECL.php';
+
+/**
+ * Validation class for package.xml - channel-level advanced validation
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Validate
+{
+    var $packageregex = _PEAR_COMMON_PACKAGE_NAME_PREG;
+    /**
+     * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     */
+    var $_packagexml;
+    /**
+     * @var int one of the PEAR_VALIDATE_* constants
+     */
+    var $_state = PEAR_VALIDATE_NORMAL;
+    /**
+     * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same)
+     * @var array
+     * @access private
+     */
+    var $_failures = array('error' => array(), 'warning' => array());
+
+    /**
+     * Override this method to handle validation of normal package names
+     * @param string
+     * @return bool
+     * @access protected
+     */
+    function _validPackageName($name)
+    {
+        return (bool) preg_match('/^' . $this->packageregex . '\\z/', $name);
+    }
+
+    /**
+     * @param string package name to validate
+     * @param string name of channel-specific validation package
+     * @final
+     */
+    function validPackageName($name, $validatepackagename = false)
+    {
+        if ($validatepackagename) {
+            if (strtolower($name) == strtolower($validatepackagename)) {
+                return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*\\z/', $name);
+            }
+        }
+        return $this->_validPackageName($name);
+    }
+
+    /**
+     * This validates a bundle name, and bundle names must conform
+     * to the PEAR naming convention, so the method is final and static.
+     * @param string
+     * @final
+     * @static
+     */
+    function validGroupName($name)
+    {
+        return (bool) preg_match('/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/', $name);
+    }
+
+    /**
+     * Determine whether $state represents a valid stability level
+     * @param string
+     * @return bool
+     * @static
+     * @final
+     */
+    function validState($state)
+    {
+        return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable'));
+    }
+
+    /**
+     * Get a list of valid stability levels
+     * @return array
+     * @static
+     * @final
+     */
+    function getValidStates()
+    {
+        return array('snapshot', 'devel', 'alpha', 'beta', 'stable');
+    }
+
+    /**
+     * Determine whether a version is a properly formatted version number that can be used
+     * by version_compare
+     * @param string
+     * @return bool
+     * @static
+     * @final
+     */
+    function validVersion($ver)
+    {
+        return (bool) preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
+    }
+
+    /**
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     */
+    function setPackageFile(&$pf)
+    {
+        $this->_packagexml = &$pf;
+    }
+
+    /**
+     * @access private
+     */
+    function _addFailure($field, $reason)
+    {
+        $this->_failures['errors'][] = array('field' => $field, 'reason' => $reason);
+    }
+
+    /**
+     * @access private
+     */
+    function _addWarning($field, $reason)
+    {
+        $this->_failures['warnings'][] = array('field' => $field, 'reason' => $reason);
+    }
+
+    function getFailures()
+    {
+        $failures = $this->_failures;
+        $this->_failures = array('warnings' => array(), 'errors' => array());
+        return $failures;
+    }
+
+    /**
+     * @param int one of the PEAR_VALIDATE_* constants
+     */
+    function validate($state = null)
+    {
+        if (!isset($this->_packagexml)) {
+            return false;
+        }
+        if ($state !== null) {
+            $this->_state = $state;
+        }
+        $this->_failures = array('warnings' => array(), 'errors' => array());
+        $this->validatePackageName();
+        $this->validateVersion();
+        $this->validateMaintainers();
+        $this->validateDate();
+        $this->validateSummary();
+        $this->validateDescription();
+        $this->validateLicense();
+        $this->validateNotes();
+        if ($this->_packagexml->getPackagexmlVersion() == '1.0') {
+            $this->validateState();
+            $this->validateFilelist();
+        } elseif ($this->_packagexml->getPackagexmlVersion() == '2.0' ||
+                  $this->_packagexml->getPackagexmlVersion() == '2.1') {
+            $this->validateTime();
+            $this->validateStability();
+            $this->validateDeps();
+            $this->validateMainFilelist();
+            $this->validateReleaseFilelist();
+            //$this->validateGlobalTasks();
+            $this->validateChangelog();
+        }
+        return !((bool) count($this->_failures['errors']));
+    }
+
+    /**
+     * @access protected
+     */
+    function validatePackageName()
+    {
+        if ($this->_state == PEAR_VALIDATE_PACKAGING ||
+              $this->_state == PEAR_VALIDATE_NORMAL) {
+            if (($this->_packagexml->getPackagexmlVersion() == '2.0' ||
+                 $this->_packagexml->getPackagexmlVersion() == '2.1') &&
+                  $this->_packagexml->getExtends()) {
+                $version = $this->_packagexml->getVersion() . '';
+                $name = $this->_packagexml->getPackage();
+                $test = array_shift($a = explode('.', $version));
+                if ($test == '0') {
+                    return true;
+                }
+                $vlen = strlen($test);
+                $majver = substr($name, strlen($name) - $vlen);
+                while ($majver && !is_numeric($majver{0})) {
+                    $majver = substr($majver, 1);
+                }
+                if ($majver != $test) {
+                    $this->_addWarning('package', "package $name extends package " .
+                        $this->_packagexml->getExtends() . ' and so the name should ' .
+                        'have a postfix equal to the major version like "' .
+                        $this->_packagexml->getExtends() . $test . '"');
+                    return true;
+                } elseif (substr($name, 0, strlen($name) - $vlen) !=
+                            $this->_packagexml->getExtends()) {
+                    $this->_addWarning('package', "package $name extends package " .
+                        $this->_packagexml->getExtends() . ' and so the name must ' .
+                        'be an extension like "' . $this->_packagexml->getExtends() .
+                        $test . '"');
+                    return true;
+                }
+            }
+        }
+        if (!$this->validPackageName($this->_packagexml->getPackage())) {
+            $this->_addFailure('name', 'package name "' .
+                $this->_packagexml->getPackage() . '" is invalid');
+            return false;
+        }
+    }
+
+    /**
+     * @access protected
+     */
+    function validateVersion()
+    {
+        if ($this->_state != PEAR_VALIDATE_PACKAGING) {
+            if (!$this->validVersion($this->_packagexml->getVersion())) {
+                $this->_addFailure('version',
+                    'Invalid version number "' . $this->_packagexml->getVersion() . '"');
+            }
+            return false;
+        }
+        $version = $this->_packagexml->getVersion();
+        $versioncomponents = explode('.', $version);
+        if (count($versioncomponents) != 3) {
+            $this->_addWarning('version',
+                'A version number should have 3 decimals (x.y.z)');
+            return true;
+        }
+        $name = $this->_packagexml->getPackage();
+        // version must be based upon state
+        switch ($this->_packagexml->getState()) {
+            case 'snapshot' :
+                return true;
+            case 'devel' :
+                if ($versioncomponents[0] . 'a' == '0a') {
+                    return true;
+                }
+                if ($versioncomponents[0] == 0) {
+                    $versioncomponents[0] = '0';
+                    $this->_addWarning('version',
+                        'version "' . $version . '" should be "' .
+                        implode('.' ,$versioncomponents) . '"');
+                } else {
+                    $this->_addWarning('version',
+                        'packages with devel stability must be < version 1.0.0');
+                }
+                return true;
+            break;
+            case 'alpha' :
+            case 'beta' :
+                // check for a package that extends a package,
+                // like Foo and Foo2
+                if ($this->_state == PEAR_VALIDATE_PACKAGING) {
+                    if (substr($versioncomponents[2], 1, 2) == 'rc') {
+                        $this->_addFailure('version', 'Release Candidate versions ' .
+                            'must have capital RC, not lower-case rc');
+                        return false;
+                    }
+                }
+                if (!$this->_packagexml->getExtends()) {
+                    if ($versioncomponents[0] == '1') {
+                        if ($versioncomponents[2]{0} == '0') {
+                            if ($versioncomponents[2] == '0') {
+                                // version 1.*.0000
+                                $this->_addWarning('version',
+                                    'version 1.' . $versioncomponents[1] .
+                                        '.0 probably should not be alpha or beta');
+                                return true;
+                            } elseif (strlen($versioncomponents[2]) > 1) {
+                                // version 1.*.0RC1 or 1.*.0beta24 etc.
+                                return true;
+                            } else {
+                                // version 1.*.0
+                                $this->_addWarning('version',
+                                    'version 1.' . $versioncomponents[1] .
+                                        '.0 probably should not be alpha or beta');
+                                return true;
+                            }
+                        } else {
+                            $this->_addWarning('version',
+                                'bugfix versions (1.3.x where x > 0) probably should ' .
+                                'not be alpha or beta');
+                            return true;
+                        }
+                    } elseif ($versioncomponents[0] != '0') {
+                        $this->_addWarning('version',
+                            'major versions greater than 1 are not allowed for packages ' .
+                            'without an <extends> tag or an identical postfix (foo2 v2.0.0)');
+                        return true;
+                    }
+                    if ($versioncomponents[0] . 'a' == '0a') {
+                        return true;
+                    }
+                    if ($versioncomponents[0] == 0) {
+                        $versioncomponents[0] = '0';
+                        $this->_addWarning('version',
+                            'version "' . $version . '" should be "' .
+                            implode('.' ,$versioncomponents) . '"');
+                    }
+                } else {
+                    $vlen = strlen($versioncomponents[0] . '');
+                    $majver = substr($name, strlen($name) - $vlen);
+                    while ($majver && !is_numeric($majver{0})) {
+                        $majver = substr($majver, 1);
+                    }
+                    if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
+                        $this->_addWarning('version', 'first version number "' .
+                            $versioncomponents[0] . '" must match the postfix of ' .
+                            'package name "' . $name . '" (' .
+                            $majver . ')');
+                        return true;
+                    }
+                    if ($versioncomponents[0] == $majver) {
+                        if ($versioncomponents[2]{0} == '0') {
+                            if ($versioncomponents[2] == '0') {
+                                // version 2.*.0000
+                                $this->_addWarning('version',
+                                    "version $majver." . $versioncomponents[1] .
+                                        '.0 probably should not be alpha or beta');
+                                return false;
+                            } elseif (strlen($versioncomponents[2]) > 1) {
+                                // version 2.*.0RC1 or 2.*.0beta24 etc.
+                                return true;
+                            } else {
+                                // version 2.*.0
+                                $this->_addWarning('version',
+                                    "version $majver." . $versioncomponents[1] .
+                                        '.0 cannot be alpha or beta');
+                                return true;
+                            }
+                        } else {
+                            $this->_addWarning('version',
+                                "bugfix versions ($majver.x.y where y > 0) should " .
+                                'not be alpha or beta');
+                            return true;
+                        }
+                    } elseif ($versioncomponents[0] != '0') {
+                        $this->_addWarning('version',
+                            "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases");
+                        return true;
+                    }
+                    if ($versioncomponents[0] . 'a' == '0a') {
+                        return true;
+                    }
+                    if ($versioncomponents[0] == 0) {
+                        $versioncomponents[0] = '0';
+                        $this->_addWarning('version',
+                            'version "' . $version . '" should be "' .
+                            implode('.' ,$versioncomponents) . '"');
+                    }
+                }
+                return true;
+            break;
+            case 'stable' :
+                if ($versioncomponents[0] == '0') {
+                    $this->_addWarning('version', 'versions less than 1.0.0 cannot ' .
+                    'be stable');
+                    return true;
+                }
+                if (!is_numeric($versioncomponents[2])) {
+                    if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i',
+                          $versioncomponents[2])) {
+                        $this->_addWarning('version', 'version "' . $version . '" or any ' .
+                            'RC/beta/alpha version cannot be stable');
+                        return true;
+                    }
+                }
+                // check for a package that extends a package,
+                // like Foo and Foo2
+                if ($this->_packagexml->getExtends()) {
+                    $vlen = strlen($versioncomponents[0] . '');
+                    $majver = substr($name, strlen($name) - $vlen);
+                    while ($majver && !is_numeric($majver{0})) {
+                        $majver = substr($majver, 1);
+                    }
+                    if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
+                        $this->_addWarning('version', 'first version number "' .
+                            $versioncomponents[0] . '" must match the postfix of ' .
+                            'package name "' . $name . '" (' .
+                            $majver . ')');
+                        return true;
+                    }
+                } elseif ($versioncomponents[0] > 1) {
+                    $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' .
+                        '1 for any package that does not have an <extends> tag');
+                }
+                return true;
+            break;
+            default :
+                return false;
+            break;
+        }
+    }
+
+    /**
+     * @access protected
+     */
+    function validateMaintainers()
+    {
+        // maintainers can only be truly validated server-side for most channels
+        // but allow this customization for those who wish it
+        return true;
+    }
+
+    /**
+     * @access protected
+     */
+    function validateDate()
+    {
+        if ($this->_state == PEAR_VALIDATE_NORMAL ||
+              $this->_state == PEAR_VALIDATE_PACKAGING) {
+
+            if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/',
+                  $this->_packagexml->getDate(), $res) ||
+                  count($res) < 4
+                  || !checkdate($res[2], $res[3], $res[1])
+                ) {
+                $this->_addFailure('date', 'invalid release date "' .
+                    $this->_packagexml->getDate() . '"');
+                return false;
+            }
+
+            if ($this->_state == PEAR_VALIDATE_PACKAGING &&
+                  $this->_packagexml->getDate() != date('Y-m-d')) {
+                $this->_addWarning('date', 'Release Date "' .
+                    $this->_packagexml->getDate() . '" is not today');
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @access protected
+     */
+    function validateTime()
+    {
+        if (!$this->_packagexml->getTime()) {
+            // default of no time value set
+            return true;
+        }
+
+        // packager automatically sets time, so only validate if pear validate is called
+        if ($this->_state = PEAR_VALIDATE_NORMAL) {
+            if (!preg_match('/\d\d:\d\d:\d\d/',
+                  $this->_packagexml->getTime())) {
+                $this->_addFailure('time', 'invalid release time "' .
+                    $this->_packagexml->getTime() . '"');
+                return false;
+            }
+
+            $result = preg_match('|\d{2}\:\d{2}\:\d{2}|', $this->_packagexml->getTime(), $matches);
+            if ($result === false || empty($matches)) {
+                $this->_addFailure('time', 'invalid release time "' .
+                    $this->_packagexml->getTime() . '"');
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * @access protected
+     */
+    function validateState()
+    {
+        // this is the closest to "final" php4 can get
+        if (!PEAR_Validate::validState($this->_packagexml->getState())) {
+            if (strtolower($this->_packagexml->getState() == 'rc')) {
+                $this->_addFailure('state', 'RC is not a state, it is a version ' .
+                    'postfix, use ' . $this->_packagexml->getVersion() . 'RC1, state beta');
+            }
+            $this->_addFailure('state', 'invalid release state "' .
+                $this->_packagexml->getState() . '", must be one of: ' .
+                implode(', ', PEAR_Validate::getValidStates()));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @access protected
+     */
+    function validateStability()
+    {
+        $ret = true;
+        $packagestability = $this->_packagexml->getState();
+        $apistability = $this->_packagexml->getState('api');
+        if (!PEAR_Validate::validState($packagestability)) {
+            $this->_addFailure('state', 'invalid release stability "' .
+                $this->_packagexml->getState() . '", must be one of: ' .
+                implode(', ', PEAR_Validate::getValidStates()));
+            $ret = false;
+        }
+        $apistates = PEAR_Validate::getValidStates();
+        array_shift($apistates); // snapshot is not allowed
+        if (!in_array($apistability, $apistates)) {
+            $this->_addFailure('state', 'invalid API stability "' .
+                $this->_packagexml->getState('api') . '", must be one of: ' .
+                implode(', ', $apistates));
+            $ret = false;
+        }
+        return $ret;
+    }
+
+    /**
+     * @access protected
+     */
+    function validateSummary()
+    {
+        return true;
+    }
+
+    /**
+     * @access protected
+     */
+    function validateDescription()
+    {
+        return true;
+    }
+
+    /**
+     * @access protected
+     */
+    function validateLicense()
+    {
+        return true;
+    }
+
+    /**
+     * @access protected
+     */
+    function validateNotes()
+    {
+        return true;
+    }
+
+    /**
+     * for package.xml 2.0 only - channels can't use package.xml 1.0
+     * @access protected
+     */
+    function validateDependencies()
+    {
+        return true;
+    }
+
+    /**
+     * for package.xml 1.0 only
+     * @access private
+     */
+    function _validateFilelist()
+    {
+        return true; // placeholder for now
+    }
+
+    /**
+     * for package.xml 2.0 only
+     * @access protected
+     */
+    function validateMainFilelist()
+    {
+        return true; // placeholder for now
+    }
+
+    /**
+     * for package.xml 2.0 only
+     * @access protected
+     */
+    function validateReleaseFilelist()
+    {
+        return true; // placeholder for now
+    }
+
+    /**
+     * @access protected
+     */
+    function validateChangelog()
+    {
+        return true;
+    }
+
+    /**
+     * @access protected
+     */
+    function validateFilelist()
+    {
+        return true;
+    }
+
+    /**
+     * @access protected
+     */
+    function validateDeps()
+    {
+        return true;
+    }
+}
Index: trunk/library/PEAR/PEAR/Frontend/CLI.php
===================================================================
--- trunk/library/PEAR/PEAR/Frontend/CLI.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Frontend/CLI.php	(revision 5146)
@@ -0,0 +1,751 @@
+<?php
+/**
+ * PEAR_Frontend_CLI
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: CLI.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+/**
+ * base class
+ */
+require_once 'PEAR/Frontend.php';
+
+/**
+ * Command-line Frontend for the PEAR Installer
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Frontend_CLI extends PEAR_Frontend
+{
+    /**
+     * What type of user interface this frontend is for.
+     * @var string
+     * @access public
+     */
+    var $type = 'CLI';
+    var $lp = ''; // line prefix
+
+    var $params = array();
+    var $term = array(
+        'bold'   => '',
+        'normal' => '',
+    );
+
+    function PEAR_Frontend_CLI()
+    {
+        parent::PEAR();
+        $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
+        if (function_exists('posix_isatty') && !posix_isatty(1)) {
+            // output is being redirected to a file or through a pipe
+        } elseif ($term) {
+            if (preg_match('/^(xterm|vt220|linux)/', $term)) {
+                $this->term['bold']   = sprintf("%c%c%c%c", 27, 91, 49, 109);
+                $this->term['normal'] = sprintf("%c%c%c", 27, 91, 109);
+            } elseif (preg_match('/^vt100/', $term)) {
+                $this->term['bold']   = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
+                $this->term['normal'] = sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
+            }
+        } elseif (OS_WINDOWS) {
+            // XXX add ANSI codes here
+        }
+    }
+
+    /**
+     * @param object PEAR_Error object
+     */
+    function displayError($e)
+    {
+        return $this->_displayLine($e->getMessage());
+    }
+
+    /**
+     * @param object PEAR_Error object
+     */
+    function displayFatalError($eobj)
+    {
+        $this->displayError($eobj);
+        if (class_exists('PEAR_Config')) {
+            $config = &PEAR_Config::singleton();
+            if ($config->get('verbose') > 5) {
+                if (function_exists('debug_print_backtrace')) {
+                    debug_print_backtrace();
+                    exit(1);
+                }
+
+                $raised = false;
+                foreach (debug_backtrace() as $i => $frame) {
+                    if (!$raised) {
+                        if (isset($frame['class'])
+                            && strtolower($frame['class']) == 'pear'
+                            && strtolower($frame['function']) == 'raiseerror'
+                        ) {
+                            $raised = true;
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    $frame['class']    = !isset($frame['class'])    ? '' : $frame['class'];
+                    $frame['type']     = !isset($frame['type'])     ? '' : $frame['type'];
+                    $frame['function'] = !isset($frame['function']) ? '' : $frame['function'];
+                    $frame['line']     = !isset($frame['line'])     ? '' : $frame['line'];
+                    $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]");
+                }
+            }
+        }
+
+        exit(1);
+    }
+
+    /**
+     * Instruct the runInstallScript method to skip a paramgroup that matches the
+     * id value passed in.
+     *
+     * This method is useful for dynamically configuring which sections of a post-install script
+     * will be run based on the user's setup, which is very useful for making flexible
+     * post-install scripts without losing the cross-Frontend ability to retrieve user input
+     * @param string
+     */
+    function skipParamgroup($id)
+    {
+        $this->_skipSections[$id] = true;
+    }
+
+    function runPostinstallScripts(&$scripts)
+    {
+        foreach ($scripts as $i => $script) {
+            $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj);
+        }
+    }
+
+    /**
+     * @param array $xml contents of postinstallscript tag
+     * @param object $script post-installation script
+     * @param string install|upgrade
+     */
+    function runInstallScript($xml, &$script)
+    {
+        $this->_skipSections = array();
+        if (!is_array($xml) || !isset($xml['paramgroup'])) {
+            $script->run(array(), '_default');
+            return;
+        }
+
+        $completedPhases = array();
+        if (!isset($xml['paramgroup'][0])) {
+            $xml['paramgroup'] = array($xml['paramgroup']);
+        }
+
+        foreach ($xml['paramgroup'] as $group) {
+            if (isset($this->_skipSections[$group['id']])) {
+                // the post-install script chose to skip this section dynamically
+                continue;
+            }
+
+            if (isset($group['name'])) {
+                $paramname = explode('::', $group['name']);
+                if ($lastgroup['id'] != $paramname[0]) {
+                    continue;
+                }
+
+                $group['name'] = $paramname[1];
+                if (!isset($answers)) {
+                    return;
+                }
+
+                if (isset($answers[$group['name']])) {
+                    switch ($group['conditiontype']) {
+                        case '=' :
+                            if ($answers[$group['name']] != $group['value']) {
+                                continue 2;
+                            }
+                        break;
+                        case '!=' :
+                            if ($answers[$group['name']] == $group['value']) {
+                                continue 2;
+                            }
+                        break;
+                        case 'preg_match' :
+                            if (!@preg_match('/' . $group['value'] . '/',
+                                  $answers[$group['name']])) {
+                                continue 2;
+                            }
+                        break;
+                        default :
+                        return;
+                    }
+                }
+            }
+
+            $lastgroup = $group;
+            if (isset($group['instructions'])) {
+                $this->_display($group['instructions']);
+            }
+
+            if (!isset($group['param'][0])) {
+                $group['param'] = array($group['param']);
+            }
+
+            if (isset($group['param'])) {
+                if (method_exists($script, 'postProcessPrompts')) {
+                    $prompts = $script->postProcessPrompts($group['param'], $group['id']);
+                    if (!is_array($prompts) || count($prompts) != count($group['param'])) {
+                        $this->outputData('postinstall', 'Error: post-install script did not ' .
+                            'return proper post-processed prompts');
+                        $prompts = $group['param'];
+                    } else {
+                        foreach ($prompts as $i => $var) {
+                            if (!is_array($var) || !isset($var['prompt']) ||
+                                  !isset($var['name']) ||
+                                  ($var['name'] != $group['param'][$i]['name']) ||
+                                  ($var['type'] != $group['param'][$i]['type'])
+                            ) {
+                                $this->outputData('postinstall', 'Error: post-install script ' .
+                                    'modified the variables or prompts, severe security risk. ' .
+                                    'Will instead use the defaults from the package.xml');
+                                $prompts = $group['param'];
+                            }
+                        }
+                    }
+
+                    $answers = $this->confirmDialog($prompts);
+                } else {
+                    $answers = $this->confirmDialog($group['param']);
+                }
+            }
+
+            if ((isset($answers) && $answers) || !isset($group['param'])) {
+                if (!isset($answers)) {
+                    $answers = array();
+                }
+
+                array_unshift($completedPhases, $group['id']);
+                if (!$script->run($answers, $group['id'])) {
+                    $script->run($completedPhases, '_undoOnError');
+                    return;
+                }
+            } else {
+                $script->run($completedPhases, '_undoOnError');
+                return;
+            }
+        }
+    }
+
+    /**
+     * Ask for user input, confirm the answers and continue until the user is satisfied
+     * @param array an array of arrays, format array('name' => 'paramname', 'prompt' =>
+     *              'text to display', 'type' => 'string'[, default => 'default value'])
+     * @return array
+     */
+    function confirmDialog($params)
+    {
+        $answers = $prompts = $types = array();
+        foreach ($params as $param) {
+            $prompts[$param['name']] = $param['prompt'];
+            $types[$param['name']]   = $param['type'];
+            $answers[$param['name']] = isset($param['default']) ? $param['default'] : '';
+        }
+
+        $tried = false;
+        do {
+            if ($tried) {
+                $i = 1;
+                foreach ($answers as $var => $value) {
+                    if (!strlen($value)) {
+                        echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n");
+                    }
+                    $i++;
+                }
+            }
+
+            $answers = $this->userDialog('', $prompts, $types, $answers);
+            $tried   = true;
+        } while (is_array($answers) && count(array_filter($answers)) != count($prompts));
+
+        return $answers;
+    }
+
+    function userDialog($command, $prompts, $types = array(), $defaults = array(), $screensize = 20)
+    {
+        if (!is_array($prompts)) {
+            return array();
+        }
+
+        $testprompts = array_keys($prompts);
+        $result      = $defaults;
+
+        reset($prompts);
+        if (count($prompts) === 1) {
+            foreach ($prompts as $key => $prompt) {
+                $type    = $types[$key];
+                $default = @$defaults[$key];
+                print "$prompt ";
+                if ($default) {
+                    print "[$default] ";
+                }
+                print ": ";
+
+                $line         = fgets(STDIN, 2048);
+                $result[$key] =  ($default && trim($line) == '') ? $default : trim($line);
+            }
+
+            return $result;
+        }
+
+        $first_run = true;
+        while (true) {
+            $descLength = max(array_map('strlen', $prompts));
+            $descFormat = "%-{$descLength}s";
+            $last       = count($prompts);
+
+            $i = 0;
+            foreach ($prompts as $n => $var) {
+                $res = isset($result[$n]) ? $result[$n] : null;
+                printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], $res);
+            }
+            print "\n1-$last, 'all', 'abort', or Enter to continue: ";
+
+            $tmp = trim(fgets(STDIN, 1024));
+            if (empty($tmp)) {
+                break;
+            }
+
+            if ($tmp == 'abort') {
+                return false;
+            }
+
+            if (isset($testprompts[(int)$tmp - 1])) {
+                $var     = $testprompts[(int)$tmp - 1];
+                $desc    = $prompts[$var];
+                $current = @$result[$var];
+                print "$desc [$current] : ";
+                $tmp = trim(fgets(STDIN, 1024));
+                if ($tmp !== '') {
+                    $result[$var] = $tmp;
+                }
+            } elseif ($tmp == 'all') {
+                foreach ($prompts as $var => $desc) {
+                    $current = $result[$var];
+                    print "$desc [$current] : ";
+                    $tmp = trim(fgets(STDIN, 1024));
+                    if (trim($tmp) !== '') {
+                        $result[$var] = trim($tmp);
+                    }
+                }
+            }
+
+            $first_run = false;
+        }
+
+        return $result;
+    }
+
+    function userConfirm($prompt, $default = 'yes')
+    {
+        trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
+        static $positives = array('y', 'yes', 'on', '1');
+        static $negatives = array('n', 'no', 'off', '0');
+        print "$this->lp$prompt [$default] : ";
+        $fp = fopen("php://stdin", "r");
+        $line = fgets($fp, 2048);
+        fclose($fp);
+        $answer = strtolower(trim($line));
+        if (empty($answer)) {
+            $answer = $default;
+        }
+        if (in_array($answer, $positives)) {
+            return true;
+        }
+        if (in_array($answer, $negatives)) {
+            return false;
+        }
+        if (in_array($default, $positives)) {
+            return true;
+        }
+        return false;
+    }
+
+    function outputData($data, $command = '_default')
+    {
+        switch ($command) {
+            case 'channel-info':
+                foreach ($data as $type => $section) {
+                    if ($type == 'main') {
+                        $section['data'] = array_values($section['data']);
+                    }
+
+                    $this->outputData($section);
+                }
+                break;
+            case 'install':
+            case 'upgrade':
+            case 'upgrade-all':
+                if (is_array($data) && isset($data['release_warnings'])) {
+                    $this->_displayLine('');
+                    $this->_startTable(array(
+                        'border' => false,
+                        'caption' => 'Release Warnings'
+                    ));
+                    $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
+                    $this->_endTable();
+                    $this->_displayLine('');
+                }
+
+                $this->_displayLine(is_array($data) ? $data['data'] : $data);
+                break;
+            case 'search':
+                $this->_startTable($data);
+                if (isset($data['headline']) && is_array($data['headline'])) {
+                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
+                }
+
+                $packages = array();
+                foreach($data['data'] as $category) {
+                    foreach($category as $name => $pkg) {
+                        $packages[$pkg[0]] = $pkg;
+                    }
+                }
+
+                $p = array_keys($packages);
+                natcasesort($p);
+                foreach ($p as $name) {
+                    $this->_tableRow($packages[$name], null, array(1 => array('wrap' => 55)));
+                }
+
+                $this->_endTable();
+                break;
+            case 'list-all':
+                if (!isset($data['data'])) {
+                      $this->_displayLine('No packages in channel');
+                      break;
+                }
+
+                $this->_startTable($data);
+                if (isset($data['headline']) && is_array($data['headline'])) {
+                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
+                }
+
+                $packages = array();
+                foreach($data['data'] as $category) {
+                    foreach($category as $name => $pkg) {
+                        $packages[$pkg[0]] = $pkg;
+                    }
+                }
+
+                $p = array_keys($packages);
+                natcasesort($p);
+                foreach ($p as $name) {
+                    $pkg = $packages[$name];
+                    unset($pkg[4], $pkg[5]);
+                    $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
+                }
+
+                $this->_endTable();
+                break;
+            case 'config-show':
+                $data['border'] = false;
+                $opts = array(
+                    0 => array('wrap' => 30),
+                    1 => array('wrap' => 20),
+                    2 => array('wrap' => 35)
+                );
+
+                $this->_startTable($data);
+                if (isset($data['headline']) && is_array($data['headline'])) {
+                    $this->_tableRow($data['headline'], array('bold' => true), $opts);
+                }
+
+                foreach ($data['data'] as $group) {
+                    foreach ($group as $value) {
+                        if ($value[2] == '') {
+                            $value[2] = "<not set>";
+                        }
+
+                        $this->_tableRow($value, null, $opts);
+                    }
+                }
+
+                $this->_endTable();
+                break;
+            case 'remote-info':
+                $d = $data;
+                $data = array(
+                    'caption' => 'Package details:',
+                    'border'  => false,
+                    'data'    => array(
+                        array("Latest",      $data['stable']),
+                        array("Installed",   $data['installed']),
+                        array("Package",     $data['name']),
+                        array("License",     $data['license']),
+                        array("Category",    $data['category']),
+                        array("Summary",     $data['summary']),
+                        array("Description", $data['description']),
+                    ),
+                );
+
+                if (isset($d['deprecated']) && $d['deprecated']) {
+                    $conf = &PEAR_Config::singleton();
+                    $reg = $conf->getRegistry();
+                    $name = $reg->parsedPackageNameToString($d['deprecated'], true);
+                    $data['data'][] = array('Deprecated! use', $name);
+                }
+            default: {
+                if (is_array($data)) {
+                    $this->_startTable($data);
+                    $count = count($data['data'][0]);
+                    if ($count == 2) {
+                        $opts = array(0 => array('wrap' => 25),
+                                      1 => array('wrap' => 48)
+                        );
+                    } elseif ($count == 3) {
+                        $opts = array(0 => array('wrap' => 30),
+                                      1 => array('wrap' => 20),
+                                      2 => array('wrap' => 35)
+                        );
+                    } else {
+                        $opts = null;
+                    }
+                    if (isset($data['headline']) && is_array($data['headline'])) {
+                        $this->_tableRow($data['headline'],
+                                         array('bold' => true),
+                                         $opts);
+                    }
+
+                    if (is_array($data['data'])) {
+                        foreach($data['data'] as $row) {
+                            $this->_tableRow($row, null, $opts);
+                        }
+                    } else {
+                        $this->_tableRow(array($data['data']), null, $opts);
+                     }
+                    $this->_endTable();
+                } else {
+                    $this->_displayLine($data);
+                }
+            }
+        }
+    }
+
+    function log($text, $append_crlf = true)
+    {
+        if ($append_crlf) {
+            return $this->_displayLine($text);
+        }
+
+        return $this->_display($text);
+    }
+
+    function bold($text)
+    {
+        if (empty($this->term['bold'])) {
+            return strtoupper($text);
+        }
+
+        return $this->term['bold'] . $text . $this->term['normal'];
+    }
+
+    function _displayHeading($title)
+    {
+        print $this->lp.$this->bold($title)."\n";
+        print $this->lp.str_repeat("=", strlen($title))."\n";
+    }
+
+    function _startTable($params = array())
+    {
+        $params['table_data'] = array();
+        $params['widest']     = array();  // indexed by column
+        $params['highest']    = array(); // indexed by row
+        $params['ncols']      = 0;
+        $this->params         = $params;
+    }
+
+    function _tableRow($columns, $rowparams = array(), $colparams = array())
+    {
+        $highest = 1;
+        for ($i = 0; $i < count($columns); $i++) {
+            $col = &$columns[$i];
+            if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
+                $col = wordwrap($col, $colparams[$i]['wrap']);
+            }
+
+            if (strpos($col, "\n") !== false) {
+                $multiline = explode("\n", $col);
+                $w = 0;
+                foreach ($multiline as $n => $line) {
+                    $len = strlen($line);
+                    if ($len > $w) {
+                        $w = $len;
+                    }
+                }
+                $lines = count($multiline);
+            } else {
+                $w = strlen($col);
+            }
+
+            if (isset($this->params['widest'][$i])) {
+                if ($w > $this->params['widest'][$i]) {
+                    $this->params['widest'][$i] = $w;
+                }
+            } else {
+                $this->params['widest'][$i] = $w;
+            }
+
+            $tmp = count_chars($columns[$i], 1);
+            // handle unix, mac and windows formats
+            $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1;
+            if ($lines > $highest) {
+                $highest = $lines;
+            }
+        }
+
+        if (count($columns) > $this->params['ncols']) {
+            $this->params['ncols'] = count($columns);
+        }
+
+        $new_row = array(
+            'data'      => $columns,
+            'height'    => $highest,
+            'rowparams' => $rowparams,
+            'colparams' => $colparams,
+        );
+        $this->params['table_data'][] = $new_row;
+    }
+
+    function _endTable()
+    {
+        extract($this->params);
+        if (!empty($caption)) {
+            $this->_displayHeading($caption);
+        }
+
+        if (count($table_data) === 0) {
+            return;
+        }
+
+        if (!isset($width)) {
+            $width = $widest;
+        } else {
+            for ($i = 0; $i < $ncols; $i++) {
+                if (!isset($width[$i])) {
+                    $width[$i] = $widest[$i];
+                }
+            }
+        }
+
+        $border = false;
+        if (empty($border)) {
+            $cellstart  = '';
+            $cellend    = ' ';
+            $rowend     = '';
+            $padrowend  = false;
+            $borderline = '';
+        } else {
+            $cellstart  = '| ';
+            $cellend    = ' ';
+            $rowend     = '|';
+            $padrowend  = true;
+            $borderline = '+';
+            foreach ($width as $w) {
+                $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
+                $borderline .= '+';
+            }
+        }
+
+        if ($borderline) {
+            $this->_displayLine($borderline);
+        }
+
+        for ($i = 0; $i < count($table_data); $i++) {
+            extract($table_data[$i]);
+            if (!is_array($rowparams)) {
+                $rowparams = array();
+            }
+
+            if (!is_array($colparams)) {
+                $colparams = array();
+            }
+
+            $rowlines = array();
+            if ($height > 1) {
+                for ($c = 0; $c < count($data); $c++) {
+                    $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
+                    if (count($rowlines[$c]) < $height) {
+                        $rowlines[$c] = array_pad($rowlines[$c], $height, '');
+                    }
+                }
+            } else {
+                for ($c = 0; $c < count($data); $c++) {
+                    $rowlines[$c] = array($data[$c]);
+                }
+            }
+
+            for ($r = 0; $r < $height; $r++) {
+                $rowtext = '';
+                for ($c = 0; $c < count($data); $c++) {
+                    if (isset($colparams[$c])) {
+                        $attribs = array_merge($rowparams, $colparams);
+                    } else {
+                        $attribs = $rowparams;
+                    }
+
+                    $w = isset($width[$c]) ? $width[$c] : 0;
+                    //$cell = $data[$c];
+                    $cell = $rowlines[$c][$r];
+                    $l = strlen($cell);
+                    if ($l > $w) {
+                        $cell = substr($cell, 0, $w);
+                    }
+
+                    if (isset($attribs['bold'])) {
+                        $cell = $this->bold($cell);
+                    }
+
+                    if ($l < $w) {
+                        // not using str_pad here because we may
+                        // add bold escape characters to $cell
+                        $cell .= str_repeat(' ', $w - $l);
+                    }
+
+                    $rowtext .= $cellstart . $cell . $cellend;
+                }
+
+                if (!$border) {
+                    $rowtext = rtrim($rowtext);
+                }
+
+                $rowtext .= $rowend;
+                $this->_displayLine($rowtext);
+            }
+        }
+
+        if ($borderline) {
+            $this->_displayLine($borderline);
+        }
+    }
+
+    function _displayLine($text)
+    {
+        print "$this->lp$text\n";
+    }
+
+    function _display($text)
+    {
+        print $text;
+    }
+}
Index: trunk/library/PEAR/PEAR/PackageFile.php
===================================================================
--- trunk/library/PEAR/PEAR/PackageFile.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/PackageFile.php	(revision 5146)
@@ -0,0 +1,492 @@
+<?php
+/**
+ * PEAR_PackageFile, package.xml parsing utility class
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: PackageFile.php 313024 2011-07-06 19:51:24Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * needed for PEAR_VALIDATE_* constants
+ */
+require_once 'PEAR/Validate.php';
+/**
+ * Error code if the package.xml <package> tag does not contain a valid version
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1);
+/**
+ * Error code if the package.xml <package> tag version is not supported (version 1.0 and 1.1 are the only supported versions,
+ * currently
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2);
+/**
+ * Abstraction for the package.xml package description file
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_PackageFile
+{
+    /**
+     * @var PEAR_Config
+     */
+    var $_config;
+    var $_debug;
+
+    var $_logger = false;
+    /**
+     * @var boolean
+     */
+    var $_rawReturn = false;
+
+    /**
+     * helper for extracting Archive_Tar errors
+     * @var array
+     * @access private
+     */
+    var $_extractErrors = array();
+
+    /**
+     *
+     * @param   PEAR_Config $config
+     * @param   ?   $debug
+     * @param   string @tmpdir Optional temporary directory for uncompressing
+     *          files
+     */
+    function PEAR_PackageFile(&$config, $debug = false)
+    {
+        $this->_config = $config;
+        $this->_debug = $debug;
+    }
+
+    /**
+     * Turn off validation - return a parsed package.xml without checking it
+     *
+     * This is used by the package-validate command
+     */
+    function rawReturn()
+    {
+        $this->_rawReturn = true;
+    }
+
+    function setLogger(&$l)
+    {
+        $this->_logger = &$l;
+    }
+
+    /**
+     * Create a PEAR_PackageFile_Parser_v* of a given version.
+     * @param   int $version
+     * @return  PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1
+     */
+    function &parserFactory($version)
+    {
+        if (!in_array($version{0}, array('1', '2'))) {
+            $a = false;
+            return $a;
+        }
+
+        include_once 'PEAR/PackageFile/Parser/v' . $version{0} . '.php';
+        $version = $version{0};
+        $class = "PEAR_PackageFile_Parser_v$version";
+        $a = new $class;
+        return $a;
+    }
+
+    /**
+     * For simpler unit-testing
+     * @return string
+     */
+    function getClassPrefix()
+    {
+        return 'PEAR_PackageFile_v';
+    }
+
+    /**
+     * Create a PEAR_PackageFile_v* of a given version.
+     * @param   int $version
+     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v1
+     */
+    function &factory($version)
+    {
+        if (!in_array($version{0}, array('1', '2'))) {
+            $a = false;
+            return $a;
+        }
+
+        include_once 'PEAR/PackageFile/v' . $version{0} . '.php';
+        $version = $version{0};
+        $class = $this->getClassPrefix() . $version;
+        $a = new $class;
+        return $a;
+    }
+
+    /**
+     * Create a PEAR_PackageFile_v* from its toArray() method
+     *
+     * WARNING: no validation is performed, the array is assumed to be valid,
+     * always parse from xml if you want validation.
+     * @param   array $arr
+     * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2
+     * @uses    factory() to construct the returned object.
+     */
+    function &fromArray($arr)
+    {
+        if (isset($arr['xsdversion'])) {
+            $obj = &$this->factory($arr['xsdversion']);
+            if ($this->_logger) {
+                $obj->setLogger($this->_logger);
+            }
+
+            $obj->setConfig($this->_config);
+            $obj->fromArray($arr);
+            return $obj;
+        }
+
+        if (isset($arr['package']['attribs']['version'])) {
+            $obj = &$this->factory($arr['package']['attribs']['version']);
+        } else {
+            $obj = &$this->factory('1.0');
+        }
+
+        if ($this->_logger) {
+            $obj->setLogger($this->_logger);
+        }
+
+        $obj->setConfig($this->_config);
+        $obj->fromArray($arr);
+        return $obj;
+    }
+
+    /**
+     * Create a PEAR_PackageFile_v* from an XML string.
+     * @access  public
+     * @param   string $data contents of package.xml file
+     * @param   int $state package state (one of PEAR_VALIDATE_* constants)
+     * @param   string $file full path to the package.xml file (and the files
+     *          it references)
+     * @param   string $archive optional name of the archive that the XML was
+     *          extracted from, if any
+     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @uses    parserFactory() to construct a parser to load the package.
+     */
+    function &fromXmlString($data, $state, $file, $archive = false)
+    {
+        if (preg_match('/<package[^>]+version=[\'"]([0-9]+\.[0-9]+)[\'"]/', $data, $packageversion)) {
+            if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) {
+                return PEAR::raiseError('package.xml version "' . $packageversion[1] .
+                    '" is not supported, only 1.0, 2.0, and 2.1 are supported.');
+            }
+
+            $object = &$this->parserFactory($packageversion[1]);
+            if ($this->_logger) {
+                $object->setLogger($this->_logger);
+            }
+
+            $object->setConfig($this->_config);
+            $pf = $object->parse($data, $file, $archive);
+            if (PEAR::isError($pf)) {
+                return $pf;
+            }
+
+            if ($this->_rawReturn) {
+                return $pf;
+            }
+
+            if (!$pf->validate($state)) {;
+                if ($this->_config->get('verbose') > 0
+                    && $this->_logger && $pf->getValidationWarnings(false)
+                ) {
+                    foreach ($pf->getValidationWarnings(false) as $warning) {
+                        $this->_logger->log(0, 'ERROR: ' . $warning['message']);
+                    }
+                }
+
+                $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
+                    2, null, null, $pf->getValidationWarnings());
+                return $a;
+            }
+
+            if ($this->_logger && $pf->getValidationWarnings(false)) {
+                foreach ($pf->getValidationWarnings() as $warning) {
+                    $this->_logger->log(0, 'WARNING: ' . $warning['message']);
+                }
+            }
+
+            if (method_exists($pf, 'flattenFilelist')) {
+                $pf->flattenFilelist(); // for v2
+            }
+
+            return $pf;
+        } elseif (preg_match('/<package[^>]+version=[\'"]([^"\']+)[\'"]/', $data, $packageversion)) {
+            $a = PEAR::raiseError('package.xml file "' . $file .
+                '" has unsupported package.xml <package> version "' . $packageversion[1] . '"');
+            return $a;
+        } else {
+            if (!class_exists('PEAR_ErrorStack')) {
+                require_once 'PEAR/ErrorStack.php';
+            }
+
+            PEAR_ErrorStack::staticPush('PEAR_PackageFile',
+                PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION,
+                'warning', array('xml' => $data), 'package.xml "' . $file .
+                    '" has no package.xml <package> version');
+            $object = &$this->parserFactory('1.0');
+            $object->setConfig($this->_config);
+            $pf = $object->parse($data, $file, $archive);
+            if (PEAR::isError($pf)) {
+                return $pf;
+            }
+
+            if ($this->_rawReturn) {
+                return $pf;
+            }
+
+            if (!$pf->validate($state)) {
+                $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
+                    2, null, null, $pf->getValidationWarnings());
+                return $a;
+            }
+
+            if ($this->_logger && $pf->getValidationWarnings(false)) {
+                foreach ($pf->getValidationWarnings() as $warning) {
+                    $this->_logger->log(0, 'WARNING: ' . $warning['message']);
+                }
+            }
+
+            if (method_exists($pf, 'flattenFilelist')) {
+                $pf->flattenFilelist(); // for v2
+            }
+
+            return $pf;
+        }
+    }
+
+    /**
+     * Register a temporary file or directory.  When the destructor is
+     * executed, all registered temporary files and directories are
+     * removed.
+     *
+     * @param string  $file  name of file or directory
+     * @return  void
+     */
+    function addTempFile($file)
+    {
+        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
+    }
+
+    /**
+     * Create a PEAR_PackageFile_v* from a compresed Tar or Tgz file.
+     * @access  public
+     * @param string contents of package.xml file
+     * @param int package state (one of PEAR_VALIDATE_* constants)
+     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @using   Archive_Tar to extract the files
+     * @using   fromPackageFile() to load the package after the package.xml
+     *          file is extracted.
+     */
+    function &fromTgzFile($file, $state)
+    {
+        if (!class_exists('Archive_Tar')) {
+            require_once 'Archive/Tar.php';
+        }
+
+        $tar = new Archive_Tar($file);
+        if ($this->_debug <= 1) {
+            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
+        }
+
+        $content = $tar->listContent();
+        if ($this->_debug <= 1) {
+            $tar->popErrorHandling();
+        }
+
+        if (!is_array($content)) {
+            if (is_string($file) && strlen($file < 255) &&
+                  (!file_exists($file) || !@is_file($file))) {
+                $ret = PEAR::raiseError("could not open file \"$file\"");
+                return $ret;
+            }
+
+            $file = realpath($file);
+            $ret = PEAR::raiseError("Could not get contents of package \"$file\"".
+                                     '. Invalid tgz file.');
+            return $ret;
+        }
+
+        if (!count($content) && !@is_file($file)) {
+            $ret = PEAR::raiseError("could not open file \"$file\"");
+            return $ret;
+        }
+
+        $xml      = null;
+        $origfile = $file;
+        foreach ($content as $file) {
+            $name = $file['filename'];
+            if ($name == 'package2.xml') { // allow a .tgz to distribute both versions
+                $xml = $name;
+                break;
+            }
+
+            if ($name == 'package.xml') {
+                $xml = $name;
+                break;
+            } elseif (preg_match('/package.xml$/', $name, $match)) {
+                $xml = $name;
+                break;
+            }
+        }
+
+        $tmpdir = System::mktemp('-t "' . $this->_config->get('temp_dir') . '" -d pear');
+        if ($tmpdir === false) {
+            $ret = PEAR::raiseError("there was a problem with getting the configured temp directory");
+            return $ret;
+        }
+
+        PEAR_PackageFile::addTempFile($tmpdir);
+
+        $this->_extractErrors();
+        PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors'));
+
+        if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
+            $extra = implode("\n", $this->_extractErrors());
+            if ($extra) {
+                $extra = ' ' . $extra;
+            }
+
+            PEAR::staticPopErrorHandling();
+            $ret = PEAR::raiseError('could not extract the package.xml file from "' .
+                $origfile . '"' . $extra);
+            return $ret;
+        }
+
+        PEAR::staticPopErrorHandling();
+        $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile);
+        return $ret;
+    }
+
+    /**
+     * helper callback for extracting Archive_Tar errors
+     *
+     * @param PEAR_Error|null $err
+     * @return array
+     * @access private
+     */
+    function _extractErrors($err = null)
+    {
+        static $errors = array();
+        if ($err === null) {
+            $e = $errors;
+            $errors = array();
+            return $e;
+        }
+        $errors[] = $err->getMessage();
+    }
+
+    /**
+     * Create a PEAR_PackageFile_v* from a package.xml file.
+     *
+     * @access public
+     * @param   string  $descfile  name of package xml file
+     * @param   int     $state package state (one of PEAR_VALIDATE_* constants)
+     * @param   string|false $archive name of the archive this package.xml came
+     *          from, if any
+     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @uses    PEAR_PackageFile::fromXmlString to create the oject after the
+     *          XML is loaded from the package.xml file.
+     */
+    function &fromPackageFile($descfile, $state, $archive = false)
+    {
+        $fp = false;
+        if (is_string($descfile) && strlen($descfile) < 255 &&
+             (
+              !file_exists($descfile) || !is_file($descfile) || !is_readable($descfile)
+              || (!$fp = @fopen($descfile, 'r'))
+             )
+        ) {
+            $a = PEAR::raiseError("Unable to open $descfile");
+            return $a;
+        }
+
+        // read the whole thing so we only get one cdata callback
+        // for each block of cdata
+        fclose($fp);
+        $data = file_get_contents($descfile);
+        $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive);
+        return $ret;
+    }
+
+    /**
+     * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file.
+     *
+     * This method is able to extract information about a package from a .tgz
+     * archive or from a XML package definition file.
+     *
+     * @access public
+     * @param   string  $info file name
+     * @param   int     $state package state (one of PEAR_VALIDATE_* constants)
+     * @return  PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @uses    fromPackageFile() if the file appears to be XML
+     * @uses    fromTgzFile() to load all non-XML files
+     */
+    function &fromAnyFile($info, $state)
+    {
+        if (is_dir($info)) {
+            $dir_name = realpath($info);
+            if (file_exists($dir_name . '/package.xml')) {
+                $info = PEAR_PackageFile::fromPackageFile($dir_name .  '/package.xml', $state);
+            } elseif (file_exists($dir_name .  '/package2.xml')) {
+                $info = PEAR_PackageFile::fromPackageFile($dir_name .  '/package2.xml', $state);
+            } else {
+                $info = PEAR::raiseError("No package definition found in '$info' directory");
+            }
+
+            return $info;
+        }
+
+        $fp = false;
+        if (is_string($info) && strlen($info) < 255 &&
+             (file_exists($info) || ($fp = @fopen($info, 'r')))
+        ) {
+
+            if ($fp) {
+                fclose($fp);
+            }
+
+            $tmp = substr($info, -4);
+            if ($tmp == '.xml') {
+                $info = &PEAR_PackageFile::fromPackageFile($info, $state);
+            } elseif ($tmp == '.tar' || $tmp == '.tgz') {
+                $info = &PEAR_PackageFile::fromTgzFile($info, $state);
+            } else {
+                $fp   = fopen($info, 'r');
+                $test = fread($fp, 5);
+                fclose($fp);
+                if ($test == '<?xml') {
+                    $info = &PEAR_PackageFile::fromPackageFile($info, $state);
+                } else {
+                    $info = &PEAR_PackageFile::fromTgzFile($info, $state);
+                }
+            }
+
+            return $info;
+        }
+
+        $info = PEAR::raiseError("Cannot open '$info' for parsing");
+        return $info;
+    }
+}
Index: trunk/library/PEAR/PEAR/Installer.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer.php	(revision 5146)
@@ -0,0 +1,1823 @@
+<?php
+/**
+ * PEAR_Installer
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V. Cox <cox@idecnet.com>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Installer.php 313024 2011-07-06 19:51:24Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * Used for installation groups in package.xml 2.0 and platform exceptions
+ */
+require_once 'OS/Guess.php';
+require_once 'PEAR/Downloader.php';
+
+define('PEAR_INSTALLER_NOBINARY', -240);
+/**
+ * Administration class used to install PEAR packages and maintain the
+ * installed package database.
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V. Cox <cox@idecnet.com>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Installer extends PEAR_Downloader
+{
+    // {{{ properties
+
+    /** name of the package directory, for example Foo-1.0
+     * @var string
+     */
+    var $pkgdir;
+
+    /** directory where PHP code files go
+     * @var string
+     */
+    var $phpdir;
+
+    /** directory where PHP extension files go
+     * @var string
+     */
+    var $extdir;
+
+    /** directory where documentation goes
+     * @var string
+     */
+    var $docdir;
+
+    /** installation root directory (ala PHP's INSTALL_ROOT or
+     * automake's DESTDIR
+     * @var string
+     */
+    var $installroot = '';
+
+    /** debug level
+     * @var int
+     */
+    var $debug = 1;
+
+    /** temporary directory
+     * @var string
+     */
+    var $tmpdir;
+
+    /**
+     * PEAR_Registry object used by the installer
+     * @var PEAR_Registry
+     */
+    var $registry;
+
+    /**
+     * array of PEAR_Downloader_Packages
+     * @var array
+     */
+    var $_downloadedPackages;
+
+    /** List of file transactions queued for an install/upgrade/uninstall.
+     *
+     *  Format:
+     *    array(
+     *      0 => array("rename => array("from-file", "to-file")),
+     *      1 => array("delete" => array("file-to-delete")),
+     *      ...
+     *    )
+     *
+     * @var array
+     */
+    var $file_operations = array();
+
+    // }}}
+
+    // {{{ constructor
+
+    /**
+     * PEAR_Installer constructor.
+     *
+     * @param object $ui user interface object (instance of PEAR_Frontend_*)
+     *
+     * @access public
+     */
+    function PEAR_Installer(&$ui)
+    {
+        parent::PEAR_Common();
+        $this->setFrontendObject($ui);
+        $this->debug = $this->config->get('verbose');
+    }
+
+    function setOptions($options)
+    {
+        $this->_options = $options;
+    }
+
+    function setConfig(&$config)
+    {
+        $this->config    = &$config;
+        $this->_registry = &$config->getRegistry();
+    }
+
+    // }}}
+
+    function _removeBackups($files)
+    {
+        foreach ($files as $path) {
+            $this->addFileOperation('removebackup', array($path));
+        }
+    }
+
+    // {{{ _deletePackageFiles()
+
+    /**
+     * Delete a package's installed files, does not remove empty directories.
+     *
+     * @param string package name
+     * @param string channel name
+     * @param bool if true, then files are backed up first
+     * @return bool TRUE on success, or a PEAR error on failure
+     * @access protected
+     */
+    function _deletePackageFiles($package, $channel = false, $backup = false)
+    {
+        if (!$channel) {
+            $channel = 'pear.php.net';
+        }
+
+        if (!strlen($package)) {
+            return $this->raiseError("No package to uninstall given");
+        }
+
+        if (strtolower($package) == 'pear' && $channel == 'pear.php.net') {
+            // to avoid race conditions, include all possible needed files
+            require_once 'PEAR/Task/Common.php';
+            require_once 'PEAR/Task/Replace.php';
+            require_once 'PEAR/Task/Unixeol.php';
+            require_once 'PEAR/Task/Windowseol.php';
+            require_once 'PEAR/PackageFile/v1.php';
+            require_once 'PEAR/PackageFile/v2.php';
+            require_once 'PEAR/PackageFile/Generator/v1.php';
+            require_once 'PEAR/PackageFile/Generator/v2.php';
+        }
+
+        $filelist = $this->_registry->packageInfo($package, 'filelist', $channel);
+        if ($filelist == null) {
+            return $this->raiseError("$channel/$package not installed");
+        }
+
+        $ret = array();
+        foreach ($filelist as $file => $props) {
+            if (empty($props['installed_as'])) {
+                continue;
+            }
+
+            $path = $props['installed_as'];
+            if ($backup) {
+                $this->addFileOperation('backup', array($path));
+                $ret[] = $path;
+            }
+
+            $this->addFileOperation('delete', array($path));
+        }
+
+        if ($backup) {
+            return $ret;
+        }
+
+        return true;
+    }
+
+    // }}}
+    // {{{ _installFile()
+
+    /**
+     * @param string filename
+     * @param array attributes from <file> tag in package.xml
+     * @param string path to install the file in
+     * @param array options from command-line
+     * @access private
+     */
+    function _installFile($file, $atts, $tmp_path, $options)
+    {
+        // {{{ return if this file is meant for another platform
+        static $os;
+        if (!isset($this->_registry)) {
+            $this->_registry = &$this->config->getRegistry();
+        }
+
+        if (isset($atts['platform'])) {
+            if (empty($os)) {
+                $os = new OS_Guess();
+            }
+
+            if (strlen($atts['platform']) && $atts['platform']{0} == '!') {
+                $negate   = true;
+                $platform = substr($atts['platform'], 1);
+            } else {
+                $negate    = false;
+                $platform = $atts['platform'];
+            }
+
+            if ((bool) $os->matchSignature($platform) === $negate) {
+                $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
+                return PEAR_INSTALLER_SKIPPED;
+            }
+        }
+        // }}}
+
+        $channel = $this->pkginfo->getChannel();
+        // {{{ assemble the destination paths
+        switch ($atts['role']) {
+            case 'src':
+            case 'extsrc':
+                $this->source_files++;
+                return;
+            case 'doc':
+            case 'data':
+            case 'test':
+                $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) .
+                            DIRECTORY_SEPARATOR . $this->pkginfo->getPackage();
+                unset($atts['baseinstalldir']);
+                break;
+            case 'ext':
+            case 'php':
+                $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel);
+                break;
+            case 'script':
+                $dest_dir = $this->config->get('bin_dir', null, $channel);
+                break;
+            default:
+                return $this->raiseError("Invalid role `$atts[role]' for file $file");
+        }
+
+        $save_destdir = $dest_dir;
+        if (!empty($atts['baseinstalldir'])) {
+            $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
+        }
+
+        if (dirname($file) != '.' && empty($atts['install-as'])) {
+            $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
+        }
+
+        if (empty($atts['install-as'])) {
+            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
+        } else {
+            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
+        }
+        $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
+
+        // Clean up the DIRECTORY_SEPARATOR mess
+        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
+        list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
+                                                    array(DIRECTORY_SEPARATOR,
+                                                          DIRECTORY_SEPARATOR,
+                                                          DIRECTORY_SEPARATOR),
+                                                    array($dest_file, $orig_file));
+        $final_dest_file = $installed_as = $dest_file;
+        if (isset($this->_options['packagingroot'])) {
+            $installedas_dest_dir  = dirname($final_dest_file);
+            $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
+            $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']);
+        } else {
+            $installedas_dest_dir  = dirname($final_dest_file);
+            $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
+        }
+
+        $dest_dir  = dirname($final_dest_file);
+        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
+        if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
+            return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
+        }
+        // }}}
+
+        if (empty($this->_options['register-only']) &&
+              (!file_exists($dest_dir) || !is_dir($dest_dir))) {
+            if (!$this->mkDirHier($dest_dir)) {
+                return $this->raiseError("failed to mkdir $dest_dir",
+                                         PEAR_INSTALLER_FAILED);
+            }
+            $this->log(3, "+ mkdir $dest_dir");
+        }
+
+        // pretty much nothing happens if we are only registering the install
+        if (empty($this->_options['register-only'])) {
+            if (empty($atts['replacements'])) {
+                if (!file_exists($orig_file)) {
+                    return $this->raiseError("file $orig_file does not exist",
+                                             PEAR_INSTALLER_FAILED);
+                }
+
+                if (!@copy($orig_file, $dest_file)) {
+                    return $this->raiseError("failed to write $dest_file: $php_errormsg",
+                                             PEAR_INSTALLER_FAILED);
+                }
+
+                $this->log(3, "+ cp $orig_file $dest_file");
+                if (isset($atts['md5sum'])) {
+                    $md5sum = md5_file($dest_file);
+                }
+            } else {
+                // {{{ file with replacements
+                if (!file_exists($orig_file)) {
+                    return $this->raiseError("file does not exist",
+                                             PEAR_INSTALLER_FAILED);
+                }
+
+                $contents = file_get_contents($orig_file);
+                if ($contents === false) {
+                    $contents = '';
+                }
+
+                if (isset($atts['md5sum'])) {
+                    $md5sum = md5($contents);
+                }
+
+                $subst_from = $subst_to = array();
+                foreach ($atts['replacements'] as $a) {
+                    $to = '';
+                    if ($a['type'] == 'php-const') {
+                        if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) {
+                            eval("\$to = $a[to];");
+                        } else {
+                            if (!isset($options['soft'])) {
+                                $this->log(0, "invalid php-const replacement: $a[to]");
+                            }
+                            continue;
+                        }
+                    } elseif ($a['type'] == 'pear-config') {
+                        if ($a['to'] == 'master_server') {
+                            $chan = $this->_registry->getChannel($channel);
+                            if (!PEAR::isError($chan)) {
+                                $to = $chan->getServer();
+                            } else {
+                                $to = $this->config->get($a['to'], null, $channel);
+                            }
+                        } else {
+                            $to = $this->config->get($a['to'], null, $channel);
+                        }
+                        if (is_null($to)) {
+                            if (!isset($options['soft'])) {
+                                $this->log(0, "invalid pear-config replacement: $a[to]");
+                            }
+                            continue;
+                        }
+                    } elseif ($a['type'] == 'package-info') {
+                        if ($t = $this->pkginfo->packageInfo($a['to'])) {
+                            $to = $t;
+                        } else {
+                            if (!isset($options['soft'])) {
+                                $this->log(0, "invalid package-info replacement: $a[to]");
+                            }
+                            continue;
+                        }
+                    }
+                    if (!is_null($to)) {
+                        $subst_from[] = $a['from'];
+                        $subst_to[] = $to;
+                    }
+                }
+
+                $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
+                if (sizeof($subst_from)) {
+                    $contents = str_replace($subst_from, $subst_to, $contents);
+                }
+
+                $wp = @fopen($dest_file, "wb");
+                if (!is_resource($wp)) {
+                    return $this->raiseError("failed to create $dest_file: $php_errormsg",
+                                             PEAR_INSTALLER_FAILED);
+                }
+
+                if (@fwrite($wp, $contents) === false) {
+                    return $this->raiseError("failed writing to $dest_file: $php_errormsg",
+                                             PEAR_INSTALLER_FAILED);
+                }
+
+                fclose($wp);
+                // }}}
+            }
+
+            // {{{ check the md5
+            if (isset($md5sum)) {
+                if (strtolower($md5sum) === strtolower($atts['md5sum'])) {
+                    $this->log(2, "md5sum ok: $final_dest_file");
+                } else {
+                    if (empty($options['force'])) {
+                        // delete the file
+                        if (file_exists($dest_file)) {
+                            unlink($dest_file);
+                        }
+
+                        if (!isset($options['ignore-errors'])) {
+                            return $this->raiseError("bad md5sum for file $final_dest_file",
+                                                 PEAR_INSTALLER_FAILED);
+                        }
+
+                        if (!isset($options['soft'])) {
+                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
+                        }
+                    } else {
+                        if (!isset($options['soft'])) {
+                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
+                        }
+                    }
+                }
+            }
+            // }}}
+            // {{{ set file permissions
+            if (!OS_WINDOWS) {
+                if ($atts['role'] == 'script') {
+                    $mode = 0777 & ~(int)octdec($this->config->get('umask'));
+                    $this->log(3, "+ chmod +x $dest_file");
+                } else {
+                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
+                }
+
+                if ($atts['role'] != 'src') {
+                    $this->addFileOperation("chmod", array($mode, $dest_file));
+                    if (!@chmod($dest_file, $mode)) {
+                        if (!isset($options['soft'])) {
+                            $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
+                        }
+                    }
+                }
+            }
+            // }}}
+
+            if ($atts['role'] == 'src') {
+                rename($dest_file, $final_dest_file);
+                $this->log(2, "renamed source file $dest_file to $final_dest_file");
+            } else {
+                $this->addFileOperation("rename", array($dest_file, $final_dest_file,
+                    $atts['role'] == 'ext'));
+            }
+        }
+
+        // Store the full path where the file was installed for easy unistall
+        if ($atts['role'] != 'script') {
+            $loc = $this->config->get($atts['role'] . '_dir');
+        } else {
+            $loc = $this->config->get('bin_dir');
+        }
+
+        if ($atts['role'] != 'src') {
+            $this->addFileOperation("installed_as", array($file, $installed_as,
+                                    $loc,
+                                    dirname(substr($installedas_dest_file, strlen($loc)))));
+        }
+
+        //$this->log(2, "installed: $dest_file");
+        return PEAR_INSTALLER_OK;
+    }
+
+    // }}}
+    // {{{ _installFile2()
+
+    /**
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @param string filename
+     * @param array attributes from <file> tag in package.xml
+     * @param string path to install the file in
+     * @param array options from command-line
+     * @access private
+     */
+    function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options)
+    {
+        $atts = $real_atts;
+        if (!isset($this->_registry)) {
+            $this->_registry = &$this->config->getRegistry();
+        }
+
+        $channel = $pkg->getChannel();
+        // {{{ assemble the destination paths
+        if (!in_array($atts['attribs']['role'],
+              PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
+            return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
+                    "' for file $file");
+        }
+
+        $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
+        $err  = $role->setup($this, $pkg, $atts['attribs'], $file);
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+
+        if (!$role->isInstallable()) {
+            return;
+        }
+
+        $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
+        if (PEAR::isError($info)) {
+            return $info;
+        }
+
+        list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
+        if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
+            return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
+        }
+
+        $final_dest_file = $installed_as = $dest_file;
+        if (isset($this->_options['packagingroot'])) {
+            $final_dest_file = $this->_prependPath($final_dest_file,
+                $this->_options['packagingroot']);
+        }
+
+        $dest_dir  = dirname($final_dest_file);
+        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
+        // }}}
+
+        if (empty($this->_options['register-only'])) {
+            if (!file_exists($dest_dir) || !is_dir($dest_dir)) {
+                if (!$this->mkDirHier($dest_dir)) {
+                    return $this->raiseError("failed to mkdir $dest_dir",
+                                             PEAR_INSTALLER_FAILED);
+                }
+                $this->log(3, "+ mkdir $dest_dir");
+            }
+        }
+
+        $attribs = $atts['attribs'];
+        unset($atts['attribs']);
+        // pretty much nothing happens if we are only registering the install
+        if (empty($this->_options['register-only'])) {
+            if (!count($atts)) { // no tasks
+                if (!file_exists($orig_file)) {
+                    return $this->raiseError("file $orig_file does not exist",
+                                             PEAR_INSTALLER_FAILED);
+                }
+
+                if (!@copy($orig_file, $dest_file)) {
+                    return $this->raiseError("failed to write $dest_file: $php_errormsg",
+                                             PEAR_INSTALLER_FAILED);
+                }
+
+                $this->log(3, "+ cp $orig_file $dest_file");
+                if (isset($attribs['md5sum'])) {
+                    $md5sum = md5_file($dest_file);
+                }
+            } else { // file with tasks
+                if (!file_exists($orig_file)) {
+                    return $this->raiseError("file $orig_file does not exist",
+                                             PEAR_INSTALLER_FAILED);
+                }
+
+                $contents = file_get_contents($orig_file);
+                if ($contents === false) {
+                    $contents = '';
+                }
+
+                if (isset($attribs['md5sum'])) {
+                    $md5sum = md5($contents);
+                }
+
+                foreach ($atts as $tag => $raw) {
+                    $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag);
+                    $task = "PEAR_Task_$tag";
+                    $task = &new $task($this->config, $this, PEAR_TASK_INSTALL);
+                    if (!$task->isScript()) { // scripts are only handled after installation
+                        $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
+                        $res = $task->startSession($pkg, $contents, $final_dest_file);
+                        if ($res === false) {
+                            continue; // skip this file
+                        }
+
+                        if (PEAR::isError($res)) {
+                            return $res;
+                        }
+
+                        $contents = $res; // save changes
+                    }
+
+                    $wp = @fopen($dest_file, "wb");
+                    if (!is_resource($wp)) {
+                        return $this->raiseError("failed to create $dest_file: $php_errormsg",
+                                                 PEAR_INSTALLER_FAILED);
+                    }
+
+                    if (fwrite($wp, $contents) === false) {
+                        return $this->raiseError("failed writing to $dest_file: $php_errormsg",
+                                                 PEAR_INSTALLER_FAILED);
+                    }
+
+                    fclose($wp);
+                }
+            }
+
+            // {{{ check the md5
+            if (isset($md5sum)) {
+                // Make sure the original md5 sum matches with expected
+                if (strtolower($md5sum) === strtolower($attribs['md5sum'])) {
+                    $this->log(2, "md5sum ok: $final_dest_file");
+
+                    if (isset($contents)) {
+                        // set md5 sum based on $content in case any tasks were run.
+                        $real_atts['attribs']['md5sum'] = md5($contents);
+                    }
+                } else {
+                    if (empty($options['force'])) {
+                        // delete the file
+                        if (file_exists($dest_file)) {
+                            unlink($dest_file);
+                        }
+
+                        if (!isset($options['ignore-errors'])) {
+                            return $this->raiseError("bad md5sum for file $final_dest_file",
+                                                     PEAR_INSTALLER_FAILED);
+                        }
+
+                        if (!isset($options['soft'])) {
+                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
+                        }
+                    } else {
+                        if (!isset($options['soft'])) {
+                            $this->log(0, "warning : bad md5sum for file $final_dest_file");
+                        }
+                    }
+                }
+            } else {
+                $real_atts['attribs']['md5sum'] = md5_file($dest_file);
+            }
+
+            // }}}
+            // {{{ set file permissions
+            if (!OS_WINDOWS) {
+                if ($role->isExecutable()) {
+                    $mode = 0777 & ~(int)octdec($this->config->get('umask'));
+                    $this->log(3, "+ chmod +x $dest_file");
+                } else {
+                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
+                }
+
+                if ($attribs['role'] != 'src') {
+                    $this->addFileOperation("chmod", array($mode, $dest_file));
+                    if (!@chmod($dest_file, $mode)) {
+                        if (!isset($options['soft'])) {
+                            $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
+                        }
+                    }
+                }
+            }
+            // }}}
+
+            if ($attribs['role'] == 'src') {
+                rename($dest_file, $final_dest_file);
+                $this->log(2, "renamed source file $dest_file to $final_dest_file");
+            } else {
+                $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
+            }
+        }
+
+        // Store the full path where the file was installed for easy uninstall
+        if ($attribs['role'] != 'src') {
+            $loc = $this->config->get($role->getLocationConfig(), null, $channel);
+            $this->addFileOperation('installed_as', array($file, $installed_as,
+                                $loc,
+                                dirname(substr($installed_as, strlen($loc)))));
+        }
+
+        //$this->log(2, "installed: $dest_file");
+        return PEAR_INSTALLER_OK;
+    }
+
+    // }}}
+    // {{{ addFileOperation()
+
+    /**
+     * Add a file operation to the current file transaction.
+     *
+     * @see startFileTransaction()
+     * @param string $type This can be one of:
+     *    - rename:  rename a file ($data has 3 values)
+     *    - backup:  backup an existing file ($data has 1 value)
+     *    - removebackup:  clean up backups created during install ($data has 1 value)
+     *    - chmod:   change permissions on a file ($data has 2 values)
+     *    - delete:  delete a file ($data has 1 value)
+     *    - rmdir:   delete a directory if empty ($data has 1 value)
+     *    - installed_as: mark a file as installed ($data has 4 values).
+     * @param array $data For all file operations, this array must contain the
+     *    full path to the file or directory that is being operated on.  For
+     *    the rename command, the first parameter must be the file to rename,
+     *    the second its new name, the third whether this is a PHP extension.
+     *
+     *    The installed_as operation contains 4 elements in this order:
+     *    1. Filename as listed in the filelist element from package.xml
+     *    2. Full path to the installed file
+     *    3. Full path from the php_dir configuration variable used in this
+     *       installation
+     *    4. Relative path from the php_dir that this file is installed in
+     */
+    function addFileOperation($type, $data)
+    {
+        if (!is_array($data)) {
+            return $this->raiseError('Internal Error: $data in addFileOperation'
+                . ' must be an array, was ' . gettype($data));
+        }
+
+        if ($type == 'chmod') {
+            $octmode = decoct($data[0]);
+            $this->log(3, "adding to transaction: $type $octmode $data[1]");
+        } else {
+            $this->log(3, "adding to transaction: $type " . implode(" ", $data));
+        }
+        $this->file_operations[] = array($type, $data);
+    }
+
+    // }}}
+    // {{{ startFileTransaction()
+
+    function startFileTransaction($rollback_in_case = false)
+    {
+        if (count($this->file_operations) && $rollback_in_case) {
+            $this->rollbackFileTransaction();
+        }
+        $this->file_operations = array();
+    }
+
+    // }}}
+    // {{{ commitFileTransaction()
+
+    function commitFileTransaction()
+    {
+        // {{{ first, check permissions and such manually
+        $errors = array();
+        foreach ($this->file_operations as $key => $tr) {
+            list($type, $data) = $tr;
+            switch ($type) {
+                case 'rename':
+                    if (!file_exists($data[0])) {
+                        $errors[] = "cannot rename file $data[0], doesn't exist";
+                    }
+
+                    // check that dest dir. is writable
+                    if (!is_writable(dirname($data[1]))) {
+                        $errors[] = "permission denied ($type): $data[1]";
+                    }
+                    break;
+                case 'chmod':
+                    // check that file is writable
+                    if (!is_writable($data[1])) {
+                        $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
+                    }
+                    break;
+                case 'delete':
+                    if (!file_exists($data[0])) {
+                        $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
+                    }
+                    // check that directory is writable
+                    if (file_exists($data[0])) {
+                        if (!is_writable(dirname($data[0]))) {
+                            $errors[] = "permission denied ($type): $data[0]";
+                        } else {
+                            // make sure the file to be deleted can be opened for writing
+                            $fp = false;
+                            if (!is_dir($data[0]) &&
+                                  (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) {
+                                $errors[] = "permission denied ($type): $data[0]";
+                            } elseif ($fp) {
+                                fclose($fp);
+                            }
+                        }
+
+                        /* Verify we are not deleting a file owned by another package
+                         * This can happen when a file moves from package A to B in
+                         * an upgrade ala http://pear.php.net/17986
+                         */
+                        $info = array(
+                            'package' => strtolower($this->pkginfo->getName()),
+                            'channel' => strtolower($this->pkginfo->getChannel()),
+                        );
+                        $result = $this->_registry->checkFileMap($data[0], $info, '1.1');
+                        if (is_array($result)) {
+                            $res = array_diff($result, $info);
+                            if (!empty($res)) {
+                                $new = $this->_registry->getPackage($result[1], $result[0]);
+                                $this->file_operations[$key] = false;
+                                $this->log(3, "file $data[0] was scheduled for removal from {$this->pkginfo->getName()} but is owned by {$new->getChannel()}/{$new->getName()}, removal has been cancelled.");
+                            }
+                        }
+                    }
+                    break;
+            }
+
+        }
+        // }}}
+
+        $n = count($this->file_operations);
+        $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName());
+
+        $m = count($errors);
+        if ($m > 0) {
+            foreach ($errors as $error) {
+                if (!isset($this->_options['soft'])) {
+                    $this->log(1, $error);
+                }
+            }
+
+            if (!isset($this->_options['ignore-errors'])) {
+                return false;
+            }
+        }
+
+        $this->_dirtree = array();
+        // {{{ really commit the transaction
+        foreach ($this->file_operations as $i => $tr) {
+            if (!$tr) {
+                // support removal of non-existing backups
+                continue;
+            }
+
+            list($type, $data) = $tr;
+            switch ($type) {
+                case 'backup':
+                    if (!file_exists($data[0])) {
+                        $this->file_operations[$i] = false;
+                        break;
+                    }
+
+                    if (!@copy($data[0], $data[0] . '.bak')) {
+                        $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] .
+                            '.bak ' . $php_errormsg);
+                        return false;
+                    }
+                    $this->log(3, "+ backup $data[0] to $data[0].bak");
+                    break;
+                case 'removebackup':
+                    if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
+                        unlink($data[0] . '.bak');
+                        $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
+                    }
+                    break;
+                case 'rename':
+                    $test = file_exists($data[1]) ? @unlink($data[1]) : null;
+                    if (!$test && file_exists($data[1])) {
+                        if ($data[2]) {
+                            $extra = ', this extension must be installed manually.  Rename to "' .
+                                basename($data[1]) . '"';
+                        } else {
+                            $extra = '';
+                        }
+
+                        if (!isset($this->_options['soft'])) {
+                            $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
+                                $data[0] . $extra);
+                        }
+
+                        if (!isset($this->_options['ignore-errors'])) {
+                            return false;
+                        }
+                    }
+
+                    // permissions issues with rename - copy() is far superior
+                    $perms = @fileperms($data[0]);
+                    if (!@copy($data[0], $data[1])) {
+                        $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] .
+                            ' ' . $php_errormsg);
+                        return false;
+                    }
+
+                    // copy over permissions, otherwise they are lost
+                    @chmod($data[1], $perms);
+                    @unlink($data[0]);
+                    $this->log(3, "+ mv $data[0] $data[1]");
+                    break;
+                case 'chmod':
+                    if (!@chmod($data[1], $data[0])) {
+                        $this->log(1, 'Could not chmod ' . $data[1] . ' to ' .
+                            decoct($data[0]) . ' ' . $php_errormsg);
+                        return false;
+                    }
+
+                    $octmode = decoct($data[0]);
+                    $this->log(3, "+ chmod $octmode $data[1]");
+                    break;
+                case 'delete':
+                    if (file_exists($data[0])) {
+                        if (!@unlink($data[0])) {
+                            $this->log(1, 'Could not delete ' . $data[0] . ' ' .
+                                $php_errormsg);
+                            return false;
+                        }
+                        $this->log(3, "+ rm $data[0]");
+                    }
+                    break;
+                case 'rmdir':
+                    if (file_exists($data[0])) {
+                        do {
+                            $testme = opendir($data[0]);
+                            while (false !== ($entry = readdir($testme))) {
+                                if ($entry == '.' || $entry == '..') {
+                                    continue;
+                                }
+                                closedir($testme);
+                                break 2; // this directory is not empty and can't be
+                                         // deleted
+                            }
+
+                            closedir($testme);
+                            if (!@rmdir($data[0])) {
+                                $this->log(1, 'Could not rmdir ' . $data[0] . ' ' .
+                                    $php_errormsg);
+                                return false;
+                            }
+                            $this->log(3, "+ rmdir $data[0]");
+                        } while (false);
+                    }
+                    break;
+                case 'installed_as':
+                    $this->pkginfo->setInstalledAs($data[0], $data[1]);
+                    if (!isset($this->_dirtree[dirname($data[1])])) {
+                        $this->_dirtree[dirname($data[1])] = true;
+                        $this->pkginfo->setDirtree(dirname($data[1]));
+
+                        while(!empty($data[3]) && dirname($data[3]) != $data[3] &&
+                                $data[3] != '/' && $data[3] != '\\') {
+                            $this->pkginfo->setDirtree($pp =
+                                $this->_prependPath($data[3], $data[2]));
+                            $this->_dirtree[$pp] = true;
+                            $data[3] = dirname($data[3]);
+                        }
+                    }
+                    break;
+            }
+        }
+        // }}}
+        $this->log(2, "successfully committed $n file operations");
+        $this->file_operations = array();
+        return true;
+    }
+
+    // }}}
+    // {{{ rollbackFileTransaction()
+
+    function rollbackFileTransaction()
+    {
+        $n = count($this->file_operations);
+        $this->log(2, "rolling back $n file operations");
+        foreach ($this->file_operations as $tr) {
+            list($type, $data) = $tr;
+            switch ($type) {
+                case 'backup':
+                    if (file_exists($data[0] . '.bak')) {
+                        if (file_exists($data[0] && is_writable($data[0]))) {
+                            unlink($data[0]);
+                        }
+                        @copy($data[0] . '.bak', $data[0]);
+                        $this->log(3, "+ restore $data[0] from $data[0].bak");
+                    }
+                    break;
+                case 'removebackup':
+                    if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
+                        unlink($data[0] . '.bak');
+                        $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
+                    }
+                    break;
+                case 'rename':
+                    @unlink($data[0]);
+                    $this->log(3, "+ rm $data[0]");
+                    break;
+                case 'mkdir':
+                    @rmdir($data[0]);
+                    $this->log(3, "+ rmdir $data[0]");
+                    break;
+                case 'chmod':
+                    break;
+                case 'delete':
+                    break;
+                case 'installed_as':
+                    $this->pkginfo->setInstalledAs($data[0], false);
+                    break;
+            }
+        }
+        $this->pkginfo->resetDirtree();
+        $this->file_operations = array();
+    }
+
+    // }}}
+    // {{{ mkDirHier($dir)
+
+    function mkDirHier($dir)
+    {
+        $this->addFileOperation('mkdir', array($dir));
+        return parent::mkDirHier($dir);
+    }
+
+    // }}}
+    // {{{ download()
+
+    /**
+     * Download any files and their dependencies, if necessary
+     *
+     * @param array a mixed list of package names, local files, or package.xml
+     * @param PEAR_Config
+     * @param array options from the command line
+     * @param array this is the array that will be populated with packages to
+     *              install.  Format of each entry:
+     *
+     * <code>
+     * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+     *    'info' => array() // parsed package.xml
+     * );
+     * </code>
+     * @param array this will be populated with any error messages
+     * @param false private recursion variable
+     * @param false private recursion variable
+     * @param false private recursion variable
+     * @deprecated in favor of PEAR_Downloader
+     */
+    function download($packages, $options, &$config, &$installpackages,
+                      &$errors, $installed = false, $willinstall = false, $state = false)
+    {
+        // trickiness: initialize here
+        parent::PEAR_Downloader($this->ui, $options, $config);
+        $ret             = parent::download($packages);
+        $errors          = $this->getErrorMsgs();
+        $installpackages = $this->getDownloadedPackages();
+        trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
+                      "in favor of PEAR_Downloader class", E_USER_WARNING);
+        return $ret;
+    }
+
+    // }}}
+    // {{{ _parsePackageXml()
+
+    function _parsePackageXml(&$descfile)
+    {
+        // Parse xml file -----------------------------------------------
+        $pkg = new PEAR_PackageFile($this->config, $this->debug);
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
+        PEAR::staticPopErrorHandling();
+        if (PEAR::isError($p)) {
+            if (is_array($p->getUserInfo())) {
+                foreach ($p->getUserInfo() as $err) {
+                    $loglevel = $err['level'] == 'error' ? 0 : 1;
+                    if (!isset($this->_options['soft'])) {
+                        $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
+                    }
+                }
+            }
+            return $this->raiseError('Installation failed: invalid package file');
+        }
+
+        $descfile = $p->getPackageFile();
+        return $p;
+    }
+
+    // }}}
+    /**
+     * Set the list of PEAR_Downloader_Package objects to allow more sane
+     * dependency validation
+     * @param array
+     */
+    function setDownloadedPackages(&$pkgs)
+    {
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+        $err = $this->analyzeDependencies($pkgs);
+        PEAR::popErrorHandling();
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+        $this->_downloadedPackages = &$pkgs;
+    }
+
+    /**
+     * Set the list of PEAR_Downloader_Package objects to allow more sane
+     * dependency validation
+     * @param array
+     */
+    function setUninstallPackages(&$pkgs)
+    {
+        $this->_downloadedPackages = &$pkgs;
+    }
+
+    function getInstallPackages()
+    {
+        return $this->_downloadedPackages;
+    }
+
+    // {{{ install()
+
+    /**
+     * Installs the files within the package file specified.
+     *
+     * @param string|PEAR_Downloader_Package $pkgfile path to the package file,
+     *        or a pre-initialized packagefile object
+     * @param array $options
+     * recognized options:
+     * - installroot   : optional prefix directory for installation
+     * - force         : force installation
+     * - register-only : update registry but don't install files
+     * - upgrade       : upgrade existing install
+     * - soft          : fail silently
+     * - nodeps        : ignore dependency conflicts/missing dependencies
+     * - alldeps       : install all dependencies
+     * - onlyreqdeps   : install only required dependencies
+     *
+     * @return array|PEAR_Error package info if successful
+     */
+    function install($pkgfile, $options = array())
+    {
+        $this->_options = $options;
+        $this->_registry = &$this->config->getRegistry();
+        if (is_object($pkgfile)) {
+            $dlpkg    = &$pkgfile;
+            $pkg      = $pkgfile->getPackageFile();
+            $pkgfile  = $pkg->getArchiveFile();
+            $descfile = $pkg->getPackageFile();
+        } else {
+            $descfile = $pkgfile;
+            $pkg      = $this->_parsePackageXml($descfile);
+            if (PEAR::isError($pkg)) {
+                return $pkg;
+            }
+        }
+
+        $tmpdir = dirname($descfile);
+        if (realpath($descfile) != realpath($pkgfile)) {
+            // Use the temp_dir since $descfile can contain the download dir path
+            $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net');
+            $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"');
+
+            $tar = new Archive_Tar($pkgfile);
+            if (!$tar->extract($tmpdir)) {
+                return $this->raiseError("unable to unpack $pkgfile");
+            }
+        }
+
+        $pkgname = $pkg->getName();
+        $channel = $pkg->getChannel();
+        if (isset($this->_options['packagingroot'])) {
+            $regdir = $this->_prependPath(
+                $this->config->get('php_dir', null, 'pear.php.net'),
+                $this->_options['packagingroot']);
+
+            $packrootphp_dir = $this->_prependPath(
+                $this->config->get('php_dir', null, $channel),
+                $this->_options['packagingroot']);
+        }
+
+        if (isset($options['installroot'])) {
+            $this->config->setInstallRoot($options['installroot']);
+            $this->_registry = &$this->config->getRegistry();
+            $installregistry = &$this->_registry;
+            $this->installroot = ''; // all done automagically now
+            $php_dir = $this->config->get('php_dir', null, $channel);
+        } else {
+            $this->config->setInstallRoot(false);
+            $this->_registry = &$this->config->getRegistry();
+            if (isset($this->_options['packagingroot'])) {
+                $installregistry = &new PEAR_Registry($regdir);
+                if (!$installregistry->channelExists($channel, true)) {
+                    // we need to fake a channel-discover of this channel
+                    $chanobj = $this->_registry->getChannel($channel, true);
+                    $installregistry->addChannel($chanobj);
+                }
+                $php_dir = $packrootphp_dir;
+            } else {
+                $installregistry = &$this->_registry;
+                $php_dir = $this->config->get('php_dir', null, $channel);
+            }
+            $this->installroot = '';
+        }
+
+        // {{{ checks to do when not in "force" mode
+        if (empty($options['force']) &&
+              (file_exists($this->config->get('php_dir')) &&
+               is_dir($this->config->get('php_dir')))) {
+            $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
+            $instfilelist = $pkg->getInstallationFileList(true);
+            if (PEAR::isError($instfilelist)) {
+                return $instfilelist;
+            }
+
+            // ensure we have the most accurate registry
+            $installregistry->flushFileMap();
+            $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
+            if (PEAR::isError($test)) {
+                return $test;
+            }
+
+            if (sizeof($test)) {
+                $pkgs = $this->getInstallPackages();
+                $found = false;
+                foreach ($pkgs as $param) {
+                    if ($pkg->isSubpackageOf($param)) {
+                        $found = true;
+                        break;
+                    }
+                }
+
+                if ($found) {
+                    // subpackages can conflict with earlier versions of parent packages
+                    $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
+                    $tmp = $test;
+                    foreach ($tmp as $file => $info) {
+                        if (is_array($info)) {
+                            if (strtolower($info[1]) == strtolower($param->getPackage()) &&
+                                  strtolower($info[0]) == strtolower($param->getChannel())
+                            ) {
+                                if (isset($parentreg['filelist'][$file])) {
+                                    unset($parentreg['filelist'][$file]);
+                                } else{
+                                    $pos     = strpos($file, '/');
+                                    $basedir = substr($file, 0, $pos);
+                                    $file2   = substr($file, $pos + 1);
+                                    if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
+                                        && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
+                                    ) {
+                                        unset($parentreg['filelist'][$file2]);
+                                    }
+                                }
+
+                                unset($test[$file]);
+                            }
+                        } else {
+                            if (strtolower($param->getChannel()) != 'pear.php.net') {
+                                continue;
+                            }
+
+                            if (strtolower($info) == strtolower($param->getPackage())) {
+                                if (isset($parentreg['filelist'][$file])) {
+                                    unset($parentreg['filelist'][$file]);
+                                } else{
+                                    $pos     = strpos($file, '/');
+                                    $basedir = substr($file, 0, $pos);
+                                    $file2   = substr($file, $pos + 1);
+                                    if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
+                                        && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
+                                    ) {
+                                        unset($parentreg['filelist'][$file2]);
+                                    }
+                                }
+
+                                unset($test[$file]);
+                            }
+                        }
+                    }
+
+                    $pfk = &new PEAR_PackageFile($this->config);
+                    $parentpkg = &$pfk->fromArray($parentreg);
+                    $installregistry->updatePackage2($parentpkg);
+                }
+
+                if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
+                    $tmp = $test;
+                    foreach ($tmp as $file => $info) {
+                        if (is_string($info)) {
+                            // pear.php.net packages are always stored as strings
+                            if (strtolower($info) == strtolower($param->getPackage())) {
+                                // upgrading existing package
+                                unset($test[$file]);
+                            }
+                        }
+                    }
+                }
+
+                if (count($test)) {
+                    $msg = "$channel/$pkgname: conflicting files found:\n";
+                    $longest = max(array_map("strlen", array_keys($test)));
+                    $fmt = "%${longest}s (%s)\n";
+                    foreach ($test as $file => $info) {
+                        if (!is_array($info)) {
+                            $info = array('pear.php.net', $info);
+                        }
+                        $info = $info[0] . '/' . $info[1];
+                        $msg .= sprintf($fmt, $file, $info);
+                    }
+
+                    if (!isset($options['ignore-errors'])) {
+                        return $this->raiseError($msg);
+                    }
+
+                    if (!isset($options['soft'])) {
+                        $this->log(0, "WARNING: $msg");
+                    }
+                }
+            }
+        }
+        // }}}
+
+        $this->startFileTransaction();
+
+        $usechannel = $channel;
+        if ($channel == 'pecl.php.net') {
+            $test = $installregistry->packageExists($pkgname, $channel);
+            if (!$test) {
+                $test = $installregistry->packageExists($pkgname, 'pear.php.net');
+                $usechannel = 'pear.php.net';
+            }
+        } else {
+            $test = $installregistry->packageExists($pkgname, $channel);
+        }
+
+        if (empty($options['upgrade']) && empty($options['soft'])) {
+            // checks to do only when installing new packages
+            if (empty($options['force']) && $test) {
+                return $this->raiseError("$channel/$pkgname is already installed");
+            }
+        } else {
+            // Upgrade
+            if ($test) {
+                $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
+                $v2 = $pkg->getVersion();
+                $cmp = version_compare("$v1", "$v2", 'gt');
+                if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
+                    return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
+                }
+            }
+        }
+
+        // Do cleanups for upgrade and install, remove old release's files first
+        if ($test && empty($options['register-only'])) {
+            // when upgrading, remove old release's files first:
+            if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel,
+                  true))) {
+                if (!isset($options['ignore-errors'])) {
+                    return $this->raiseError($err);
+                }
+
+                if (!isset($options['soft'])) {
+                    $this->log(0, 'WARNING: ' . $err->getMessage());
+                }
+            } else {
+                $backedup = $err;
+            }
+        }
+
+        // {{{ Copy files to dest dir ---------------------------------------
+
+        // info from the package it self we want to access from _installFile
+        $this->pkginfo = &$pkg;
+        // used to determine whether we should build any C code
+        $this->source_files = 0;
+
+        $savechannel = $this->config->get('default_channel');
+        if (empty($options['register-only']) && !is_dir($php_dir)) {
+            if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
+                return $this->raiseError("no installation destination directory '$php_dir'\n");
+            }
+        }
+
+        if (substr($pkgfile, -4) != '.xml') {
+            $tmpdir .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
+        }
+
+        $this->configSet('default_channel', $channel);
+        // {{{ install files
+
+        $ver = $pkg->getPackagexmlVersion();
+        if (version_compare($ver, '2.0', '>=')) {
+            $filelist = $pkg->getInstallationFilelist();
+        } else {
+            $filelist = $pkg->getFileList();
+        }
+
+        if (PEAR::isError($filelist)) {
+            return $filelist;
+        }
+
+        $p = &$installregistry->getPackage($pkgname, $channel);
+        $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false;
+
+        $pkg->resetFilelist();
+        $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
+            'version', $pkg->getChannel()));
+        foreach ($filelist as $file => $atts) {
+            $this->expectError(PEAR_INSTALLER_FAILED);
+            if ($pkg->getPackagexmlVersion() == '1.0') {
+                $res = $this->_installFile($file, $atts, $tmpdir, $options);
+            } else {
+                $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options);
+            }
+            $this->popExpect();
+
+            if (PEAR::isError($res)) {
+                if (empty($options['ignore-errors'])) {
+                    $this->rollbackFileTransaction();
+                    if ($res->getMessage() == "file does not exist") {
+                        $this->raiseError("file $file in package.xml does not exist");
+                    }
+
+                    return $this->raiseError($res);
+                }
+
+                if (!isset($options['soft'])) {
+                    $this->log(0, "Warning: " . $res->getMessage());
+                }
+            }
+
+            $real = isset($atts['attribs']) ? $atts['attribs'] : $atts;
+            if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') {
+                // Register files that were installed
+                $pkg->installedFile($file, $atts);
+            }
+        }
+        // }}}
+
+        // {{{ compile and install source files
+        if ($this->source_files > 0 && empty($options['nobuild'])) {
+            if (PEAR::isError($err =
+                  $this->_compileSourceFiles($savechannel, $pkg))) {
+                return $err;
+            }
+        }
+        // }}}
+
+        if (isset($backedup)) {
+            $this->_removeBackups($backedup);
+        }
+
+        if (!$this->commitFileTransaction()) {
+            $this->rollbackFileTransaction();
+            $this->configSet('default_channel', $savechannel);
+            return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
+        }
+        // }}}
+
+        $ret          = false;
+        $installphase = 'install';
+        $oldversion   = false;
+        // {{{ Register that the package is installed -----------------------
+        if (empty($options['upgrade'])) {
+            // if 'force' is used, replace the info in registry
+            $usechannel = $channel;
+            if ($channel == 'pecl.php.net') {
+                $test = $installregistry->packageExists($pkgname, $channel);
+                if (!$test) {
+                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
+                    $usechannel = 'pear.php.net';
+                }
+            } else {
+                $test = $installregistry->packageExists($pkgname, $channel);
+            }
+
+            if (!empty($options['force']) && $test) {
+                $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
+                $installregistry->deletePackage($pkgname, $usechannel);
+            }
+            $ret = $installregistry->addPackage2($pkg);
+        } else {
+            if ($dirtree) {
+                $this->startFileTransaction();
+                // attempt to delete empty directories
+                uksort($dirtree, array($this, '_sortDirs'));
+                foreach($dirtree as $dir => $notused) {
+                    $this->addFileOperation('rmdir', array($dir));
+                }
+                $this->commitFileTransaction();
+            }
+
+            $usechannel = $channel;
+            if ($channel == 'pecl.php.net') {
+                $test = $installregistry->packageExists($pkgname, $channel);
+                if (!$test) {
+                    $test = $installregistry->packageExists($pkgname, 'pear.php.net');
+                    $usechannel = 'pear.php.net';
+                }
+            } else {
+                $test = $installregistry->packageExists($pkgname, $channel);
+            }
+
+            // new: upgrade installs a package if it isn't installed
+            if (!$test) {
+                $ret = $installregistry->addPackage2($pkg);
+            } else {
+                if ($usechannel != $channel) {
+                    $installregistry->deletePackage($pkgname, $usechannel);
+                    $ret = $installregistry->addPackage2($pkg);
+                } else {
+                    $ret = $installregistry->updatePackage2($pkg);
+                }
+                $installphase = 'upgrade';
+            }
+        }
+
+        if (!$ret) {
+            $this->configSet('default_channel', $savechannel);
+            return $this->raiseError("Adding package $channel/$pkgname to registry failed");
+        }
+        // }}}
+
+        $this->configSet('default_channel', $savechannel);
+        if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
+            if (PEAR_Task_Common::hasPostinstallTasks()) {
+                PEAR_Task_Common::runPostinstallTasks($installphase);
+            }
+        }
+
+        return $pkg->toArray(true);
+    }
+
+    // }}}
+
+    // {{{ _compileSourceFiles()
+    /**
+     * @param string
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     */
+    function _compileSourceFiles($savechannel, &$filelist)
+    {
+        require_once 'PEAR/Builder.php';
+        $this->log(1, "$this->source_files source files, building");
+        $bob = &new PEAR_Builder($this->ui);
+        $bob->debug = $this->debug;
+        $built = $bob->build($filelist, array(&$this, '_buildCallback'));
+        if (PEAR::isError($built)) {
+            $this->rollbackFileTransaction();
+            $this->configSet('default_channel', $savechannel);
+            return $built;
+        }
+
+        $this->log(1, "\nBuild process completed successfully");
+        foreach ($built as $ext) {
+            $bn = basename($ext['file']);
+            list($_ext_name, $_ext_suff) = explode('.', $bn);
+            if ($_ext_suff == '.so' || $_ext_suff == '.dll') {
+                if (extension_loaded($_ext_name)) {
+                    $this->raiseError("Extension '$_ext_name' already loaded. " .
+                                      'Please unload it in your php.ini file ' .
+                                      'prior to install or upgrade');
+                }
+                $role = 'ext';
+            } else {
+                $role = 'src';
+            }
+
+            $dest = $ext['dest'];
+            $packagingroot = '';
+            if (isset($this->_options['packagingroot'])) {
+                $packagingroot = $this->_options['packagingroot'];
+            }
+
+            $copyto = $this->_prependPath($dest, $packagingroot);
+            $extra  = $copyto != $dest ? " as '$copyto'" : '';
+            $this->log(1, "Installing '$dest'$extra");
+
+            $copydir = dirname($copyto);
+            // pretty much nothing happens if we are only registering the install
+            if (empty($this->_options['register-only'])) {
+                if (!file_exists($copydir) || !is_dir($copydir)) {
+                    if (!$this->mkDirHier($copydir)) {
+                        return $this->raiseError("failed to mkdir $copydir",
+                            PEAR_INSTALLER_FAILED);
+                    }
+
+                    $this->log(3, "+ mkdir $copydir");
+                }
+
+                if (!@copy($ext['file'], $copyto)) {
+                    return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED);
+                }
+
+                $this->log(3, "+ cp $ext[file] $copyto");
+                $this->addFileOperation('rename', array($ext['file'], $copyto));
+                if (!OS_WINDOWS) {
+                    $mode = 0666 & ~(int)octdec($this->config->get('umask'));
+                    $this->addFileOperation('chmod', array($mode, $copyto));
+                    if (!@chmod($copyto, $mode)) {
+                        $this->log(0, "failed to change mode of $copyto ($php_errormsg)");
+                    }
+                }
+            }
+
+
+            $data = array(
+                'role'         => $role,
+                'name'         => $bn,
+                'installed_as' => $dest,
+                'php_api'      => $ext['php_api'],
+                'zend_mod_api' => $ext['zend_mod_api'],
+                'zend_ext_api' => $ext['zend_ext_api'],
+            );
+
+            if ($filelist->getPackageXmlVersion() == '1.0') {
+                $filelist->installedFile($bn, $data);
+            } else {
+                $filelist->installedFile($bn, array('attribs' => $data));
+            }
+        }
+    }
+
+    // }}}
+    function &getUninstallPackages()
+    {
+        return $this->_downloadedPackages;
+    }
+    // {{{ uninstall()
+
+    /**
+     * Uninstall a package
+     *
+     * This method removes all files installed by the application, and then
+     * removes any empty directories.
+     * @param string package name
+     * @param array Command-line options.  Possibilities include:
+     *
+     *              - installroot: base installation dir, if not the default
+     *              - register-only : update registry but don't remove files
+     *              - nodeps: do not process dependencies of other packages to ensure
+     *                        uninstallation does not break things
+     */
+    function uninstall($package, $options = array())
+    {
+        $installRoot = isset($options['installroot']) ? $options['installroot'] : '';
+        $this->config->setInstallRoot($installRoot);
+
+        $this->installroot = '';
+        $this->_registry = &$this->config->getRegistry();
+        if (is_object($package)) {
+            $channel = $package->getChannel();
+            $pkg     = $package;
+            $package = $pkg->getPackage();
+        } else {
+            $pkg = false;
+            $info = $this->_registry->parsePackageName($package,
+                $this->config->get('default_channel'));
+            $channel = $info['channel'];
+            $package = $info['package'];
+        }
+
+        $savechannel = $this->config->get('default_channel');
+        $this->configSet('default_channel', $channel);
+        if (!is_object($pkg)) {
+            $pkg = $this->_registry->getPackage($package, $channel);
+        }
+
+        if (!$pkg) {
+            $this->configSet('default_channel', $savechannel);
+            return $this->raiseError($this->_registry->parsedPackageNameToString(
+                array(
+                    'channel' => $channel,
+                    'package' => $package
+                ), true) . ' not installed');
+        }
+
+        if ($pkg->getInstalledBinary()) {
+            // this is just an alias for a binary package
+            return $this->_registry->deletePackage($package, $channel);
+        }
+
+        $filelist = $pkg->getFilelist();
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        if (!class_exists('PEAR_Dependency2')) {
+            require_once 'PEAR/Dependency2.php';
+        }
+
+        $depchecker = &new PEAR_Dependency2($this->config, $options,
+            array('channel' => $channel, 'package' => $package),
+            PEAR_VALIDATE_UNINSTALLING);
+        $e = $depchecker->validatePackageUninstall($this);
+        PEAR::staticPopErrorHandling();
+        if (PEAR::isError($e)) {
+            if (!isset($options['ignore-errors'])) {
+                return $this->raiseError($e);
+            }
+
+            if (!isset($options['soft'])) {
+                $this->log(0, 'WARNING: ' . $e->getMessage());
+            }
+        } elseif (is_array($e)) {
+            if (!isset($options['soft'])) {
+                $this->log(0, $e[0]);
+            }
+        }
+
+        $this->pkginfo = &$pkg;
+        // pretty much nothing happens if we are only registering the uninstall
+        if (empty($options['register-only'])) {
+            // {{{ Delete the files
+            $this->startFileTransaction();
+            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+            if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
+                PEAR::popErrorHandling();
+                $this->rollbackFileTransaction();
+                $this->configSet('default_channel', $savechannel);
+                if (!isset($options['ignore-errors'])) {
+                    return $this->raiseError($err);
+                }
+
+                if (!isset($options['soft'])) {
+                    $this->log(0, 'WARNING: ' . $err->getMessage());
+                }
+            } else {
+                PEAR::popErrorHandling();
+            }
+
+            if (!$this->commitFileTransaction()) {
+                $this->rollbackFileTransaction();
+                if (!isset($options['ignore-errors'])) {
+                    return $this->raiseError("uninstall failed");
+                }
+
+                if (!isset($options['soft'])) {
+                    $this->log(0, 'WARNING: uninstall failed');
+                }
+            } else {
+                $this->startFileTransaction();
+                $dirtree = $pkg->getDirTree();
+                if ($dirtree === false) {
+                    $this->configSet('default_channel', $savechannel);
+                    return $this->_registry->deletePackage($package, $channel);
+                }
+
+                // attempt to delete empty directories
+                uksort($dirtree, array($this, '_sortDirs'));
+                foreach($dirtree as $dir => $notused) {
+                    $this->addFileOperation('rmdir', array($dir));
+                }
+
+                if (!$this->commitFileTransaction()) {
+                    $this->rollbackFileTransaction();
+                    if (!isset($options['ignore-errors'])) {
+                        return $this->raiseError("uninstall failed");
+                    }
+
+                    if (!isset($options['soft'])) {
+                        $this->log(0, 'WARNING: uninstall failed');
+                    }
+                }
+            }
+            // }}}
+        }
+
+        $this->configSet('default_channel', $savechannel);
+        // Register that the package is no longer installed
+        return $this->_registry->deletePackage($package, $channel);
+    }
+
+    /**
+     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
+     *
+     * It also removes duplicate dependencies
+     * @param array an array of PEAR_PackageFile_v[1/2] objects
+     * @return array|PEAR_Error array of array(packagefilename, package.xml contents)
+     */
+    function sortPackagesForUninstall(&$packages)
+    {
+        $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
+        if (PEAR::isError($this->_dependencyDB)) {
+            return $this->_dependencyDB;
+        }
+        usort($packages, array(&$this, '_sortUninstall'));
+    }
+
+    function _sortUninstall($a, $b)
+    {
+        if (!$a->getDeps() && !$b->getDeps()) {
+            return 0; // neither package has dependencies, order is insignificant
+        }
+        if ($a->getDeps() && !$b->getDeps()) {
+            return -1; // $a must be installed after $b because $a has dependencies
+        }
+        if (!$a->getDeps() && $b->getDeps()) {
+            return 1; // $b must be installed after $a because $b has dependencies
+        }
+        // both packages have dependencies
+        if ($this->_dependencyDB->dependsOn($a, $b)) {
+            return -1;
+        }
+        if ($this->_dependencyDB->dependsOn($b, $a)) {
+            return 1;
+        }
+        return 0;
+    }
+
+    // }}}
+    // {{{ _sortDirs()
+    function _sortDirs($a, $b)
+    {
+        if (strnatcmp($a, $b) == -1) return 1;
+        if (strnatcmp($a, $b) == 1) return -1;
+        return 0;
+    }
+
+    // }}}
+
+    // {{{ _buildCallback()
+
+    function _buildCallback($what, $data)
+    {
+        if (($what == 'cmdoutput' && $this->debug > 1) ||
+            ($what == 'output' && $this->debug > 0)) {
+            $this->ui->outputData(rtrim($data), 'build');
+        }
+    }
+
+    // }}}
+}
Index: trunk/library/PEAR/PEAR/Downloader.php
===================================================================
--- trunk/library/PEAR/PEAR/Downloader.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Downloader.php	(revision 5146)
@@ -0,0 +1,1766 @@
+<?php
+/**
+ * PEAR_Downloader, the PEAR Installer's download utility class
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Martin Jansen <mj@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Downloader.php 313024 2011-07-06 19:51:24Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.3.0
+ */
+
+/**
+ * Needed for constants, extending
+ */
+require_once 'PEAR/Common.php';
+
+define('PEAR_INSTALLER_OK',       1);
+define('PEAR_INSTALLER_FAILED',   0);
+define('PEAR_INSTALLER_SKIPPED', -1);
+define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
+
+/**
+ * Administration class used to download anything from the internet (PEAR Packages,
+ * static URLs, xml files)
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Martin Jansen <mj@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.3.0
+ */
+class PEAR_Downloader extends PEAR_Common
+{
+    /**
+     * @var PEAR_Registry
+     * @access private
+     */
+    var $_registry;
+
+    /**
+     * Preferred Installation State (snapshot, devel, alpha, beta, stable)
+     * @var string|null
+     * @access private
+     */
+    var $_preferredState;
+
+    /**
+     * Options from command-line passed to Install.
+     *
+     * Recognized options:<br />
+     *  - onlyreqdeps   : install all required dependencies as well
+     *  - alldeps       : install all dependencies, including optional
+     *  - installroot   : base relative path to install files in
+     *  - force         : force a download even if warnings would prevent it
+     *  - nocompress    : download uncompressed tarballs
+     * @see PEAR_Command_Install
+     * @access private
+     * @var array
+     */
+    var $_options;
+
+    /**
+     * Downloaded Packages after a call to download().
+     *
+     * Format of each entry:
+     *
+     * <code>
+     * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+     *    'info' => array() // parsed package.xml
+     * );
+     * </code>
+     * @access private
+     * @var array
+     */
+    var $_downloadedPackages = array();
+
+    /**
+     * Packages slated for download.
+     *
+     * This is used to prevent downloading a package more than once should it be a dependency
+     * for two packages to be installed.
+     * Format of each entry:
+     *
+     * <pre>
+     * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
+     * );
+     * </pre>
+     * @access private
+     * @var array
+     */
+    var $_toDownload = array();
+
+    /**
+     * Array of every package installed, with names lower-cased.
+     *
+     * Format:
+     * <code>
+     * array('package1' => 0, 'package2' => 1, );
+     * </code>
+     * @var array
+     */
+    var $_installed = array();
+
+    /**
+     * @var array
+     * @access private
+     */
+    var $_errorStack = array();
+
+    /**
+     * @var boolean
+     * @access private
+     */
+    var $_internalDownload = false;
+
+    /**
+     * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
+     * @var array
+     * @access private
+     */
+    var $_packageSortTree;
+
+    /**
+     * Temporary directory, or configuration value where downloads will occur
+     * @var string
+     */
+    var $_downloadDir;
+
+    /**
+     * @param PEAR_Frontend_*
+     * @param array
+     * @param PEAR_Config
+     */
+    function PEAR_Downloader(&$ui, $options, &$config)
+    {
+        parent::PEAR_Common();
+        $this->_options = $options;
+        $this->config = &$config;
+        $this->_preferredState = $this->config->get('preferred_state');
+        $this->ui = &$ui;
+        if (!$this->_preferredState) {
+            // don't inadvertantly use a non-set preferred_state
+            $this->_preferredState = null;
+        }
+
+        if (isset($this->_options['installroot'])) {
+            $this->config->setInstallRoot($this->_options['installroot']);
+        }
+        $this->_registry = &$config->getRegistry();
+
+        if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+            $this->_installed = $this->_registry->listAllPackages();
+            foreach ($this->_installed as $key => $unused) {
+                if (!count($unused)) {
+                    continue;
+                }
+                $strtolower = create_function('$a','return strtolower($a);');
+                array_walk($this->_installed[$key], $strtolower);
+            }
+        }
+    }
+
+    /**
+     * Attempt to discover a channel's remote capabilities from
+     * its server name
+     * @param string
+     * @return boolean
+     */
+    function discover($channel)
+    {
+        $this->log(1, 'Attempting to discover channel "' . $channel . '"...');
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+        $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
+        if (!class_exists('System')) {
+            require_once 'System.php';
+        }
+
+        $tmpdir = $this->config->get('temp_dir');
+        $tmp = System::mktemp('-d -t "' . $tmpdir . '"');
+        $a   = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
+        PEAR::popErrorHandling();
+        if (PEAR::isError($a)) {
+            // Attempt to fallback to https automatically.
+            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+            $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...');
+            $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
+            PEAR::popErrorHandling();
+            if (PEAR::isError($a)) {
+                return false;
+            }
+        }
+
+        list($a, $lastmodified) = $a;
+        if (!class_exists('PEAR_ChannelFile')) {
+            require_once 'PEAR/ChannelFile.php';
+        }
+
+        $b = new PEAR_ChannelFile;
+        if ($b->fromXmlFile($a)) {
+            unlink($a);
+            if ($this->config->get('auto_discover')) {
+                $this->_registry->addChannel($b, $lastmodified);
+                $alias = $b->getName();
+                if ($b->getName() == $this->_registry->channelName($b->getAlias())) {
+                    $alias = $b->getAlias();
+                }
+
+                $this->log(1, 'Auto-discovered channel "' . $channel .
+                    '", alias "' . $alias . '", adding to registry');
+            }
+
+            return true;
+        }
+
+        unlink($a);
+        return false;
+    }
+
+    /**
+     * For simpler unit-testing
+     * @param PEAR_Downloader
+     * @return PEAR_Downloader_Package
+     */
+    function &newDownloaderPackage(&$t)
+    {
+        if (!class_exists('PEAR_Downloader_Package')) {
+            require_once 'PEAR/Downloader/Package.php';
+        }
+        $a = &new PEAR_Downloader_Package($t);
+        return $a;
+    }
+
+    /**
+     * For simpler unit-testing
+     * @param PEAR_Config
+     * @param array
+     * @param array
+     * @param int
+     */
+    function &getDependency2Object(&$c, $i, $p, $s)
+    {
+        if (!class_exists('PEAR_Dependency2')) {
+            require_once 'PEAR/Dependency2.php';
+        }
+        $z = &new PEAR_Dependency2($c, $i, $p, $s);
+        return $z;
+    }
+
+    function &download($params)
+    {
+        if (!count($params)) {
+            $a = array();
+            return $a;
+        }
+
+        if (!isset($this->_registry)) {
+            $this->_registry = &$this->config->getRegistry();
+        }
+
+        $channelschecked = array();
+        // convert all parameters into PEAR_Downloader_Package objects
+        foreach ($params as $i => $param) {
+            $params[$i] = &$this->newDownloaderPackage($this);
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $err = $params[$i]->initialize($param);
+            PEAR::staticPopErrorHandling();
+            if (!$err) {
+                // skip parameters that were missed by preferred_state
+                continue;
+            }
+
+            if (PEAR::isError($err)) {
+                if (!isset($this->_options['soft']) && $err->getMessage() !== '') {
+                    $this->log(0, $err->getMessage());
+                }
+
+                $params[$i] = false;
+                if (is_object($param)) {
+                    $param = $param->getChannel() . '/' . $param->getPackage();
+                }
+
+                if (!isset($this->_options['soft'])) {
+                    $this->log(2, 'Package "' . $param . '" is not valid');
+                }
+
+                // Message logged above in a specific verbose mode, passing null to not show up on CLI
+                $this->pushError(null, PEAR_INSTALLER_SKIPPED);
+            } else {
+                do {
+                    if ($params[$i] && $params[$i]->getType() == 'local') {
+                        // bug #7090 skip channel.xml check for local packages
+                        break;
+                    }
+
+                    if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) &&
+                          !isset($this->_options['offline'])
+                    ) {
+                        $channelschecked[$params[$i]->getChannel()] = true;
+                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                        if (!class_exists('System')) {
+                            require_once 'System.php';
+                        }
+
+                        $curchannel = &$this->_registry->getChannel($params[$i]->getChannel());
+                        if (PEAR::isError($curchannel)) {
+                            PEAR::staticPopErrorHandling();
+                            return $this->raiseError($curchannel);
+                        }
+
+                        if (PEAR::isError($dir = $this->getDownloadDir())) {
+                            PEAR::staticPopErrorHandling();
+                            break;
+                        }
+
+                        $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel());
+                        $url    = 'http://' . $mirror . '/channel.xml';
+                        $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified());
+
+                        PEAR::staticPopErrorHandling();
+                        if (PEAR::isError($a) || !$a) {
+                            // Attempt fallback to https automatically
+                            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                            $a = $this->downloadHttp('https://' . $mirror .
+                                '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified());
+
+                            PEAR::staticPopErrorHandling();
+                            if (PEAR::isError($a) || !$a) {
+                                break;
+                            }
+                        }
+                        $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' .
+                            'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() .
+                            '" to update');
+                    }
+                } while (false);
+
+                if ($params[$i] && !isset($this->_options['downloadonly'])) {
+                    if (isset($this->_options['packagingroot'])) {
+                        $checkdir = $this->_prependPath(
+                            $this->config->get('php_dir', null, $params[$i]->getChannel()),
+                            $this->_options['packagingroot']);
+                    } else {
+                        $checkdir = $this->config->get('php_dir',
+                            null, $params[$i]->getChannel());
+                    }
+
+                    while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
+                        $checkdir = dirname($checkdir);
+                    }
+
+                    if ($checkdir == '.') {
+                        $checkdir = '/';
+                    }
+
+                    if (!is_writeable($checkdir)) {
+                        return PEAR::raiseError('Cannot install, php_dir for channel "' .
+                            $params[$i]->getChannel() . '" is not writeable by the current user');
+                    }
+                }
+            }
+        }
+
+        unset($channelschecked);
+        PEAR_Downloader_Package::removeDuplicates($params);
+        if (!count($params)) {
+            $a = array();
+            return $a;
+        }
+
+        if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) {
+            $reverify = true;
+            while ($reverify) {
+                $reverify = false;
+                foreach ($params as $i => $param) {
+                    //PHP Bug 40768 / PEAR Bug #10944
+                    //Nested foreaches fail in PHP 5.2.1
+                    key($params);
+                    $ret = $params[$i]->detectDependencies($params);
+                    if (PEAR::isError($ret)) {
+                        $reverify = true;
+                        $params[$i] = false;
+                        PEAR_Downloader_Package::removeDuplicates($params);
+                        if (!isset($this->_options['soft'])) {
+                            $this->log(0, $ret->getMessage());
+                        }
+                        continue 2;
+                    }
+                }
+            }
+        }
+
+        if (isset($this->_options['offline'])) {
+            $this->log(3, 'Skipping dependency download check, --offline specified');
+        }
+
+        if (!count($params)) {
+            $a = array();
+            return $a;
+        }
+
+        while (PEAR_Downloader_Package::mergeDependencies($params));
+        PEAR_Downloader_Package::removeDuplicates($params, true);
+        $errorparams = array();
+        if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) {
+            if (count($errorparams)) {
+                foreach ($errorparams as $param) {
+                    $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage());
+                    $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED);
+                }
+                $a = array();
+                return $a;
+            }
+        }
+
+        PEAR_Downloader_Package::removeInstalled($params);
+        if (!count($params)) {
+            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
+            $a = array();
+            return $a;
+        }
+
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+        $err = $this->analyzeDependencies($params);
+        PEAR::popErrorHandling();
+        if (!count($params)) {
+            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
+            $a = array();
+            return $a;
+        }
+
+        $ret = array();
+        $newparams = array();
+        if (isset($this->_options['pretend'])) {
+            return $params;
+        }
+
+        $somefailed = false;
+        foreach ($params as $i => $package) {
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $pf = &$params[$i]->download();
+            PEAR::staticPopErrorHandling();
+            if (PEAR::isError($pf)) {
+                if (!isset($this->_options['soft'])) {
+                    $this->log(1, $pf->getMessage());
+                    $this->log(0, 'Error: cannot download "' .
+                        $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
+                            true) .
+                        '"');
+                }
+                $somefailed = true;
+                continue;
+            }
+
+            $newparams[] = &$params[$i];
+            $ret[] = array(
+                'file' => $pf->getArchiveFile(),
+                'info' => &$pf,
+                'pkg'  => $pf->getPackage()
+            );
+        }
+
+        if ($somefailed) {
+            // remove params that did not download successfully
+            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+            $err = $this->analyzeDependencies($newparams, true);
+            PEAR::popErrorHandling();
+            if (!count($newparams)) {
+                $this->pushError('Download failed', PEAR_INSTALLER_FAILED);
+                $a = array();
+                return $a;
+            }
+        }
+
+        $this->_downloadedPackages = $ret;
+        return $newparams;
+    }
+
+    /**
+     * @param array all packages to be installed
+     */
+    function analyzeDependencies(&$params, $force = false)
+    {
+        if (isset($this->_options['downloadonly'])) {
+            return;
+        }
+
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $redo  = true;
+        $reset = $hasfailed = $failed = false;
+        while ($redo) {
+            $redo = false;
+            foreach ($params as $i => $param) {
+                $deps = $param->getDeps();
+                if (!$deps) {
+                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
+                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
+                    $send = $param->getPackageFile();
+
+                    $installcheck = $depchecker->validatePackage($send, $this, $params);
+                    if (PEAR::isError($installcheck)) {
+                        if (!isset($this->_options['soft'])) {
+                            $this->log(0, $installcheck->getMessage());
+                        }
+                        $hasfailed  = true;
+                        $params[$i] = false;
+                        $reset      = true;
+                        $redo       = true;
+                        $failed     = false;
+                        PEAR_Downloader_Package::removeDuplicates($params);
+                        continue 2;
+                    }
+                    continue;
+                }
+
+                if (!$reset && $param->alreadyValidated() && !$force) {
+                    continue;
+                }
+
+                if (count($deps)) {
+                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
+                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
+                    $send = $param->getPackageFile();
+                    if ($send === null) {
+                        $send = $param->getDownloadURL();
+                    }
+
+                    $installcheck = $depchecker->validatePackage($send, $this, $params);
+                    if (PEAR::isError($installcheck)) {
+                        if (!isset($this->_options['soft'])) {
+                            $this->log(0, $installcheck->getMessage());
+                        }
+                        $hasfailed  = true;
+                        $params[$i] = false;
+                        $reset      = true;
+                        $redo       = true;
+                        $failed     = false;
+                        PEAR_Downloader_Package::removeDuplicates($params);
+                        continue 2;
+                    }
+
+                    $failed = false;
+                    if (isset($deps['required']) && is_array($deps['required'])) {
+                        foreach ($deps['required'] as $type => $dep) {
+                            // note: Dependency2 will never return a PEAR_Error if ignore-errors
+                            // is specified, so soft is needed to turn off logging
+                            if (!isset($dep[0])) {
+                                if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep,
+                                      true, $params))) {
+                                    $failed = true;
+                                    if (!isset($this->_options['soft'])) {
+                                        $this->log(0, $e->getMessage());
+                                    }
+                                } elseif (is_array($e) && !$param->alreadyValidated()) {
+                                    if (!isset($this->_options['soft'])) {
+                                        $this->log(0, $e[0]);
+                                    }
+                                }
+                            } else {
+                                foreach ($dep as $d) {
+                                    if (PEAR::isError($e =
+                                          $depchecker->{"validate{$type}Dependency"}($d,
+                                          true, $params))) {
+                                        $failed = true;
+                                        if (!isset($this->_options['soft'])) {
+                                            $this->log(0, $e->getMessage());
+                                        }
+                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
+                                        if (!isset($this->_options['soft'])) {
+                                            $this->log(0, $e[0]);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        if (isset($deps['optional']) && is_array($deps['optional'])) {
+                            foreach ($deps['optional'] as $type => $dep) {
+                                if (!isset($dep[0])) {
+                                    if (PEAR::isError($e =
+                                          $depchecker->{"validate{$type}Dependency"}($dep,
+                                          false, $params))) {
+                                        $failed = true;
+                                        if (!isset($this->_options['soft'])) {
+                                            $this->log(0, $e->getMessage());
+                                        }
+                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
+                                        if (!isset($this->_options['soft'])) {
+                                            $this->log(0, $e[0]);
+                                        }
+                                    }
+                                } else {
+                                    foreach ($dep as $d) {
+                                        if (PEAR::isError($e =
+                                              $depchecker->{"validate{$type}Dependency"}($d,
+                                              false, $params))) {
+                                            $failed = true;
+                                            if (!isset($this->_options['soft'])) {
+                                                $this->log(0, $e->getMessage());
+                                            }
+                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
+                                            if (!isset($this->_options['soft'])) {
+                                                $this->log(0, $e[0]);
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        $groupname = $param->getGroup();
+                        if (isset($deps['group']) && $groupname) {
+                            if (!isset($deps['group'][0])) {
+                                $deps['group'] = array($deps['group']);
+                            }
+
+                            $found = false;
+                            foreach ($deps['group'] as $group) {
+                                if ($group['attribs']['name'] == $groupname) {
+                                    $found = true;
+                                    break;
+                                }
+                            }
+
+                            if ($found) {
+                                unset($group['attribs']);
+                                foreach ($group as $type => $dep) {
+                                    if (!isset($dep[0])) {
+                                        if (PEAR::isError($e =
+                                              $depchecker->{"validate{$type}Dependency"}($dep,
+                                              false, $params))) {
+                                            $failed = true;
+                                            if (!isset($this->_options['soft'])) {
+                                                $this->log(0, $e->getMessage());
+                                            }
+                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
+                                            if (!isset($this->_options['soft'])) {
+                                                $this->log(0, $e[0]);
+                                            }
+                                        }
+                                    } else {
+                                        foreach ($dep as $d) {
+                                            if (PEAR::isError($e =
+                                                  $depchecker->{"validate{$type}Dependency"}($d,
+                                                  false, $params))) {
+                                                $failed = true;
+                                                if (!isset($this->_options['soft'])) {
+                                                    $this->log(0, $e->getMessage());
+                                                }
+                                            } elseif (is_array($e) && !$param->alreadyValidated()) {
+                                                if (!isset($this->_options['soft'])) {
+                                                    $this->log(0, $e[0]);
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    } else {
+                        foreach ($deps as $dep) {
+                            if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) {
+                                $failed = true;
+                                if (!isset($this->_options['soft'])) {
+                                    $this->log(0, $e->getMessage());
+                                }
+                            } elseif (is_array($e) && !$param->alreadyValidated()) {
+                                if (!isset($this->_options['soft'])) {
+                                    $this->log(0, $e[0]);
+                                }
+                            }
+                        }
+                    }
+                    $params[$i]->setValidated();
+                }
+
+                if ($failed) {
+                    $hasfailed  = true;
+                    $params[$i] = false;
+                    $reset      = true;
+                    $redo       = true;
+                    $failed     = false;
+                    PEAR_Downloader_Package::removeDuplicates($params);
+                    continue 2;
+                }
+            }
+        }
+
+        PEAR::staticPopErrorHandling();
+        if ($hasfailed && (isset($this->_options['ignore-errors']) ||
+              isset($this->_options['nodeps']))) {
+            // this is probably not needed, but just in case
+            if (!isset($this->_options['soft'])) {
+                $this->log(0, 'WARNING: dependencies failed');
+            }
+        }
+    }
+
+    /**
+     * Retrieve the directory that downloads will happen in
+     * @access private
+     * @return string
+     */
+    function getDownloadDir()
+    {
+        if (isset($this->_downloadDir)) {
+            return $this->_downloadDir;
+        }
+
+        $downloaddir = $this->config->get('download_dir');
+        if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) {
+            if  (is_dir($downloaddir) && !is_writable($downloaddir)) {
+                $this->log(0, 'WARNING: configuration download directory "' . $downloaddir .
+                    '" is not writeable.  Change download_dir config variable to ' .
+                    'a writeable dir to avoid this warning');
+            }
+
+            if (!class_exists('System')) {
+                require_once 'System.php';
+            }
+
+            if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+                return $downloaddir;
+            }
+            $this->log(3, '+ tmp dir created at ' . $downloaddir);
+        }
+
+        if (!is_writable($downloaddir)) {
+            if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) ||
+                  !is_writable($downloaddir)) {
+                return PEAR::raiseError('download directory "' . $downloaddir .
+                    '" is not writeable.  Change download_dir config variable to ' .
+                    'a writeable dir');
+            }
+        }
+
+        return $this->_downloadDir = $downloaddir;
+    }
+
+    function setDownloadDir($dir)
+    {
+        if (!@is_writable($dir)) {
+            if (PEAR::isError(System::mkdir(array('-p', $dir)))) {
+                return PEAR::raiseError('download directory "' . $dir .
+                    '" is not writeable.  Change download_dir config variable to ' .
+                    'a writeable dir');
+            }
+        }
+        $this->_downloadDir = $dir;
+    }
+
+    function configSet($key, $value, $layer = 'user', $channel = false)
+    {
+        $this->config->set($key, $value, $layer, $channel);
+        $this->_preferredState = $this->config->get('preferred_state', null, $channel);
+        if (!$this->_preferredState) {
+            // don't inadvertantly use a non-set preferred_state
+            $this->_preferredState = null;
+        }
+    }
+
+    function setOptions($options)
+    {
+        $this->_options = $options;
+    }
+
+    function getOptions()
+    {
+        return $this->_options;
+    }
+
+
+    /**
+     * @param array output of {@link parsePackageName()}
+     * @access private
+     */
+    function _getPackageDownloadUrl($parr)
+    {
+        $curchannel = $this->config->get('default_channel');
+        $this->configSet('default_channel', $parr['channel']);
+        // getDownloadURL returns an array.  On error, it only contains information
+        // on the latest release as array(version, info).  On success it contains
+        // array(version, info, download url string)
+        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
+        if (!$this->_registry->channelExists($parr['channel'])) {
+            do {
+                if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) {
+                    break;
+                }
+
+                $this->configSet('default_channel', $curchannel);
+                return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']);
+            } while (false);
+        }
+
+        $chan = &$this->_registry->getChannel($parr['channel']);
+        if (PEAR::isError($chan)) {
+            return $chan;
+        }
+
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $version   = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']);
+        $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']);
+        // package is installed - use the installed release stability level
+        if (!isset($parr['state']) && $stability !== null) {
+            $state = $stability['release'];
+        }
+        PEAR::staticPopErrorHandling();
+        $base2 = false;
+
+        $preferred_mirror = $this->config->get('preferred_mirror');
+        if (!$chan->supportsREST($preferred_mirror) ||
+              (
+               !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror))
+               &&
+               !($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
+              )
+        ) {
+            return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
+        }
+
+        if ($base2) {
+            $rest = &$this->config->getREST('1.3', $this->_options);
+            $base = $base2;
+        } else {
+            $rest = &$this->config->getREST('1.0', $this->_options);
+        }
+
+        $downloadVersion = false;
+        if (!isset($parr['version']) && !isset($parr['state']) && $version
+              && !PEAR::isError($version)
+              && !isset($this->_options['downloadonly'])
+        ) {
+            $downloadVersion = $version;
+        }
+
+        $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName());
+        if (PEAR::isError($url)) {
+            $this->configSet('default_channel', $curchannel);
+            return $url;
+        }
+
+        if ($parr['channel'] != $curchannel) {
+            $this->configSet('default_channel', $curchannel);
+        }
+
+        if (!is_array($url)) {
+            return $url;
+        }
+
+        $url['raw'] = false; // no checking is necessary for REST
+        if (!is_array($url['info'])) {
+            return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
+                'this should never happen');
+        }
+
+        if (!isset($this->_options['force']) &&
+              !isset($this->_options['downloadonly']) &&
+              $version &&
+              !PEAR::isError($version) &&
+              !isset($parr['group'])
+        ) {
+            if (version_compare($version, $url['version'], '=')) {
+                return PEAR::raiseError($this->_registry->parsedPackageNameToString(
+                    $parr, true) . ' is already installed and is the same as the ' .
+                    'released version ' . $url['version'], -976);
+            }
+
+            if (version_compare($version, $url['version'], '>')) {
+                return PEAR::raiseError($this->_registry->parsedPackageNameToString(
+                    $parr, true) . ' is already installed and is newer than detected ' .
+                    'released version ' . $url['version'], -976);
+            }
+        }
+
+        if (isset($url['info']['required']) || $url['compatible']) {
+            require_once 'PEAR/PackageFile/v2.php';
+            $pf = new PEAR_PackageFile_v2;
+            $pf->setRawChannel($parr['channel']);
+            if ($url['compatible']) {
+                $pf->setRawCompatible($url['compatible']);
+            }
+        } else {
+            require_once 'PEAR/PackageFile/v1.php';
+            $pf = new PEAR_PackageFile_v1;
+        }
+
+        $pf->setRawPackage($url['package']);
+        $pf->setDeps($url['info']);
+        if ($url['compatible']) {
+            $pf->setCompatible($url['compatible']);
+        }
+
+        $pf->setRawState($url['stability']);
+        $url['info'] = &$pf;
+        if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
+            $ext = '.tar';
+        } else {
+            $ext = '.tgz';
+        }
+
+        if (is_array($url) && isset($url['url'])) {
+            $url['url'] .= $ext;
+        }
+
+        return $url;
+    }
+
+    /**
+     * @param array dependency array
+     * @access private
+     */
+    function _getDepPackageDownloadUrl($dep, $parr)
+    {
+        $xsdversion = isset($dep['rel']) ? '1.0' : '2.0';
+        $curchannel = $this->config->get('default_channel');
+        if (isset($dep['uri'])) {
+            $xsdversion = '2.0';
+            $chan = &$this->_registry->getChannel('__uri');
+            if (PEAR::isError($chan)) {
+                return $chan;
+            }
+
+            $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri');
+            $this->configSet('default_channel', '__uri');
+        } else {
+            if (isset($dep['channel'])) {
+                $remotechannel = $dep['channel'];
+            } else {
+                $remotechannel = 'pear.php.net';
+            }
+
+            if (!$this->_registry->channelExists($remotechannel)) {
+                do {
+                    if ($this->config->get('auto_discover')) {
+                        if ($this->discover($remotechannel)) {
+                            break;
+                        }
+                    }
+                    return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
+                } while (false);
+            }
+
+            $chan = &$this->_registry->getChannel($remotechannel);
+            if (PEAR::isError($chan)) {
+                return $chan;
+            }
+
+            $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel);
+            $this->configSet('default_channel', $remotechannel);
+        }
+
+        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
+        if (isset($parr['state']) && isset($parr['version'])) {
+            unset($parr['state']);
+        }
+
+        if (isset($dep['uri'])) {
+            $info = &$this->newDownloaderPackage($this);
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $err = $info->initialize($dep);
+            PEAR::staticPopErrorHandling();
+            if (!$err) {
+                // skip parameters that were missed by preferred_state
+                return PEAR::raiseError('Cannot initialize dependency');
+            }
+
+            if (PEAR::isError($err)) {
+                if (!isset($this->_options['soft'])) {
+                    $this->log(0, $err->getMessage());
+                }
+
+                if (is_object($info)) {
+                    $param = $info->getChannel() . '/' . $info->getPackage();
+                }
+                return PEAR::raiseError('Package "' . $param . '" is not valid');
+            }
+            return $info;
+        } elseif ($chan->supportsREST($this->config->get('preferred_mirror'))
+              &&
+                (
+                  ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror')))
+                    ||
+                  ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror')))
+                )
+        ) {
+            if ($base2) {
+                $base = $base2;
+                $rest = &$this->config->getREST('1.3', $this->_options);
+            } else {
+                $rest = &$this->config->getREST('1.0', $this->_options);
+            }
+
+            $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr,
+                    $state, $version, $chan->getName());
+            if (PEAR::isError($url)) {
+                return $url;
+            }
+
+            if ($parr['channel'] != $curchannel) {
+                $this->configSet('default_channel', $curchannel);
+            }
+
+            if (!is_array($url)) {
+                return $url;
+            }
+
+            $url['raw'] = false; // no checking is necessary for REST
+            if (!is_array($url['info'])) {
+                return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
+                    'this should never happen');
+            }
+
+            if (isset($url['info']['required'])) {
+                if (!class_exists('PEAR_PackageFile_v2')) {
+                    require_once 'PEAR/PackageFile/v2.php';
+                }
+                $pf = new PEAR_PackageFile_v2;
+                $pf->setRawChannel($remotechannel);
+            } else {
+                if (!class_exists('PEAR_PackageFile_v1')) {
+                    require_once 'PEAR/PackageFile/v1.php';
+                }
+                $pf = new PEAR_PackageFile_v1;
+
+            }
+            $pf->setRawPackage($url['package']);
+            $pf->setDeps($url['info']);
+            if ($url['compatible']) {
+                $pf->setCompatible($url['compatible']);
+            }
+
+            $pf->setRawState($url['stability']);
+            $url['info'] = &$pf;
+            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
+                $ext = '.tar';
+            } else {
+                $ext = '.tgz';
+            }
+
+            if (is_array($url) && isset($url['url'])) {
+                $url['url'] .= $ext;
+            }
+
+            return $url;
+        }
+
+        return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
+    }
+
+    /**
+     * @deprecated in favor of _getPackageDownloadUrl
+     */
+    function getPackageDownloadUrl($package, $version = null, $channel = false)
+    {
+        if ($version) {
+            $package .= "-$version";
+        }
+        if ($this === null || $this->_registry === null) {
+            $package = "http://pear.php.net/get/$package";
+        } else {
+            $chan = $this->_registry->getChannel($channel);
+            if (PEAR::isError($chan)) {
+                return '';
+            }
+            $package = "http://" . $chan->getServer() . "/get/$package";
+        }
+        if (!extension_loaded("zlib")) {
+            $package .= '?uncompress=yes';
+        }
+        return $package;
+    }
+
+    /**
+     * Retrieve a list of downloaded packages after a call to {@link download()}.
+     *
+     * Also resets the list of downloaded packages.
+     * @return array
+     */
+    function getDownloadedPackages()
+    {
+        $ret = $this->_downloadedPackages;
+        $this->_downloadedPackages = array();
+        $this->_toDownload = array();
+        return $ret;
+    }
+
+    function _downloadCallback($msg, $params = null)
+    {
+        switch ($msg) {
+            case 'saveas':
+                $this->log(1, "downloading $params ...");
+                break;
+            case 'done':
+                $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
+                break;
+            case 'bytesread':
+                static $bytes;
+                if (empty($bytes)) {
+                    $bytes = 0;
+                }
+                if (!($bytes % 10240)) {
+                    $this->log(1, '.', false);
+                }
+                $bytes += $params;
+                break;
+            case 'start':
+                if($params[1] == -1) {
+                    $length = "Unknown size";
+                } else {
+                    $length = number_format($params[1], 0, '', ',')." bytes";
+                }
+                $this->log(1, "Starting to download {$params[0]} ($length)");
+                break;
+        }
+        if (method_exists($this->ui, '_downloadCallback'))
+            $this->ui->_downloadCallback($msg, $params);
+    }
+
+    function _prependPath($path, $prepend)
+    {
+        if (strlen($prepend) > 0) {
+            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
+                if (preg_match('/^[a-z]:/i', $prepend)) {
+                    $prepend = substr($prepend, 2);
+                } elseif ($prepend{0} != '\\') {
+                    $prepend = "\\$prepend";
+                }
+                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
+            } else {
+                $path = $prepend . $path;
+            }
+        }
+        return $path;
+    }
+
+    /**
+     * @param string
+     * @param integer
+     */
+    function pushError($errmsg, $code = -1)
+    {
+        array_push($this->_errorStack, array($errmsg, $code));
+    }
+
+    function getErrorMsgs()
+    {
+        $msgs = array();
+        $errs = $this->_errorStack;
+        foreach ($errs as $err) {
+            $msgs[] = $err[0];
+        }
+        $this->_errorStack = array();
+        return $msgs;
+    }
+
+    /**
+     * for BC
+     *
+     * @deprecated
+     */
+    function sortPkgDeps(&$packages, $uninstall = false)
+    {
+        $uninstall ?
+            $this->sortPackagesForUninstall($packages) :
+            $this->sortPackagesForInstall($packages);
+    }
+
+    /**
+     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
+     *
+     * This uses the topological sort method from graph theory, and the
+     * Structures_Graph package to properly sort dependencies for installation.
+     * @param array an array of downloaded PEAR_Downloader_Packages
+     * @return array array of array(packagefilename, package.xml contents)
+     */
+    function sortPackagesForInstall(&$packages)
+    {
+        require_once 'Structures/Graph.php';
+        require_once 'Structures/Graph/Node.php';
+        require_once 'Structures/Graph/Manipulator/TopologicalSorter.php';
+        $depgraph = new Structures_Graph(true);
+        $nodes = array();
+        $reg = &$this->config->getRegistry();
+        foreach ($packages as $i => $package) {
+            $pname = $reg->parsedPackageNameToString(
+                array(
+                    'channel' => $package->getChannel(),
+                    'package' => strtolower($package->getPackage()),
+                ));
+            $nodes[$pname] = new Structures_Graph_Node;
+            $nodes[$pname]->setData($packages[$i]);
+            $depgraph->addNode($nodes[$pname]);
+        }
+
+        $deplinks = array();
+        foreach ($nodes as $package => $node) {
+            $pf = &$node->getData();
+            $pdeps = $pf->getDeps(true);
+            if (!$pdeps) {
+                continue;
+            }
+
+            if ($pf->getPackagexmlVersion() == '1.0') {
+                foreach ($pdeps as $dep) {
+                    if ($dep['type'] != 'pkg' ||
+                          (isset($dep['optional']) && $dep['optional'] == 'yes')) {
+                        continue;
+                    }
+
+                    $dname = $reg->parsedPackageNameToString(
+                          array(
+                              'channel' => 'pear.php.net',
+                              'package' => strtolower($dep['name']),
+                          ));
+
+                    if (isset($nodes[$dname])) {
+                        if (!isset($deplinks[$dname])) {
+                            $deplinks[$dname] = array();
+                        }
+
+                        $deplinks[$dname][$package] = 1;
+                        // dependency is in installed packages
+                        continue;
+                    }
+
+                    $dname = $reg->parsedPackageNameToString(
+                          array(
+                              'channel' => 'pecl.php.net',
+                              'package' => strtolower($dep['name']),
+                          ));
+
+                    if (isset($nodes[$dname])) {
+                        if (!isset($deplinks[$dname])) {
+                            $deplinks[$dname] = array();
+                        }
+
+                        $deplinks[$dname][$package] = 1;
+                        // dependency is in installed packages
+                        continue;
+                    }
+                }
+            } else {
+                // the only ordering we care about is:
+                // 1) subpackages must be installed before packages that depend on them
+                // 2) required deps must be installed before packages that depend on them
+                if (isset($pdeps['required']['subpackage'])) {
+                    $t = $pdeps['required']['subpackage'];
+                    if (!isset($t[0])) {
+                        $t = array($t);
+                    }
+
+                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
+                }
+
+                if (isset($pdeps['group'])) {
+                    if (!isset($pdeps['group'][0])) {
+                        $pdeps['group'] = array($pdeps['group']);
+                    }
+
+                    foreach ($pdeps['group'] as $group) {
+                        if (isset($group['subpackage'])) {
+                            $t = $group['subpackage'];
+                            if (!isset($t[0])) {
+                                $t = array($t);
+                            }
+
+                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
+                        }
+                    }
+                }
+
+                if (isset($pdeps['optional']['subpackage'])) {
+                    $t = $pdeps['optional']['subpackage'];
+                    if (!isset($t[0])) {
+                        $t = array($t);
+                    }
+
+                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
+                }
+
+                if (isset($pdeps['required']['package'])) {
+                    $t = $pdeps['required']['package'];
+                    if (!isset($t[0])) {
+                        $t = array($t);
+                    }
+
+                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
+                }
+
+                if (isset($pdeps['group'])) {
+                    if (!isset($pdeps['group'][0])) {
+                        $pdeps['group'] = array($pdeps['group']);
+                    }
+
+                    foreach ($pdeps['group'] as $group) {
+                        if (isset($group['package'])) {
+                            $t = $group['package'];
+                            if (!isset($t[0])) {
+                                $t = array($t);
+                            }
+
+                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
+                        }
+                    }
+                }
+            }
+        }
+
+        $this->_detectDepCycle($deplinks);
+        foreach ($deplinks as $dependent => $parents) {
+            foreach ($parents as $parent => $unused) {
+                $nodes[$dependent]->connectTo($nodes[$parent]);
+            }
+        }
+
+        $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph);
+        $ret = array();
+        for ($i = 0, $count = count($installOrder); $i < $count; $i++) {
+            foreach ($installOrder[$i] as $index => $sortedpackage) {
+                $data = &$installOrder[$i][$index]->getData();
+                $ret[] = &$nodes[$reg->parsedPackageNameToString(
+                          array(
+                              'channel' => $data->getChannel(),
+                              'package' => strtolower($data->getPackage()),
+                          ))]->getData();
+            }
+        }
+
+        $packages = $ret;
+        return;
+    }
+
+    /**
+     * Detect recursive links between dependencies and break the cycles
+     *
+     * @param array
+     * @access private
+     */
+    function _detectDepCycle(&$deplinks)
+    {
+        do {
+            $keepgoing = false;
+            foreach ($deplinks as $dep => $parents) {
+                foreach ($parents as $parent => $unused) {
+                    // reset the parent cycle detector
+                    $this->_testCycle(null, null, null);
+                    if ($this->_testCycle($dep, $deplinks, $parent)) {
+                        $keepgoing = true;
+                        unset($deplinks[$dep][$parent]);
+                        if (count($deplinks[$dep]) == 0) {
+                            unset($deplinks[$dep]);
+                        }
+
+                        continue 3;
+                    }
+                }
+            }
+        } while ($keepgoing);
+    }
+
+    function _testCycle($test, $deplinks, $dep)
+    {
+        static $visited = array();
+        if ($test === null) {
+            $visited = array();
+            return;
+        }
+
+        // this happens when a parent has a dep cycle on another dependency
+        // but the child is not part of the cycle
+        if (isset($visited[$dep])) {
+            return false;
+        }
+
+        $visited[$dep] = 1;
+        if ($test == $dep) {
+            return true;
+        }
+
+        if (isset($deplinks[$dep])) {
+            if (in_array($test, array_keys($deplinks[$dep]), true)) {
+                return true;
+            }
+
+            foreach ($deplinks[$dep] as $parent => $unused) {
+                if ($this->_testCycle($test, $deplinks, $parent)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Set up the dependency for installation parsing
+     *
+     * @param array $t dependency information
+     * @param PEAR_Registry $reg
+     * @param array $deplinks list of dependency links already established
+     * @param array $nodes all existing package nodes
+     * @param string $package parent package name
+     * @access private
+     */
+    function _setupGraph($t, $reg, &$deplinks, &$nodes, $package)
+    {
+        foreach ($t as $dep) {
+            $depchannel = !isset($dep['channel']) ? '__uri': $dep['channel'];
+            $dname = $reg->parsedPackageNameToString(
+                  array(
+                      'channel' => $depchannel,
+                      'package' => strtolower($dep['name']),
+                  ));
+
+            if (isset($nodes[$dname])) {
+                if (!isset($deplinks[$dname])) {
+                    $deplinks[$dname] = array();
+                }
+                $deplinks[$dname][$package] = 1;
+            }
+        }
+    }
+
+    function _dependsOn($a, $b)
+    {
+        return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), $b);
+    }
+
+    function _checkDepTree($channel, $package, $b, $checked = array())
+    {
+        $checked[$channel][$package] = true;
+        if (!isset($this->_depTree[$channel][$package])) {
+            return false;
+        }
+
+        if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())]
+              [strtolower($b->getPackage())])) {
+            return true;
+        }
+
+        foreach ($this->_depTree[$channel][$package] as $ch => $packages) {
+            foreach ($packages as $pa => $true) {
+                if ($this->_checkDepTree($ch, $pa, $b, $checked)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    function _sortInstall($a, $b)
+    {
+        if (!$a->getDeps() && !$b->getDeps()) {
+            return 0; // neither package has dependencies, order is insignificant
+        }
+        if ($a->getDeps() && !$b->getDeps()) {
+            return 1; // $a must be installed after $b because $a has dependencies
+        }
+        if (!$a->getDeps() && $b->getDeps()) {
+            return -1; // $b must be installed after $a because $b has dependencies
+        }
+        // both packages have dependencies
+        if ($this->_dependsOn($a, $b)) {
+            return 1;
+        }
+        if ($this->_dependsOn($b, $a)) {
+            return -1;
+        }
+        return 0;
+    }
+
+    /**
+     * Download a file through HTTP.  Considers suggested file name in
+     * Content-disposition: header and can run a callback function for
+     * different events.  The callback will be called with two
+     * parameters: the callback type, and parameters.  The implemented
+     * callback types are:
+     *
+     *  'setup'       called at the very beginning, parameter is a UI object
+     *                that should be used for all output
+     *  'message'     the parameter is a string with an informational message
+     *  'saveas'      may be used to save with a different file name, the
+     *                parameter is the filename that is about to be used.
+     *                If a 'saveas' callback returns a non-empty string,
+     *                that file name will be used as the filename instead.
+     *                Note that $save_dir will not be affected by this, only
+     *                the basename of the file.
+     *  'start'       download is starting, parameter is number of bytes
+     *                that are expected, or -1 if unknown
+     *  'bytesread'   parameter is the number of bytes read so far
+     *  'done'        download is complete, parameter is the total number
+     *                of bytes read
+     *  'connfailed'  if the TCP/SSL connection fails, this callback is called
+     *                with array(host,port,errno,errmsg)
+     *  'writefailed' if writing to disk fails, this callback is called
+     *                with array(destfile,errmsg)
+     *
+     * If an HTTP proxy has been configured (http_proxy PEAR_Config
+     * setting), the proxy will be used.
+     *
+     * @param string  $url       the URL to download
+     * @param object  $ui        PEAR_Frontend_* instance
+     * @param object  $config    PEAR_Config instance
+     * @param string  $save_dir  directory to save file in
+     * @param mixed   $callback  function/method to call for status
+     *                           updates
+     * @param false|string|array $lastmodified header values to check against for caching
+     *                           use false to return the header values from this download
+     * @param false|array $accept Accept headers to send
+     * @param false|string $channel Channel to use for retrieving authentication
+     * @return string|array  Returns the full path of the downloaded file or a PEAR
+     *                       error on failure.  If the error is caused by
+     *                       socket-related errors, the error object will
+     *                       have the fsockopen error code available through
+     *                       getCode().  If caching is requested, then return the header
+     *                       values.
+     *
+     * @access public
+     */
+    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null,
+                          $accept = false, $channel = false)
+    {
+        static $redirect = 0;
+        // always reset , so we are clean case of error
+        $wasredirect = $redirect;
+        $redirect = 0;
+        if ($callback) {
+            call_user_func($callback, 'setup', array(&$ui));
+        }
+
+        $info = parse_url($url);
+        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
+            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
+        }
+
+        if (!isset($info['host'])) {
+            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
+        }
+
+        $host = isset($info['host']) ? $info['host'] : null;
+        $port = isset($info['port']) ? $info['port'] : null;
+        $path = isset($info['path']) ? $info['path'] : null;
+
+        if (isset($this)) {
+            $config = &$this->config;
+        } else {
+            $config = &PEAR_Config::singleton();
+        }
+
+        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+        if ($config->get('http_proxy') &&
+              $proxy = parse_url($config->get('http_proxy'))) {
+            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
+            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
+                $proxy_host = 'ssl://' . $proxy_host;
+            }
+            $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
+            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
+            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
+
+            if ($callback) {
+                call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
+            }
+        }
+
+        if (empty($port)) {
+            $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80;
+        }
+
+        $scheme = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
+
+        if ($proxy_host != '') {
+            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
+            if (!$fp) {
+                if ($callback) {
+                    call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
+                                                                  $errno, $errstr));
+                }
+                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
+            }
+
+            if ($lastmodified === false || $lastmodified) {
+                $request  = "GET $url HTTP/1.1\r\n";
+                $request .= "Host: $host\r\n";
+            } else {
+                $request  = "GET $url HTTP/1.0\r\n";
+                $request .= "Host: $host\r\n";
+            }
+        } else {
+            $network_host = $host;
+            if (isset($info['scheme']) && $info['scheme'] == 'https') {
+                $network_host = 'ssl://' . $host;
+            }
+
+            $fp = @fsockopen($network_host, $port, $errno, $errstr);
+            if (!$fp) {
+                if ($callback) {
+                    call_user_func($callback, 'connfailed', array($host, $port,
+                                                                  $errno, $errstr));
+                }
+                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
+            }
+
+            if ($lastmodified === false || $lastmodified) {
+                $request = "GET $path HTTP/1.1\r\n";
+                $request .= "Host: $host\r\n";
+            } else {
+                $request = "GET $path HTTP/1.0\r\n";
+                $request .= "Host: $host\r\n";
+            }
+        }
+
+        $ifmodifiedsince = '';
+        if (is_array($lastmodified)) {
+            if (isset($lastmodified['Last-Modified'])) {
+                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
+            }
+
+            if (isset($lastmodified['ETag'])) {
+                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
+            }
+        } else {
+            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
+        }
+
+        $request .= $ifmodifiedsince .
+            "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n";
+
+        if (isset($this)) { // only pass in authentication for non-static calls
+            $username = $config->get('username', null, $channel);
+            $password = $config->get('password', null, $channel);
+            if ($username && $password) {
+                $tmp = base64_encode("$username:$password");
+                $request .= "Authorization: Basic $tmp\r\n";
+            }
+        }
+
+        if ($proxy_host != '' && $proxy_user != '') {
+            $request .= 'Proxy-Authorization: Basic ' .
+                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
+        }
+
+        if ($accept) {
+            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
+        }
+
+        $request .= "Connection: close\r\n";
+        $request .= "\r\n";
+        fwrite($fp, $request);
+        $headers = array();
+        $reply = 0;
+        while (trim($line = fgets($fp, 1024))) {
+            if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
+                $headers[strtolower($matches[1])] = trim($matches[2]);
+            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
+                $reply = (int)$matches[1];
+                if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
+                    return false;
+                }
+
+                if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) {
+                    return PEAR::raiseError("File $scheme://$host:$port$path not valid (received: $line)");
+                }
+            }
+        }
+
+        if ($reply != 200) {
+            if (!isset($headers['location'])) {
+                return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirected but no location)");
+            }
+
+            if ($wasredirect > 4) {
+                return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirection looped more than 5 times)");
+            }
+
+            $redirect = $wasredirect + 1;
+            return $this->downloadHttp($headers['location'],
+                    $ui, $save_dir, $callback, $lastmodified, $accept);
+        }
+
+        if (isset($headers['content-disposition']) &&
+            preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) {
+            $save_as = basename($matches[1]);
+        } else {
+            $save_as = basename($url);
+        }
+
+        if ($callback) {
+            $tmp = call_user_func($callback, 'saveas', $save_as);
+            if ($tmp) {
+                $save_as = $tmp;
+            }
+        }
+
+        $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
+        if (is_link($dest_file)) {
+            return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $dest_file . ' as it is symlinked to ' . readlink($dest_file) . ' - Possible symlink attack');
+        }
+
+        if (!$wp = @fopen($dest_file, 'wb')) {
+            fclose($fp);
+            if ($callback) {
+                call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
+            }
+            return PEAR::raiseError("could not open $dest_file for writing");
+        }
+
+        $length = isset($headers['content-length']) ? $headers['content-length'] : -1;
+
+        $bytes = 0;
+        if ($callback) {
+            call_user_func($callback, 'start', array(basename($dest_file), $length));
+        }
+
+        while ($data = fread($fp, 1024)) {
+            $bytes += strlen($data);
+            if ($callback) {
+                call_user_func($callback, 'bytesread', $bytes);
+            }
+            if (!@fwrite($wp, $data)) {
+                fclose($fp);
+                if ($callback) {
+                    call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
+                }
+                return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
+            }
+        }
+
+        fclose($fp);
+        fclose($wp);
+        if ($callback) {
+            call_user_func($callback, 'done', $bytes);
+        }
+
+        if ($lastmodified === false || $lastmodified) {
+            if (isset($headers['etag'])) {
+                $lastmodified = array('ETag' => $headers['etag']);
+            }
+
+            if (isset($headers['last-modified'])) {
+                if (is_array($lastmodified)) {
+                    $lastmodified['Last-Modified'] = $headers['last-modified'];
+                } else {
+                    $lastmodified = $headers['last-modified'];
+                }
+            }
+            return array($dest_file, $lastmodified, $headers);
+        }
+        return $dest_file;
+    }
+}
Index: trunk/library/PEAR/PEAR/Exception.php
===================================================================
--- trunk/library/PEAR/PEAR/Exception.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Exception.php	(revision 5146)
@@ -0,0 +1,389 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
+/**
+ * PEAR_Exception
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Hans Lellelid <hans@velum.net>
+ * @author     Bertrand Mansion <bmansion@mamasam.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Exception.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.3.3
+ */
+
+
+/**
+ * Base PEAR_Exception Class
+ *
+ * 1) Features:
+ *
+ * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
+ * - Definable triggers, shot when exceptions occur
+ * - Pretty and informative error messages
+ * - Added more context info available (like class, method or cause)
+ * - cause can be a PEAR_Exception or an array of mixed
+ *   PEAR_Exceptions/PEAR_ErrorStack warnings
+ * - callbacks for specific exception classes and their children
+ *
+ * 2) Ideas:
+ *
+ * - Maybe a way to define a 'template' for the output
+ *
+ * 3) Inherited properties from PHP Exception Class:
+ *
+ * protected $message
+ * protected $code
+ * protected $line
+ * protected $file
+ * private   $trace
+ *
+ * 4) Inherited methods from PHP Exception Class:
+ *
+ * __clone
+ * __construct
+ * getMessage
+ * getCode
+ * getFile
+ * getLine
+ * getTraceSafe
+ * getTraceSafeAsString
+ * __toString
+ *
+ * 5) Usage example
+ *
+ * <code>
+ *  require_once 'PEAR/Exception.php';
+ *
+ *  class Test {
+ *     function foo() {
+ *         throw new PEAR_Exception('Error Message', ERROR_CODE);
+ *     }
+ *  }
+ *
+ *  function myLogger($pear_exception) {
+ *     echo $pear_exception->getMessage();
+ *  }
+ *  // each time a exception is thrown the 'myLogger' will be called
+ *  // (its use is completely optional)
+ *  PEAR_Exception::addObserver('myLogger');
+ *  $test = new Test;
+ *  try {
+ *     $test->foo();
+ *  } catch (PEAR_Exception $e) {
+ *     print $e;
+ *  }
+ * </code>
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Hans Lellelid <hans@velum.net>
+ * @author     Bertrand Mansion <bmansion@mamasam.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.3.3
+ *
+ */
+class PEAR_Exception extends Exception
+{
+    const OBSERVER_PRINT = -2;
+    const OBSERVER_TRIGGER = -4;
+    const OBSERVER_DIE = -8;
+    protected $cause;
+    private static $_observers = array();
+    private static $_uniqueid = 0;
+    private $_trace;
+
+    /**
+     * Supported signatures:
+     *  - PEAR_Exception(string $message);
+     *  - PEAR_Exception(string $message, int $code);
+     *  - PEAR_Exception(string $message, Exception $cause);
+     *  - PEAR_Exception(string $message, Exception $cause, int $code);
+     *  - PEAR_Exception(string $message, PEAR_Error $cause);
+     *  - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
+     *  - PEAR_Exception(string $message, array $causes);
+     *  - PEAR_Exception(string $message, array $causes, int $code);
+     * @param string exception message
+     * @param int|Exception|PEAR_Error|array|null exception cause
+     * @param int|null exception code or null
+     */
+    public function __construct($message, $p2 = null, $p3 = null)
+    {
+        if (is_int($p2)) {
+            $code = $p2;
+            $this->cause = null;
+        } elseif (is_object($p2) || is_array($p2)) {
+            // using is_object allows both Exception and PEAR_Error
+            if (is_object($p2) && !($p2 instanceof Exception)) {
+                if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
+                    throw new PEAR_Exception('exception cause must be Exception, ' .
+                        'array, or PEAR_Error');
+                }
+            }
+            $code = $p3;
+            if (is_array($p2) && isset($p2['message'])) {
+                // fix potential problem of passing in a single warning
+                $p2 = array($p2);
+            }
+            $this->cause = $p2;
+        } else {
+            $code = null;
+            $this->cause = null;
+        }
+        parent::__construct($message, $code);
+        $this->signal();
+    }
+
+    /**
+     * @param mixed $callback  - A valid php callback, see php func is_callable()
+     *                         - A PEAR_Exception::OBSERVER_* constant
+     *                         - An array(const PEAR_Exception::OBSERVER_*,
+     *                           mixed $options)
+     * @param string $label    The name of the observer. Use this if you want
+     *                         to remove it later with removeObserver()
+     */
+    public static function addObserver($callback, $label = 'default')
+    {
+        self::$_observers[$label] = $callback;
+    }
+
+    public static function removeObserver($label = 'default')
+    {
+        unset(self::$_observers[$label]);
+    }
+
+    /**
+     * @return int unique identifier for an observer
+     */
+    public static function getUniqueId()
+    {
+        return self::$_uniqueid++;
+    }
+
+    private function signal()
+    {
+        foreach (self::$_observers as $func) {
+            if (is_callable($func)) {
+                call_user_func($func, $this);
+                continue;
+            }
+            settype($func, 'array');
+            switch ($func[0]) {
+                case self::OBSERVER_PRINT :
+                    $f = (isset($func[1])) ? $func[1] : '%s';
+                    printf($f, $this->getMessage());
+                    break;
+                case self::OBSERVER_TRIGGER :
+                    $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
+                    trigger_error($this->getMessage(), $f);
+                    break;
+                case self::OBSERVER_DIE :
+                    $f = (isset($func[1])) ? $func[1] : '%s';
+                    die(printf($f, $this->getMessage()));
+                    break;
+                default:
+                    trigger_error('invalid observer type', E_USER_WARNING);
+            }
+        }
+    }
+
+    /**
+     * Return specific error information that can be used for more detailed
+     * error messages or translation.
+     *
+     * This method may be overridden in child exception classes in order
+     * to add functionality not present in PEAR_Exception and is a placeholder
+     * to define API
+     *
+     * The returned array must be an associative array of parameter => value like so:
+     * <pre>
+     * array('name' => $name, 'context' => array(...))
+     * </pre>
+     * @return array
+     */
+    public function getErrorData()
+    {
+        return array();
+    }
+
+    /**
+     * Returns the exception that caused this exception to be thrown
+     * @access public
+     * @return Exception|array The context of the exception
+     */
+    public function getCause()
+    {
+        return $this->cause;
+    }
+
+    /**
+     * Function must be public to call on caused exceptions
+     * @param array
+     */
+    public function getCauseMessage(&$causes)
+    {
+        $trace = $this->getTraceSafe();
+        $cause = array('class'   => get_class($this),
+                       'message' => $this->message,
+                       'file' => 'unknown',
+                       'line' => 'unknown');
+        if (isset($trace[0])) {
+            if (isset($trace[0]['file'])) {
+                $cause['file'] = $trace[0]['file'];
+                $cause['line'] = $trace[0]['line'];
+            }
+        }
+        $causes[] = $cause;
+        if ($this->cause instanceof PEAR_Exception) {
+            $this->cause->getCauseMessage($causes);
+        } elseif ($this->cause instanceof Exception) {
+            $causes[] = array('class'   => get_class($this->cause),
+                              'message' => $this->cause->getMessage(),
+                              'file' => $this->cause->getFile(),
+                              'line' => $this->cause->getLine());
+        } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
+            $causes[] = array('class' => get_class($this->cause),
+                              'message' => $this->cause->getMessage(),
+                              'file' => 'unknown',
+                              'line' => 'unknown');
+        } elseif (is_array($this->cause)) {
+            foreach ($this->cause as $cause) {
+                if ($cause instanceof PEAR_Exception) {
+                    $cause->getCauseMessage($causes);
+                } elseif ($cause instanceof Exception) {
+                    $causes[] = array('class'   => get_class($cause),
+                                   'message' => $cause->getMessage(),
+                                   'file' => $cause->getFile(),
+                                   'line' => $cause->getLine());
+                } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
+                    $causes[] = array('class' => get_class($cause),
+                                      'message' => $cause->getMessage(),
+                                      'file' => 'unknown',
+                                      'line' => 'unknown');
+                } elseif (is_array($cause) && isset($cause['message'])) {
+                    // PEAR_ErrorStack warning
+                    $causes[] = array(
+                        'class' => $cause['package'],
+                        'message' => $cause['message'],
+                        'file' => isset($cause['context']['file']) ?
+                                            $cause['context']['file'] :
+                                            'unknown',
+                        'line' => isset($cause['context']['line']) ?
+                                            $cause['context']['line'] :
+                                            'unknown',
+                    );
+                }
+            }
+        }
+    }
+
+    public function getTraceSafe()
+    {
+        if (!isset($this->_trace)) {
+            $this->_trace = $this->getTrace();
+            if (empty($this->_trace)) {
+                $backtrace = debug_backtrace();
+                $this->_trace = array($backtrace[count($backtrace)-1]);
+            }
+        }
+        return $this->_trace;
+    }
+
+    public function getErrorClass()
+    {
+        $trace = $this->getTraceSafe();
+        return $trace[0]['class'];
+    }
+
+    public function getErrorMethod()
+    {
+        $trace = $this->getTraceSafe();
+        return $trace[0]['function'];
+    }
+
+    public function __toString()
+    {
+        if (isset($_SERVER['REQUEST_URI'])) {
+            return $this->toHtml();
+        }
+        return $this->toText();
+    }
+
+    public function toHtml()
+    {
+        $trace = $this->getTraceSafe();
+        $causes = array();
+        $this->getCauseMessage($causes);
+        $html =  '<table style="border: 1px" cellspacing="0">' . "\n";
+        foreach ($causes as $i => $cause) {
+            $html .= '<tr><td colspan="3" style="background: #ff9999">'
+               . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
+               . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
+               . 'on line <b>' . $cause['line'] . '</b>'
+               . "</td></tr>\n";
+        }
+        $html .= '<tr><td colspan="3" style="background-color: #aaaaaa; text-align: center; font-weight: bold;">Exception trace</td></tr>' . "\n"
+               . '<tr><td style="text-align: center; background: #cccccc; width:20px; font-weight: bold;">#</td>'
+               . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Function</td>'
+               . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Location</td></tr>' . "\n";
+
+        foreach ($trace as $k => $v) {
+            $html .= '<tr><td style="text-align: center;">' . $k . '</td>'
+                   . '<td>';
+            if (!empty($v['class'])) {
+                $html .= $v['class'] . $v['type'];
+            }
+            $html .= $v['function'];
+            $args = array();
+            if (!empty($v['args'])) {
+                foreach ($v['args'] as $arg) {
+                    if (is_null($arg)) $args[] = 'null';
+                    elseif (is_array($arg)) $args[] = 'Array';
+                    elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
+                    elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
+                    elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
+                    else {
+                        $arg = (string)$arg;
+                        $str = htmlspecialchars(substr($arg, 0, 16));
+                        if (strlen($arg) > 16) $str .= '&hellip;';
+                        $args[] = "'" . $str . "'";
+                    }
+                }
+            }
+            $html .= '(' . implode(', ',$args) . ')'
+                   . '</td>'
+                   . '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
+                   . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
+                   . '</td></tr>' . "\n";
+        }
+        $html .= '<tr><td style="text-align: center;">' . ($k+1) . '</td>'
+               . '<td>{main}</td>'
+               . '<td>&nbsp;</td></tr>' . "\n"
+               . '</table>';
+        return $html;
+    }
+
+    public function toText()
+    {
+        $causes = array();
+        $this->getCauseMessage($causes);
+        $causeMsg = '';
+        foreach ($causes as $i => $cause) {
+            $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
+                   . $cause['message'] . ' in ' . $cause['file']
+                   . ' on line ' . $cause['line'] . "\n";
+        }
+        return $causeMsg . $this->getTraceAsString();
+    }
+}
Index: trunk/library/PEAR/PEAR/Validator/PECL.php
===================================================================
--- trunk/library/PEAR/PEAR/Validator/PECL.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Validator/PECL.php	(revision 5146)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Channel Validator for the pecl.php.net channel
+ *
+ * PHP 4 and PHP 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2006 The PHP Group
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: PECL.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a5
+ */
+/**
+ * This is the parent class for all validators
+ */
+require_once 'PEAR/Validate.php';
+/**
+ * Channel Validator for the pecl.php.net channel
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a5
+ */
+class PEAR_Validator_PECL extends PEAR_Validate
+{
+    function validateVersion()
+    {
+        if ($this->_state == PEAR_VALIDATE_PACKAGING) {
+            $version = $this->_packagexml->getVersion();
+            $versioncomponents = explode('.', $version);
+            $last = array_pop($versioncomponents);
+            if (substr($last, 1, 2) == 'rc') {
+                $this->_addFailure('version', 'Release Candidate versions must have ' .
+                'upper-case RC, not lower-case rc');
+                return false;
+            }
+        }
+        return true;
+    }
+
+    function validatePackageName()
+    {
+        $ret = parent::validatePackageName();
+        if ($this->_packagexml->getPackageType() == 'extsrc' ||
+              $this->_packagexml->getPackageType() == 'zendextsrc') {
+            if (strtolower($this->_packagexml->getPackage()) !=
+                  strtolower($this->_packagexml->getProvidesExtension())) {
+                $this->_addWarning('providesextension', 'package name "' .
+                    $this->_packagexml->getPackage() . '" is different from extension name "' .
+                    $this->_packagexml->getProvidesExtension() . '"');
+            }
+        }
+        return $ret;
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Autoloader.php
===================================================================
--- trunk/library/PEAR/PEAR/Autoloader.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Autoloader.php	(revision 5146)
@@ -0,0 +1,218 @@
+<?php
+/**
+ * Class auto-loader
+ *
+ * PHP versions 4
+
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Autoloader.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader
+ * @since      File available since Release 0.1
+ * @deprecated File deprecated in Release 1.4.0a1
+ */
+
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+
+if (!extension_loaded("overload")) {
+    // die hard without ext/overload
+    die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader");
+}
+
+/**
+ * Include for PEAR_Error and PEAR classes
+ */
+require_once "PEAR.php";
+
+/**
+ * This class is for objects where you want to separate the code for
+ * some methods into separate classes.  This is useful if you have a
+ * class with not-frequently-used methods that contain lots of code
+ * that you would like to avoid always parsing.
+ *
+ * The PEAR_Autoloader class provides autoloading and aggregation.
+ * The autoloading lets you set up in which classes the separated
+ * methods are found.  Aggregation is the technique used to import new
+ * methods, an instance of each class providing separated methods is
+ * stored and called every time the aggregated method is called.
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author Stig Bakken <ssb@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader
+ * @since      File available since Release 0.1
+ * @deprecated File deprecated in Release 1.4.0a1
+ */
+class PEAR_Autoloader extends PEAR
+{
+    // {{{ properties
+
+    /**
+     * Map of methods and classes where they are defined
+     *
+     * @var array
+     *
+     * @access private
+     */
+    var $_autoload_map = array();
+
+    /**
+     * Map of methods and aggregate objects
+     *
+     * @var array
+     *
+     * @access private
+     */
+    var $_method_map = array();
+
+    // }}}
+    // {{{ addAutoload()
+
+    /**
+     * Add one or more autoload entries.
+     *
+     * @param string $method     which method to autoload
+     *
+     * @param string $classname  (optional) which class to find the method in.
+     *                           If the $method parameter is an array, this
+     *                           parameter may be omitted (and will be ignored
+     *                           if not), and the $method parameter will be
+     *                           treated as an associative array with method
+     *                           names as keys and class names as values.
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function addAutoload($method, $classname = null)
+    {
+        if (is_array($method)) {
+            array_walk($method, create_function('$a,&$b', '$b = strtolower($b);'));
+            $this->_autoload_map = array_merge($this->_autoload_map, $method);
+        } else {
+            $this->_autoload_map[strtolower($method)] = $classname;
+        }
+    }
+
+    // }}}
+    // {{{ removeAutoload()
+
+    /**
+     * Remove an autoload entry.
+     *
+     * @param string $method  which method to remove the autoload entry for
+     *
+     * @return bool TRUE if an entry was removed, FALSE if not
+     *
+     * @access public
+     */
+    function removeAutoload($method)
+    {
+        $method = strtolower($method);
+        $ok = isset($this->_autoload_map[$method]);
+        unset($this->_autoload_map[$method]);
+        return $ok;
+    }
+
+    // }}}
+    // {{{ addAggregateObject()
+
+    /**
+     * Add an aggregate object to this object.  If the specified class
+     * is not defined, loading it will be attempted following PEAR's
+     * file naming scheme.  All the methods in the class will be
+     * aggregated, except private ones (name starting with an
+     * underscore) and constructors.
+     *
+     * @param string $classname  what class to instantiate for the object.
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function addAggregateObject($classname)
+    {
+        $classname = strtolower($classname);
+        if (!class_exists($classname)) {
+            $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname);
+            include_once $include_file;
+        }
+        $obj =& new $classname;
+        $methods = get_class_methods($classname);
+        foreach ($methods as $method) {
+            // don't import priviate methods and constructors
+            if ($method{0} != '_' && $method != $classname) {
+                $this->_method_map[$method] = $obj;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ removeAggregateObject()
+
+    /**
+     * Remove an aggregate object.
+     *
+     * @param string $classname  the class of the object to remove
+     *
+     * @return bool  TRUE if an object was removed, FALSE if not
+     *
+     * @access public
+     */
+    function removeAggregateObject($classname)
+    {
+        $ok = false;
+        $classname = strtolower($classname);
+        reset($this->_method_map);
+        while (list($method, $obj) = each($this->_method_map)) {
+            if (is_a($obj, $classname)) {
+                unset($this->_method_map[$method]);
+                $ok = true;
+            }
+        }
+        return $ok;
+    }
+
+    // }}}
+    // {{{ __call()
+
+    /**
+     * Overloaded object call handler, called each time an
+     * undefined/aggregated method is invoked.  This method repeats
+     * the call in the right aggregate object and passes on the return
+     * value.
+     *
+     * @param string $method  which method that was called
+     *
+     * @param string $args    An array of the parameters passed in the
+     *                        original call
+     *
+     * @return mixed  The return value from the aggregated method, or a PEAR
+     *                error if the called method was unknown.
+     */
+    function __call($method, $args, &$retval)
+    {
+        $method = strtolower($method);
+        if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) {
+            $this->addAggregateObject($this->_autoload_map[$method]);
+        }
+        if (isset($this->_method_map[$method])) {
+            $retval = call_user_func_array(array($this->_method_map[$method], $method), $args);
+            return true;
+        }
+        return false;
+    }
+
+    // }}}
+}
+
+overload("PEAR_Autoloader");
+
+?>
Index: trunk/library/PEAR/PEAR/Dependency2.php
===================================================================
--- trunk/library/PEAR/PEAR/Dependency2.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Dependency2.php	(revision 5146)
@@ -0,0 +1,1358 @@
+<?php
+/**
+ * PEAR_Dependency2, advanced dependency validation
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Dependency2.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * Required for the PEAR_VALIDATE_* constants
+ */
+require_once 'PEAR/Validate.php';
+
+/**
+ * Dependency check for PEAR packages
+ *
+ * This class handles both version 1.0 and 2.0 dependencies
+ * WARNING: *any* changes to this class must be duplicated in the
+ * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc,
+ * or unit tests will not actually validate the changes
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Dependency2
+{
+    /**
+     * One of the PEAR_VALIDATE_* states
+     * @see PEAR_VALIDATE_NORMAL
+     * @var integer
+     */
+    var $_state;
+
+    /**
+     * Command-line options to install/upgrade/uninstall commands
+     * @param array
+     */
+    var $_options;
+
+    /**
+     * @var OS_Guess
+     */
+    var $_os;
+
+    /**
+     * @var PEAR_Registry
+     */
+    var $_registry;
+
+    /**
+     * @var PEAR_Config
+     */
+    var $_config;
+
+    /**
+     * @var PEAR_DependencyDB
+     */
+    var $_dependencydb;
+
+    /**
+     * Output of PEAR_Registry::parsedPackageName()
+     * @var array
+     */
+    var $_currentPackage;
+
+    /**
+     * @param PEAR_Config
+     * @param array installation options
+     * @param array format of PEAR_Registry::parsedPackageName()
+     * @param int installation state (one of PEAR_VALIDATE_*)
+     */
+    function PEAR_Dependency2(&$config, $installoptions, $package,
+                              $state = PEAR_VALIDATE_INSTALLING)
+    {
+        $this->_config = &$config;
+        if (!class_exists('PEAR_DependencyDB')) {
+            require_once 'PEAR/DependencyDB.php';
+        }
+
+        if (isset($installoptions['packagingroot'])) {
+            // make sure depdb is in the right location
+            $config->setInstallRoot($installoptions['packagingroot']);
+        }
+
+        $this->_registry = &$config->getRegistry();
+        $this->_dependencydb = &PEAR_DependencyDB::singleton($config);
+        if (isset($installoptions['packagingroot'])) {
+            $config->setInstallRoot(false);
+        }
+
+        $this->_options = $installoptions;
+        $this->_state = $state;
+        if (!class_exists('OS_Guess')) {
+            require_once 'OS/Guess.php';
+        }
+
+        $this->_os = new OS_Guess;
+        $this->_currentPackage = $package;
+    }
+
+    function _getExtraString($dep)
+    {
+        $extra = ' (';
+        if (isset($dep['uri'])) {
+            return '';
+        }
+
+        if (isset($dep['recommended'])) {
+            $extra .= 'recommended version ' . $dep['recommended'];
+        } else {
+            if (isset($dep['min'])) {
+                $extra .= 'version >= ' . $dep['min'];
+            }
+
+            if (isset($dep['max'])) {
+                if ($extra != ' (') {
+                    $extra .= ', ';
+                }
+                $extra .= 'version <= ' . $dep['max'];
+            }
+
+            if (isset($dep['exclude'])) {
+                if (!is_array($dep['exclude'])) {
+                    $dep['exclude'] = array($dep['exclude']);
+                }
+
+                if ($extra != ' (') {
+                    $extra .= ', ';
+                }
+
+                $extra .= 'excluded versions: ';
+                foreach ($dep['exclude'] as $i => $exclude) {
+                    if ($i) {
+                        $extra .= ', ';
+                    }
+                    $extra .= $exclude;
+                }
+            }
+        }
+
+        $extra .= ')';
+        if ($extra == ' ()') {
+            $extra = '';
+        }
+
+        return $extra;
+    }
+
+    /**
+     * This makes unit-testing a heck of a lot easier
+     */
+    function getPHP_OS()
+    {
+        return PHP_OS;
+    }
+
+    /**
+     * This makes unit-testing a heck of a lot easier
+     */
+    function getsysname()
+    {
+        return $this->_os->getSysname();
+    }
+
+    /**
+     * Specify a dependency on an OS.  Use arch for detailed os/processor information
+     *
+     * There are two generic OS dependencies that will be the most common, unix and windows.
+     * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix
+     */
+    function validateOsDependency($dep)
+    {
+        if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) {
+            return true;
+        }
+
+        if ($dep['name'] == '*') {
+            return true;
+        }
+
+        $not = isset($dep['conflicts']) ? true : false;
+        switch (strtolower($dep['name'])) {
+            case 'windows' :
+                if ($not) {
+                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') {
+                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                            return $this->raiseError("Cannot install %s on Windows");
+                        }
+
+                        return $this->warning("warning: Cannot install %s on Windows");
+                    }
+                } else {
+                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') {
+                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                            return $this->raiseError("Can only install %s on Windows");
+                        }
+
+                        return $this->warning("warning: Can only install %s on Windows");
+                    }
+                }
+            break;
+            case 'unix' :
+                $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix');
+                if ($not) {
+                    if (in_array($this->getSysname(), $unices)) {
+                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                            return $this->raiseError("Cannot install %s on any Unix system");
+                        }
+
+                        return $this->warning( "warning: Cannot install %s on any Unix system");
+                    }
+                } else {
+                    if (!in_array($this->getSysname(), $unices)) {
+                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                            return $this->raiseError("Can only install %s on a Unix system");
+                        }
+
+                        return $this->warning("warning: Can only install %s on a Unix system");
+                    }
+                }
+            break;
+            default :
+                if ($not) {
+                    if (strtolower($dep['name']) == strtolower($this->getSysname())) {
+                        if (!isset($this->_options['nodeps']) &&
+                              !isset($this->_options['force'])) {
+                            return $this->raiseError('Cannot install %s on ' . $dep['name'] .
+                                ' operating system');
+                        }
+
+                        return $this->warning('warning: Cannot install %s on ' .
+                            $dep['name'] . ' operating system');
+                    }
+                } else {
+                    if (strtolower($dep['name']) != strtolower($this->getSysname())) {
+                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                            return $this->raiseError('Cannot install %s on ' .
+                                $this->getSysname() .
+                                ' operating system, can only install on ' . $dep['name']);
+                        }
+
+                        return $this->warning('warning: Cannot install %s on ' .
+                            $this->getSysname() .
+                            ' operating system, can only install on ' . $dep['name']);
+                    }
+                }
+        }
+        return true;
+    }
+
+    /**
+     * This makes unit-testing a heck of a lot easier
+     */
+    function matchSignature($pattern)
+    {
+        return $this->_os->matchSignature($pattern);
+    }
+
+    /**
+     * Specify a complex dependency on an OS/processor/kernel version,
+     * Use OS for simple operating system dependency.
+     *
+     * This is the only dependency that accepts an eregable pattern.  The pattern
+     * will be matched against the php_uname() output parsed by OS_Guess
+     */
+    function validateArchDependency($dep)
+    {
+        if ($this->_state != PEAR_VALIDATE_INSTALLING) {
+            return true;
+        }
+
+        $not = isset($dep['conflicts']) ? true : false;
+        if (!$this->matchSignature($dep['pattern'])) {
+            if (!$not) {
+                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                    return $this->raiseError('%s Architecture dependency failed, does not ' .
+                        'match "' . $dep['pattern'] . '"');
+                }
+
+                return $this->warning('warning: %s Architecture dependency failed, does ' .
+                    'not match "' . $dep['pattern'] . '"');
+            }
+
+            return true;
+        }
+
+        if ($not) {
+            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                return $this->raiseError('%s Architecture dependency failed, required "' .
+                    $dep['pattern'] . '"');
+            }
+
+            return $this->warning('warning: %s Architecture dependency failed, ' .
+                'required "' . $dep['pattern'] . '"');
+        }
+
+        return true;
+    }
+
+    /**
+     * This makes unit-testing a heck of a lot easier
+     */
+    function extension_loaded($name)
+    {
+        return extension_loaded($name);
+    }
+
+    /**
+     * This makes unit-testing a heck of a lot easier
+     */
+    function phpversion($name = null)
+    {
+        if ($name !== null) {
+            return phpversion($name);
+        }
+
+        return phpversion();
+    }
+
+    function validateExtensionDependency($dep, $required = true)
+    {
+        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
+              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
+            return true;
+        }
+
+        $loaded = $this->extension_loaded($dep['name']);
+        $extra  = $this->_getExtraString($dep);
+        if (isset($dep['exclude'])) {
+            if (!is_array($dep['exclude'])) {
+                $dep['exclude'] = array($dep['exclude']);
+            }
+        }
+
+        if (!isset($dep['min']) && !isset($dep['max']) &&
+            !isset($dep['recommended']) && !isset($dep['exclude'])
+        ) {
+            if ($loaded) {
+                if (isset($dep['conflicts'])) {
+                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                        return $this->raiseError('%s conflicts with PHP extension "' .
+                            $dep['name'] . '"' . $extra);
+                    }
+
+                    return $this->warning('warning: %s conflicts with PHP extension "' .
+                        $dep['name'] . '"' . $extra);
+                }
+
+                return true;
+            }
+
+            if (isset($dep['conflicts'])) {
+                return true;
+            }
+
+            if ($required) {
+                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                    return $this->raiseError('%s requires PHP extension "' .
+                        $dep['name'] . '"' . $extra);
+                }
+
+                return $this->warning('warning: %s requires PHP extension "' .
+                    $dep['name'] . '"' . $extra);
+            }
+
+            return $this->warning('%s can optionally use PHP extension "' .
+                $dep['name'] . '"' . $extra);
+        }
+
+        if (!$loaded) {
+            if (isset($dep['conflicts'])) {
+                return true;
+            }
+
+            if (!$required) {
+                return $this->warning('%s can optionally use PHP extension "' .
+                    $dep['name'] . '"' . $extra);
+            }
+
+            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
+                    '"' . $extra);
+            }
+
+            return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
+                    '"' . $extra);
+        }
+
+        $version = (string) $this->phpversion($dep['name']);
+        if (empty($version)) {
+            $version = '0';
+        }
+
+        $fail = false;
+        if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) {
+            $fail = true;
+        }
+
+        if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) {
+            $fail = true;
+        }
+
+        if ($fail && !isset($dep['conflicts'])) {
+            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
+                    '"' . $extra . ', installed version is ' . $version);
+            }
+
+            return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
+                '"' . $extra . ', installed version is ' . $version);
+        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) {
+            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                return $this->raiseError('%s conflicts with PHP extension "' .
+                    $dep['name'] . '"' . $extra . ', installed version is ' . $version);
+            }
+
+            return $this->warning('warning: %s conflicts with PHP extension "' .
+                $dep['name'] . '"' . $extra . ', installed version is ' . $version);
+        }
+
+        if (isset($dep['exclude'])) {
+            foreach ($dep['exclude'] as $exclude) {
+                if (version_compare($version, $exclude, '==')) {
+                    if (isset($dep['conflicts'])) {
+                        continue;
+                    }
+
+                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                        return $this->raiseError('%s is not compatible with PHP extension "' .
+                            $dep['name'] . '" version ' .
+                            $exclude);
+                    }
+
+                    return $this->warning('warning: %s is not compatible with PHP extension "' .
+                        $dep['name'] . '" version ' .
+                        $exclude);
+                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
+                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                        return $this->raiseError('%s conflicts with PHP extension "' .
+                            $dep['name'] . '"' . $extra . ', installed version is ' . $version);
+                    }
+
+                    return $this->warning('warning: %s conflicts with PHP extension "' .
+                        $dep['name'] . '"' . $extra . ', installed version is ' . $version);
+                }
+            }
+        }
+
+        if (isset($dep['recommended'])) {
+            if (version_compare($version, $dep['recommended'], '==')) {
+                return true;
+            }
+
+            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] .
+                    ' version "' . $version . '"' .
+                    ' is not the recommended version "' . $dep['recommended'] .
+                    '", but may be compatible, use --force to install');
+            }
+
+            return $this->warning('warning: %s dependency: PHP extension ' .
+                $dep['name'] . ' version "' . $version . '"' .
+                ' is not the recommended version "' . $dep['recommended'].'"');
+        }
+
+        return true;
+    }
+
+    function validatePhpDependency($dep)
+    {
+        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
+              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
+            return true;
+        }
+
+        $version = $this->phpversion();
+        $extra   = $this->_getExtraString($dep);
+        if (isset($dep['exclude'])) {
+            if (!is_array($dep['exclude'])) {
+                $dep['exclude'] = array($dep['exclude']);
+            }
+        }
+
+        if (isset($dep['min'])) {
+            if (!version_compare($version, $dep['min'], '>=')) {
+                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                    return $this->raiseError('%s requires PHP' .
+                        $extra . ', installed version is ' . $version);
+                }
+
+                return $this->warning('warning: %s requires PHP' .
+                    $extra . ', installed version is ' . $version);
+            }
+        }
+
+        if (isset($dep['max'])) {
+            if (!version_compare($version, $dep['max'], '<=')) {
+                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                    return $this->raiseError('%s requires PHP' .
+                        $extra . ', installed version is ' . $version);
+                }
+
+                return $this->warning('warning: %s requires PHP' .
+                    $extra . ', installed version is ' . $version);
+            }
+        }
+
+        if (isset($dep['exclude'])) {
+            foreach ($dep['exclude'] as $exclude) {
+                if (version_compare($version, $exclude, '==')) {
+                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                        return $this->raiseError('%s is not compatible with PHP version ' .
+                            $exclude);
+                    }
+
+                    return $this->warning(
+                        'warning: %s is not compatible with PHP version ' .
+                        $exclude);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * This makes unit-testing a heck of a lot easier
+     */
+    function getPEARVersion()
+    {
+        return '1.9.4';
+    }
+
+    function validatePearinstallerDependency($dep)
+    {
+        $pearversion = $this->getPEARVersion();
+        $extra = $this->_getExtraString($dep);
+        if (isset($dep['exclude'])) {
+            if (!is_array($dep['exclude'])) {
+                $dep['exclude'] = array($dep['exclude']);
+            }
+        }
+
+        if (version_compare($pearversion, $dep['min'], '<')) {
+            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                return $this->raiseError('%s requires PEAR Installer' . $extra .
+                    ', installed version is ' . $pearversion);
+            }
+
+            return $this->warning('warning: %s requires PEAR Installer' . $extra .
+                ', installed version is ' . $pearversion);
+        }
+
+        if (isset($dep['max'])) {
+            if (version_compare($pearversion, $dep['max'], '>')) {
+                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                    return $this->raiseError('%s requires PEAR Installer' . $extra .
+                        ', installed version is ' . $pearversion);
+                }
+
+                return $this->warning('warning: %s requires PEAR Installer' . $extra .
+                    ', installed version is ' . $pearversion);
+            }
+        }
+
+        if (isset($dep['exclude'])) {
+            if (!isset($dep['exclude'][0])) {
+                $dep['exclude'] = array($dep['exclude']);
+            }
+
+            foreach ($dep['exclude'] as $exclude) {
+                if (version_compare($exclude, $pearversion, '==')) {
+                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                        return $this->raiseError('%s is not compatible with PEAR Installer ' .
+                            'version ' . $exclude);
+                    }
+
+                    return $this->warning('warning: %s is not compatible with PEAR ' .
+                        'Installer version ' . $exclude);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    function validateSubpackageDependency($dep, $required, $params)
+    {
+        return $this->validatePackageDependency($dep, $required, $params);
+    }
+
+    /**
+     * @param array dependency information (2.0 format)
+     * @param boolean whether this is a required dependency
+     * @param array a list of downloaded packages to be installed, if any
+     * @param boolean if true, then deps on pear.php.net that fail will also check
+     *                against pecl.php.net packages to accomodate extensions that have
+     *                moved to pecl.php.net from pear.php.net
+     */
+    function validatePackageDependency($dep, $required, $params, $depv1 = false)
+    {
+        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
+              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
+            return true;
+        }
+
+        if (isset($dep['providesextension'])) {
+            if ($this->extension_loaded($dep['providesextension'])) {
+                $save = $dep;
+                $subdep = $dep;
+                $subdep['name'] = $subdep['providesextension'];
+                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                $ret = $this->validateExtensionDependency($subdep, $required);
+                PEAR::popErrorHandling();
+                if (!PEAR::isError($ret)) {
+                    return true;
+                }
+            }
+        }
+
+        if ($this->_state == PEAR_VALIDATE_INSTALLING) {
+            return $this->_validatePackageInstall($dep, $required, $depv1);
+        }
+
+        if ($this->_state == PEAR_VALIDATE_DOWNLOADING) {
+            return $this->_validatePackageDownload($dep, $required, $params, $depv1);
+        }
+    }
+
+    function _validatePackageDownload($dep, $required, $params, $depv1 = false)
+    {
+        $dep['package'] = $dep['name'];
+        if (isset($dep['uri'])) {
+            $dep['channel'] = '__uri';
+        }
+
+        $depname = $this->_registry->parsedPackageNameToString($dep, true);
+        $found = false;
+        foreach ($params as $param) {
+            if ($param->isEqual(
+                  array('package' => $dep['name'],
+                        'channel' => $dep['channel']))) {
+                $found = true;
+                break;
+            }
+
+            if ($depv1 && $dep['channel'] == 'pear.php.net') {
+                if ($param->isEqual(
+                  array('package' => $dep['name'],
+                        'channel' => 'pecl.php.net'))) {
+                    $found = true;
+                    break;
+                }
+            }
+        }
+
+        if (!$found && isset($dep['providesextension'])) {
+            foreach ($params as $param) {
+                if ($param->isExtension($dep['providesextension'])) {
+                    $found = true;
+                    break;
+                }
+            }
+        }
+
+        if ($found) {
+            $version = $param->getVersion();
+            $installed = false;
+            $downloaded = true;
+        } else {
+            if ($this->_registry->packageExists($dep['name'], $dep['channel'])) {
+                $installed = true;
+                $downloaded = false;
+                $version = $this->_registry->packageinfo($dep['name'], 'version',
+                    $dep['channel']);
+            } else {
+                if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'],
+                      'pear.php.net')) {
+                    $installed = true;
+                    $downloaded = false;
+                    $version = $this->_registry->packageinfo($dep['name'], 'version',
+                        'pear.php.net');
+                } else {
+                    $version = 'not installed or downloaded';
+                    $installed = false;
+                    $downloaded = false;
+                }
+            }
+        }
+
+        $extra = $this->_getExtraString($dep);
+        if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
+            $dep['exclude'] = array($dep['exclude']);
+        }
+
+        if (!isset($dep['min']) && !isset($dep['max']) &&
+              !isset($dep['recommended']) && !isset($dep['exclude'])
+        ) {
+            if ($installed || $downloaded) {
+                $installed = $installed ? 'installed' : 'downloaded';
+                if (isset($dep['conflicts'])) {
+                    $rest = '';
+                    if ($version) {
+                        $rest = ", $installed version is " . $version;
+                    }
+
+                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                        return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest);
+                    }
+
+                    return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest);
+                }
+
+                return true;
+            }
+
+            if (isset($dep['conflicts'])) {
+                return true;
+            }
+
+            if ($required) {
+                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                    return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
+                }
+
+                return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
+            }
+
+            return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
+        }
+
+        if (!$installed && !$downloaded) {
+            if (isset($dep['conflicts'])) {
+                return true;
+            }
+
+            if ($required) {
+                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                    return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
+                }
+
+                return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
+            }
+
+            return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
+        }
+
+        $fail = false;
+        if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) {
+            $fail = true;
+        }
+
+        if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) {
+            $fail = true;
+        }
+
+        if ($fail && !isset($dep['conflicts'])) {
+            $installed = $installed ? 'installed' : 'downloaded';
+            $dep['package'] = $dep['name'];
+            $dep = $this->_registry->parsedPackageNameToString($dep, true);
+            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                return $this->raiseError('%s requires package "' . $depname . '"' .
+                    $extra . ", $installed version is " . $version);
+            }
+
+            return $this->warning('warning: %s requires package "' . $depname . '"' .
+                $extra . ", $installed version is " . $version);
+        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail &&
+              isset($dep['conflicts']) && !isset($dep['exclude'])) {
+            $installed = $installed ? 'installed' : 'downloaded';
+            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra .
+                    ", $installed version is " . $version);
+            }
+
+            return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
+                $extra . ", $installed version is " . $version);
+        }
+
+        if (isset($dep['exclude'])) {
+            $installed = $installed ? 'installed' : 'downloaded';
+            foreach ($dep['exclude'] as $exclude) {
+                if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) {
+                    if (!isset($this->_options['nodeps']) &&
+                          !isset($this->_options['force'])
+                    ) {
+                        return $this->raiseError('%s is not compatible with ' .
+                            $installed . ' package "' .
+                            $depname . '" version ' .
+                            $exclude);
+                    }
+
+                    return $this->warning('warning: %s is not compatible with ' .
+                        $installed . ' package "' .
+                        $depname . '" version ' .
+                        $exclude);
+                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
+                    $installed = $installed ? 'installed' : 'downloaded';
+                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                        return $this->raiseError('%s conflicts with package "' . $depname . '"' .
+                            $extra . ", $installed version is " . $version);
+                    }
+
+                    return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
+                        $extra . ", $installed version is " . $version);
+                }
+            }
+        }
+
+        if (isset($dep['recommended'])) {
+            $installed = $installed ? 'installed' : 'downloaded';
+            if (version_compare($version, $dep['recommended'], '==')) {
+                return true;
+            }
+
+            if (!$found && $installed) {
+                $param = $this->_registry->getPackage($dep['name'], $dep['channel']);
+            }
+
+            if ($param) {
+                $found = false;
+                foreach ($params as $parent) {
+                    if ($parent->isEqual($this->_currentPackage)) {
+                        $found = true;
+                        break;
+                    }
+                }
+
+                if ($found) {
+                    if ($param->isCompatible($parent)) {
+                        return true;
+                    }
+                } else { // this is for validPackage() calls
+                    $parent = $this->_registry->getPackage($this->_currentPackage['package'],
+                        $this->_currentPackage['channel']);
+                    if ($parent !== null && $param->isCompatible($parent)) {
+                        return true;
+                    }
+                }
+            }
+
+            if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) &&
+                  !isset($this->_options['loose'])
+            ) {
+                return $this->raiseError('%s dependency package "' . $depname .
+                    '" ' . $installed . ' version ' . $version .
+                    ' is not the recommended version ' . $dep['recommended'] .
+                    ', but may be compatible, use --force to install');
+            }
+
+            return $this->warning('warning: %s dependency package "' . $depname .
+                '" ' . $installed . ' version ' . $version .
+                ' is not the recommended version ' . $dep['recommended']);
+        }
+
+        return true;
+    }
+
+    function _validatePackageInstall($dep, $required, $depv1 = false)
+    {
+        return $this->_validatePackageDownload($dep, $required, array(), $depv1);
+    }
+
+    /**
+     * Verify that uninstalling packages passed in to command line is OK.
+     *
+     * @param PEAR_Installer $dl
+     * @return PEAR_Error|true
+     */
+    function validatePackageUninstall(&$dl)
+    {
+        if (PEAR::isError($this->_dependencydb)) {
+            return $this->_dependencydb;
+        }
+
+        $params = array();
+        // construct an array of "downloaded" packages to fool the package dependency checker
+        // into using these to validate uninstalls of circular dependencies
+        $downloaded = &$dl->getUninstallPackages();
+        foreach ($downloaded as $i => $pf) {
+            if (!class_exists('PEAR_Downloader_Package')) {
+                require_once 'PEAR/Downloader/Package.php';
+            }
+            $dp = &new PEAR_Downloader_Package($dl);
+            $dp->setPackageFile($downloaded[$i]);
+            $params[$i] = &$dp;
+        }
+
+        // check cache
+        $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' .
+            strtolower($this->_currentPackage['package']);
+        if (isset($dl->___uninstall_package_cache)) {
+            $badpackages = $dl->___uninstall_package_cache;
+            if (isset($badpackages[$memyselfandI]['warnings'])) {
+                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
+                    $dl->log(0, $warning[0]);
+                }
+            }
+
+            if (isset($badpackages[$memyselfandI]['errors'])) {
+                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
+                    if (is_array($error)) {
+                        $dl->log(0, $error[0]);
+                    } else {
+                        $dl->log(0, $error->getMessage());
+                    }
+                }
+
+                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
+                    return $this->warning(
+                        'warning: %s should not be uninstalled, other installed packages depend ' .
+                        'on this package');
+                }
+
+                return $this->raiseError(
+                    '%s cannot be uninstalled, other installed packages depend on this package');
+            }
+
+            return true;
+        }
+
+        // first, list the immediate parents of each package to be uninstalled
+        $perpackagelist = array();
+        $allparents = array();
+        foreach ($params as $i => $param) {
+            $a = array(
+                'channel' => strtolower($param->getChannel()),
+                'package' => strtolower($param->getPackage())
+            );
+
+            $deps = $this->_dependencydb->getDependentPackages($a);
+            if ($deps) {
+                foreach ($deps as $d) {
+                    $pardeps = $this->_dependencydb->getDependencies($d);
+                    foreach ($pardeps as $dep) {
+                        if (strtolower($dep['dep']['channel']) == $a['channel'] &&
+                              strtolower($dep['dep']['name']) == $a['package']) {
+                            if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) {
+                                $perpackagelist[$a['channel'] . '/' . $a['package']] = array();
+                            }
+                            $perpackagelist[$a['channel'] . '/' . $a['package']][]
+                                = array($d['channel'] . '/' . $d['package'], $dep);
+                            if (!isset($allparents[$d['channel'] . '/' . $d['package']])) {
+                                $allparents[$d['channel'] . '/' . $d['package']] = array();
+                            }
+                            if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) {
+                                $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array();
+                            }
+                            $allparents[$d['channel'] . '/' . $d['package']]
+                                       [$a['channel'] . '/' . $a['package']][]
+                                = array($d, $dep);
+                        }
+                    }
+                }
+            }
+        }
+
+        // next, remove any packages from the parents list that are not installed
+        $remove = array();
+        foreach ($allparents as $parent => $d1) {
+            foreach ($d1 as $d) {
+                if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) {
+                    continue;
+                }
+                $remove[$parent] = true;
+            }
+        }
+
+        // next remove any packages from the parents list that are not passed in for
+        // uninstallation
+        foreach ($allparents as $parent => $d1) {
+            foreach ($d1 as $d) {
+                foreach ($params as $param) {
+                    if (strtolower($param->getChannel()) == $d[0][0]['channel'] &&
+                          strtolower($param->getPackage()) == $d[0][0]['package']) {
+                        // found it
+                        continue 3;
+                    }
+                }
+                $remove[$parent] = true;
+            }
+        }
+
+        // remove all packages whose dependencies fail
+        // save which ones failed for error reporting
+        $badchildren = array();
+        do {
+            $fail = false;
+            foreach ($remove as $package => $unused) {
+                if (!isset($allparents[$package])) {
+                    continue;
+                }
+
+                foreach ($allparents[$package] as $kid => $d1) {
+                    foreach ($d1 as $depinfo) {
+                        if ($depinfo[1]['type'] != 'optional') {
+                            if (isset($badchildren[$kid])) {
+                                continue;
+                            }
+                            $badchildren[$kid] = true;
+                            $remove[$kid] = true;
+                            $fail = true;
+                            continue 2;
+                        }
+                    }
+                }
+                if ($fail) {
+                    // start over, we removed some children
+                    continue 2;
+                }
+            }
+        } while ($fail);
+
+        // next, construct the list of packages that can't be uninstalled
+        $badpackages = array();
+        $save = $this->_currentPackage;
+        foreach ($perpackagelist as $package => $packagedeps) {
+            foreach ($packagedeps as $parent) {
+                if (!isset($remove[$parent[0]])) {
+                    continue;
+                }
+
+                $packagename = $this->_registry->parsePackageName($parent[0]);
+                $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']);
+                $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']);
+                $packagename['package'] = $pa->getPackage();
+                $this->_currentPackage = $packagename;
+                // parent is not present in uninstall list, make sure we can actually
+                // uninstall it (parent dep is optional)
+                $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']);
+                $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']);
+                $parentname['package'] = $pa->getPackage();
+                $parent[1]['dep']['package'] = $parentname['package'];
+                $parent[1]['dep']['channel'] = $parentname['channel'];
+                if ($parent[1]['type'] == 'optional') {
+                    $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl);
+                    if ($test !== true) {
+                        $badpackages[$package]['warnings'][] = $test;
+                    }
+                } else {
+                    $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl);
+                    if ($test !== true) {
+                        $badpackages[$package]['errors'][] = $test;
+                    }
+                }
+            }
+        }
+
+        $this->_currentPackage          = $save;
+        $dl->___uninstall_package_cache = $badpackages;
+        if (isset($badpackages[$memyselfandI])) {
+            if (isset($badpackages[$memyselfandI]['warnings'])) {
+                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
+                    $dl->log(0, $warning[0]);
+                }
+            }
+
+            if (isset($badpackages[$memyselfandI]['errors'])) {
+                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
+                    if (is_array($error)) {
+                        $dl->log(0, $error[0]);
+                    } else {
+                        $dl->log(0, $error->getMessage());
+                    }
+                }
+
+                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
+                    return $this->warning(
+                        'warning: %s should not be uninstalled, other installed packages depend ' .
+                        'on this package');
+                }
+
+                return $this->raiseError(
+                    '%s cannot be uninstalled, other installed packages depend on this package');
+            }
+        }
+
+        return true;
+    }
+
+    function _validatePackageUninstall($dep, $required, $dl)
+    {
+        $depname = $this->_registry->parsedPackageNameToString($dep, true);
+        $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']);
+        if (!$version) {
+            return true;
+        }
+
+        $extra = $this->_getExtraString($dep);
+        if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
+            $dep['exclude'] = array($dep['exclude']);
+        }
+
+        if (isset($dep['conflicts'])) {
+            return true; // uninstall OK - these packages conflict (probably installed with --force)
+        }
+
+        if (!isset($dep['min']) && !isset($dep['max'])) {
+            if (!$required) {
+                return $this->warning('"' . $depname . '" can be optionally used by ' .
+                        'installed package %s' . $extra);
+            }
+
+            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+                return $this->raiseError('"' . $depname . '" is required by ' .
+                    'installed package %s' . $extra);
+            }
+
+            return $this->warning('warning: "' . $depname . '" is required by ' .
+                'installed package %s' . $extra);
+        }
+
+        $fail = false;
+        if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) {
+            $fail = true;
+        }
+
+        if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) {
+            $fail = true;
+        }
+
+        // we re-use this variable, preserve the original value
+        $saverequired = $required;
+        if (!$required) {
+            return $this->warning($depname . $extra . ' can be optionally used by installed package' .
+                    ' "%s"');
+        }
+
+        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
+            return $this->raiseError($depname . $extra . ' is required by installed package' .
+                ' "%s"');
+        }
+
+        return $this->raiseError('warning: ' . $depname . $extra .
+            ' is required by installed package "%s"');
+    }
+
+    /**
+     * validate a downloaded package against installed packages
+     *
+     * As of PEAR 1.4.3, this will only validate
+     *
+     * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     *              $pkg package identifier (either
+     *                   array('package' => blah, 'channel' => blah) or an array with
+     *                   index 'info' referencing an object)
+     * @param PEAR_Downloader $dl
+     * @param array $params full list of packages to install
+     * @return true|PEAR_Error
+     */
+    function validatePackage($pkg, &$dl, $params = array())
+    {
+        if (is_array($pkg) && isset($pkg['info'])) {
+            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']);
+        } else {
+            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg);
+        }
+
+        $fail = false;
+        if ($deps) {
+            if (!class_exists('PEAR_Downloader_Package')) {
+                require_once 'PEAR/Downloader/Package.php';
+            }
+
+            $dp = &new PEAR_Downloader_Package($dl);
+            if (is_object($pkg)) {
+                $dp->setPackageFile($pkg);
+            } else {
+                $dp->setDownloadURL($pkg);
+            }
+
+            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+            foreach ($deps as $channel => $info) {
+                foreach ($info as $package => $ds) {
+                    foreach ($params as $packd) {
+                        if (strtolower($packd->getPackage()) == strtolower($package) &&
+                              $packd->getChannel() == $channel) {
+                            $dl->log(3, 'skipping installed package check of "' .
+                                        $this->_registry->parsedPackageNameToString(
+                                            array('channel' => $channel, 'package' => $package),
+                                            true) .
+                                        '", version "' . $packd->getVersion() . '" will be ' .
+                                        'downloaded and installed');
+                            continue 2; // jump to next package
+                        }
+                    }
+
+                    foreach ($ds as $d) {
+                        $checker = &new PEAR_Dependency2($this->_config, $this->_options,
+                            array('channel' => $channel, 'package' => $package), $this->_state);
+                        $dep = $d['dep'];
+                        $required = $d['type'] == 'required';
+                        $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp));
+                        if (is_array($ret)) {
+                            $dl->log(0, $ret[0]);
+                        } elseif (PEAR::isError($ret)) {
+                            $dl->log(0, $ret->getMessage());
+                            $fail = true;
+                        }
+                    }
+                }
+            }
+            PEAR::popErrorHandling();
+        }
+
+        if ($fail) {
+            return $this->raiseError(
+                '%s cannot be installed, conflicts with installed packages');
+        }
+
+        return true;
+    }
+
+    /**
+     * validate a package.xml 1.0 dependency
+     */
+    function validateDependency1($dep, $params = array())
+    {
+        if (!isset($dep['optional'])) {
+            $dep['optional'] = 'no';
+        }
+
+        list($newdep, $type) = $this->normalizeDep($dep);
+        if (!$newdep) {
+            return $this->raiseError("Invalid Dependency");
+        }
+
+        if (method_exists($this, "validate{$type}Dependency")) {
+            return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no',
+                $params, true);
+        }
+    }
+
+    /**
+     * Convert a 1.0 dep into a 2.0 dep
+     */
+    function normalizeDep($dep)
+    {
+        $types = array(
+            'pkg' => 'Package',
+            'ext' => 'Extension',
+            'os' => 'Os',
+            'php' => 'Php'
+        );
+
+        if (!isset($types[$dep['type']])) {
+            return array(false, false);
+        }
+
+        $type = $types[$dep['type']];
+
+        $newdep = array();
+        switch ($type) {
+            case 'Package' :
+                $newdep['channel'] = 'pear.php.net';
+            case 'Extension' :
+            case 'Os' :
+                $newdep['name'] = $dep['name'];
+            break;
+        }
+
+        $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']);
+        switch ($dep['rel']) {
+            case 'has' :
+                return array($newdep, $type);
+            break;
+            case 'not' :
+                $newdep['conflicts'] = true;
+            break;
+            case '>=' :
+            case '>' :
+                $newdep['min'] = $dep['version'];
+                if ($dep['rel'] == '>') {
+                    $newdep['exclude'] = $dep['version'];
+                }
+            break;
+            case '<=' :
+            case '<' :
+                $newdep['max'] = $dep['version'];
+                if ($dep['rel'] == '<') {
+                    $newdep['exclude'] = $dep['version'];
+                }
+            break;
+            case 'ne' :
+            case '!=' :
+                $newdep['min'] = '0';
+                $newdep['max'] = '100000';
+                $newdep['exclude'] = $dep['version'];
+            break;
+            case '==' :
+                $newdep['min'] = $dep['version'];
+                $newdep['max'] = $dep['version'];
+            break;
+        }
+        if ($type == 'Php') {
+            if (!isset($newdep['min'])) {
+                $newdep['min'] = '4.4.0';
+            }
+
+            if (!isset($newdep['max'])) {
+                $newdep['max'] = '6.0.0';
+            }
+        }
+        return array($newdep, $type);
+    }
+
+    /**
+     * Converts text comparing operators to them sign equivalents
+     *
+     * Example: 'ge' to '>='
+     *
+     * @access public
+     * @param  string Operator
+     * @return string Sign equivalent
+     */
+    function signOperator($operator)
+    {
+        switch($operator) {
+            case 'lt': return '<';
+            case 'le': return '<=';
+            case 'gt': return '>';
+            case 'ge': return '>=';
+            case 'eq': return '==';
+            case 'ne': return '!=';
+            default:
+                return $operator;
+        }
+    }
+
+    function raiseError($msg)
+    {
+        if (isset($this->_options['ignore-errors'])) {
+            return $this->warning($msg);
+        }
+
+        return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString(
+            $this->_currentPackage, true)));
+    }
+
+    function warning($msg)
+    {
+        return array(sprintf($msg, $this->_registry->parsedPackageNameToString(
+            $this->_currentPackage, true)));
+    }
+}
Index: trunk/library/PEAR/PEAR/RunTest.php
===================================================================
--- trunk/library/PEAR/PEAR/RunTest.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/RunTest.php	(revision 5146)
@@ -0,0 +1,968 @@
+<?php
+/**
+ * PEAR_RunTest
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: RunTest.php 313024 2011-07-06 19:51:24Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.3.3
+ */
+
+/**
+ * for error handling
+ */
+require_once 'PEAR.php';
+require_once 'PEAR/Config.php';
+
+define('DETAILED', 1);
+putenv("PHP_PEAR_RUNTESTS=1");
+
+/**
+ * Simplified version of PHP's test suite
+ *
+ * Try it with:
+ *
+ * $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);'
+ *
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.3.3
+ */
+class PEAR_RunTest
+{
+    var $_headers = array();
+    var $_logger;
+    var $_options;
+    var $_php;
+    var $tests_count;
+    var $xdebug_loaded;
+    /**
+     * Saved value of php executable, used to reset $_php when we
+     * have a test that uses cgi
+     *
+     * @var unknown_type
+     */
+    var $_savephp;
+    var $ini_overwrites = array(
+        'output_handler=',
+        'open_basedir=',
+        'safe_mode=0',
+        'disable_functions=',
+        'output_buffering=Off',
+        'display_errors=1',
+        'log_errors=0',
+        'html_errors=0',
+        'track_errors=1',
+        'report_memleaks=0',
+        'report_zend_debug=0',
+        'docref_root=',
+        'docref_ext=.html',
+        'error_prepend_string=',
+        'error_append_string=',
+        'auto_prepend_file=',
+        'auto_append_file=',
+        'magic_quotes_runtime=0',
+        'xdebug.default_enable=0',
+        'allow_url_fopen=1',
+    );
+
+    /**
+     * An object that supports the PEAR_Common->log() signature, or null
+     * @param PEAR_Common|null
+     */
+    function PEAR_RunTest($logger = null, $options = array())
+    {
+        if (!defined('E_DEPRECATED')) {
+            define('E_DEPRECATED', 0);
+        }
+        if (!defined('E_STRICT')) {
+            define('E_STRICT', 0);
+        }
+        $this->ini_overwrites[] = 'error_reporting=' . (E_ALL & ~(E_DEPRECATED | E_STRICT));
+        if (is_null($logger)) {
+            require_once 'PEAR/Common.php';
+            $logger = new PEAR_Common;
+        }
+        $this->_logger  = $logger;
+        $this->_options = $options;
+
+        $conf = &PEAR_Config::singleton();
+        $this->_php = $conf->get('php_bin');
+    }
+
+    /**
+     * Taken from php-src/run-tests.php
+     *
+     * @param string $commandline command name
+     * @param array $env
+     * @param string $stdin standard input to pass to the command
+     * @return unknown
+     */
+    function system_with_timeout($commandline, $env = null, $stdin = null)
+    {
+        $data = '';
+        if (version_compare(phpversion(), '5.0.0', '<')) {
+            $proc = proc_open($commandline, array(
+                0 => array('pipe', 'r'),
+                1 => array('pipe', 'w'),
+                2 => array('pipe', 'w')
+                ), $pipes);
+        } else {
+            $proc = proc_open($commandline, array(
+                0 => array('pipe', 'r'),
+                1 => array('pipe', 'w'),
+                2 => array('pipe', 'w')
+                ), $pipes, null, $env, array('suppress_errors' => true));
+        }
+
+        if (!$proc) {
+            return false;
+        }
+
+        if (is_string($stdin)) {
+            fwrite($pipes[0], $stdin);
+        }
+        fclose($pipes[0]);
+
+        while (true) {
+            /* hide errors from interrupted syscalls */
+            $r = $pipes;
+            $e = $w = null;
+            $n = @stream_select($r, $w, $e, 60);
+
+            if ($n === 0) {
+                /* timed out */
+                $data .= "\n ** ERROR: process timed out **\n";
+                proc_terminate($proc);
+                return array(1234567890, $data);
+            } else if ($n > 0) {
+                $line = fread($pipes[1], 8192);
+                if (strlen($line) == 0) {
+                    /* EOF */
+                    break;
+                }
+                $data .= $line;
+            }
+        }
+        if (function_exists('proc_get_status')) {
+            $stat = proc_get_status($proc);
+            if ($stat['signaled']) {
+                $data .= "\nTermsig=".$stat['stopsig'];
+            }
+        }
+        $code = proc_close($proc);
+        if (function_exists('proc_get_status')) {
+            $code = $stat['exitcode'];
+        }
+        return array($code, $data);
+    }
+
+    /**
+     * Turns a PHP INI string into an array
+     *
+     * Turns -d "include_path=/foo/bar" into this:
+     * array(
+     *   'include_path' => array(
+     *          'operator' => '-d',
+     *          'value'    => '/foo/bar',
+     *   )
+     * )
+     * Works both with quotes and without
+     *
+     * @param string an PHP INI string, -d "include_path=/foo/bar"
+     * @return array
+     */
+    function iniString2array($ini_string)
+    {
+        if (!$ini_string) {
+            return array();
+        }
+        $split = preg_split('/[\s]|=/', $ini_string, -1, PREG_SPLIT_NO_EMPTY);
+        $key   = $split[1][0] == '"'                     ? substr($split[1], 1)     : $split[1];
+        $value = $split[2][strlen($split[2]) - 1] == '"' ? substr($split[2], 0, -1) : $split[2];
+        // FIXME review if this is really the struct to go with
+        $array = array($key => array('operator' => $split[0], 'value' => $value));
+        return $array;
+    }
+
+    function settings2array($settings, $ini_settings)
+    {
+        foreach ($settings as $setting) {
+            if (strpos($setting, '=') !== false) {
+                $setting = explode('=', $setting, 2);
+                $name  = trim(strtolower($setting[0]));
+                $value = trim($setting[1]);
+                $ini_settings[$name] = $value;
+            }
+        }
+        return $ini_settings;
+    }
+
+    function settings2params($ini_settings)
+    {
+        $settings = '';
+        foreach ($ini_settings as $name => $value) {
+            if (is_array($value)) {
+                $operator = $value['operator'];
+                $value    = $value['value'];
+            } else {
+                $operator = '-d';
+            }
+            $value = addslashes($value);
+            $settings .= " $operator \"$name=$value\"";
+        }
+        return $settings;
+    }
+
+    function _preparePhpBin($php, $file, $ini_settings)
+    {
+        $file = escapeshellarg($file);
+        // This was fixed in php 5.3 and is not needed after that
+        if (OS_WINDOWS && version_compare(PHP_VERSION, '5.3', '<')) {
+            $cmd = '"'.escapeshellarg($php).' '.$ini_settings.' -f ' . $file .'"';
+        } else {
+            $cmd = $php . $ini_settings . ' -f ' . $file;
+        }
+
+        return $cmd;
+    }
+
+    function runPHPUnit($file, $ini_settings = '')
+    {
+        if (!file_exists($file) && file_exists(getcwd() . DIRECTORY_SEPARATOR . $file)) {
+            $file = realpath(getcwd() . DIRECTORY_SEPARATOR . $file);
+        } elseif (file_exists($file)) {
+            $file = realpath($file);
+        }
+
+        $cmd = $this->_preparePhpBin($this->_php, $file, $ini_settings);
+        if (isset($this->_logger)) {
+            $this->_logger->log(2, 'Running command "' . $cmd . '"');
+        }
+
+        $savedir = getcwd(); // in case the test moves us around
+        chdir(dirname($file));
+        echo `$cmd`;
+        chdir($savedir);
+        return 'PASSED'; // we have no way of knowing this information so assume passing
+    }
+
+    /**
+     * Runs an individual test case.
+     *
+     * @param string       The filename of the test
+     * @param array|string INI settings to be applied to the test run
+     * @param integer      Number what the current running test is of the
+     *                     whole test suite being runned.
+     *
+     * @return string|object Returns PASSED, WARNED, FAILED depending on how the
+     *                       test came out.
+     *                       PEAR Error when the tester it self fails
+     */
+    function run($file, $ini_settings = array(), $test_number = 1)
+    {
+        if (isset($this->_savephp)) {
+            $this->_php = $this->_savephp;
+            unset($this->_savephp);
+        }
+        if (empty($this->_options['cgi'])) {
+            // try to see if php-cgi is in the path
+            $res = $this->system_with_timeout('php-cgi -v');
+            if (false !== $res && !(is_array($res) && in_array($res[0], array(-1, 127)))) {
+                $this->_options['cgi'] = 'php-cgi';
+            }
+        }
+        if (1 < $len = strlen($this->tests_count)) {
+            $test_number = str_pad($test_number, $len, ' ', STR_PAD_LEFT);
+            $test_nr = "[$test_number/$this->tests_count] ";
+        } else {
+            $test_nr = '';
+        }
+
+        $file = realpath($file);
+        $section_text = $this->_readFile($file);
+        if (PEAR::isError($section_text)) {
+            return $section_text;
+        }
+
+        if (isset($section_text['POST_RAW']) && isset($section_text['UPLOAD'])) {
+            return PEAR::raiseError("Cannot contain both POST_RAW and UPLOAD in test file: $file");
+        }
+
+        $cwd = getcwd();
+
+        $pass_options = '';
+        if (!empty($this->_options['ini'])) {
+            $pass_options = $this->_options['ini'];
+        }
+
+        if (is_string($ini_settings)) {
+            $ini_settings = $this->iniString2array($ini_settings);
+        }
+
+        $ini_settings = $this->settings2array($this->ini_overwrites, $ini_settings);
+        if ($section_text['INI']) {
+            if (strpos($section_text['INI'], '{PWD}') !== false) {
+                $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']);
+            }
+            $ini = preg_split( "/[\n\r]+/", $section_text['INI']);
+            $ini_settings = $this->settings2array($ini, $ini_settings);
+        }
+        $ini_settings = $this->settings2params($ini_settings);
+        $shortname = str_replace($cwd . DIRECTORY_SEPARATOR, '', $file);
+
+        $tested = trim($section_text['TEST']);
+        $tested.= !isset($this->_options['simple']) ? "[$shortname]" : ' ';
+
+        if (!empty($section_text['POST']) || !empty($section_text['POST_RAW']) ||
+              !empty($section_text['UPLOAD']) || !empty($section_text['GET']) ||
+              !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) {
+            if (empty($this->_options['cgi'])) {
+                if (!isset($this->_options['quiet'])) {
+                    $this->_logger->log(0, "SKIP $test_nr$tested (reason: --cgi option needed for this test, type 'pear help run-tests')");
+                }
+                if (isset($this->_options['tapoutput'])) {
+                    return array('ok', ' # skip --cgi option needed for this test, "pear help run-tests" for info');
+                }
+                return 'SKIPPED';
+            }
+            $this->_savephp = $this->_php;
+            $this->_php = $this->_options['cgi'];
+        }
+
+        $temp_dir = realpath(dirname($file));
+        $main_file_name = basename($file, 'phpt');
+        $diff_filename     = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff';
+        $log_filename      = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log';
+        $exp_filename      = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp';
+        $output_filename   = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out';
+        $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem';
+        $temp_file         = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php';
+        $temp_skipif       = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php';
+        $temp_clean        = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php';
+        $tmp_post          = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');
+
+        // unlink old test results
+        $this->_cleanupOldFiles($file);
+
+        // Check if test should be skipped.
+        $res  = $this->_runSkipIf($section_text, $temp_skipif, $tested, $ini_settings);
+        if (count($res) != 2) {
+            return $res;
+        }
+        $info = $res['info'];
+        $warn = $res['warn'];
+
+        // We've satisfied the preconditions - run the test!
+        if (isset($this->_options['coverage']) && $this->xdebug_loaded) {
+            $xdebug_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'xdebug';
+            $text = "\n" . 'function coverage_shutdown() {' .
+                    "\n" . '    $xdebug = var_export(xdebug_get_code_coverage(), true);';
+            if (!function_exists('file_put_contents')) {
+                $text .= "\n" . '    $fh = fopen(\'' . $xdebug_file . '\', "wb");' .
+                        "\n" . '    if ($fh !== false) {' .
+                        "\n" . '        fwrite($fh, $xdebug);' .
+                        "\n" . '        fclose($fh);' .
+                        "\n" . '    }';
+            } else {
+                $text .= "\n" . '    file_put_contents(\'' . $xdebug_file . '\', $xdebug);';
+            }
+
+            // Workaround for http://pear.php.net/bugs/bug.php?id=17292
+            $lines             = explode("\n", $section_text['FILE']);
+            $numLines          = count($lines);
+            $namespace         = '';
+            $coverage_shutdown = 'coverage_shutdown';
+
+            if (
+                substr($lines[0], 0, 2) == '<?' ||
+                substr($lines[0], 0, 5) == '<?php'
+            ) {
+                unset($lines[0]);
+            }
+
+
+            for ($i = 0; $i < $numLines; $i++) {
+                if (isset($lines[$i]) && substr($lines[$i], 0, 9) == 'namespace') {
+                    $namespace         = substr($lines[$i], 10, -1);
+                    $coverage_shutdown = $namespace . '\\coverage_shutdown';
+                    $namespace         = "namespace " . $namespace . ";\n";
+
+                    unset($lines[$i]);
+                    break;
+                }
+            }
+
+            $text .= "\n    xdebug_stop_code_coverage();" .
+                "\n" . '} // end coverage_shutdown()' .
+                "\n\n" . 'register_shutdown_function("' . $coverage_shutdown . '");';
+            $text .= "\n" . 'xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);' . "\n";
+
+            $this->save_text($temp_file, "<?php\n" . $namespace . $text  . "\n" . implode("\n", $lines));
+        } else {
+            $this->save_text($temp_file, $section_text['FILE']);
+        }
+
+        $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : '';
+        $cmd = $this->_preparePhpBin($this->_php, $temp_file, $ini_settings);
+        $cmd.= "$args 2>&1";
+        if (isset($this->_logger)) {
+            $this->_logger->log(2, 'Running command "' . $cmd . '"');
+        }
+
+        // Reset environment from any previous test.
+        $env = $this->_resetEnv($section_text, $temp_file);
+
+        $section_text = $this->_processUpload($section_text, $file);
+        if (PEAR::isError($section_text)) {
+            return $section_text;
+        }
+
+        if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) {
+            $post = trim($section_text['POST_RAW']);
+            $raw_lines = explode("\n", $post);
+
+            $request = '';
+            $started = false;
+            foreach ($raw_lines as $i => $line) {
+                if (empty($env['CONTENT_TYPE']) &&
+                    preg_match('/^Content-Type:(.*)/i', $line, $res)) {
+                    $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1]));
+                    continue;
+                }
+                if ($started) {
+                    $request .= "\n";
+                }
+                $started = true;
+                $request .= $line;
+            }
+
+            $env['CONTENT_LENGTH'] = strlen($request);
+            $env['REQUEST_METHOD'] = 'POST';
+
+            $this->save_text($tmp_post, $request);
+            $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
+        } elseif (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {
+            $post = trim($section_text['POST']);
+            $this->save_text($tmp_post, $post);
+            $content_length = strlen($post);
+
+            $env['REQUEST_METHOD'] = 'POST';
+            $env['CONTENT_TYPE']   = 'application/x-www-form-urlencoded';
+            $env['CONTENT_LENGTH'] = $content_length;
+
+            $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
+        } else {
+            $env['REQUEST_METHOD'] = 'GET';
+            $env['CONTENT_TYPE']   = '';
+            $env['CONTENT_LENGTH'] = '';
+        }
+
+        if (OS_WINDOWS && isset($section_text['RETURNS'])) {
+            ob_start();
+            system($cmd, $return_value);
+            $out = ob_get_contents();
+            ob_end_clean();
+            $section_text['RETURNS'] = (int) trim($section_text['RETURNS']);
+            $returnfail = ($return_value != $section_text['RETURNS']);
+        } else {
+            $returnfail = false;
+            $stdin = isset($section_text['STDIN']) ? $section_text['STDIN'] : null;
+            $out = $this->system_with_timeout($cmd, $env, $stdin);
+            $return_value = $out[0];
+            $out = $out[1];
+        }
+
+        $output = preg_replace('/\r\n/', "\n", trim($out));
+
+        if (isset($tmp_post) && realpath($tmp_post) && file_exists($tmp_post)) {
+            @unlink(realpath($tmp_post));
+        }
+        chdir($cwd); // in case the test moves us around
+
+        $this->_testCleanup($section_text, $temp_clean);
+
+        /* when using CGI, strip the headers from the output */
+        $output = $this->_stripHeadersCGI($output);
+
+        if (isset($section_text['EXPECTHEADERS'])) {
+            $testheaders = $this->_processHeaders($section_text['EXPECTHEADERS']);
+            $missing = array_diff_assoc($testheaders, $this->_headers);
+            $changed = '';
+            foreach ($missing as $header => $value) {
+                if (isset($this->_headers[$header])) {
+                    $changed .= "-$header: $value\n+$header: ";
+                    $changed .= $this->_headers[$header];
+                } else {
+                    $changed .= "-$header: $value\n";
+                }
+            }
+            if ($missing) {
+                // tack on failed headers to output:
+                $output .= "\n====EXPECTHEADERS FAILURE====:\n$changed";
+            }
+        }
+        // Does the output match what is expected?
+        do {
+            if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) {
+                if (isset($section_text['EXPECTF'])) {
+                    $wanted = trim($section_text['EXPECTF']);
+                } else {
+                    $wanted = trim($section_text['EXPECTREGEX']);
+                }
+                $wanted_re = preg_replace('/\r\n/', "\n", $wanted);
+                if (isset($section_text['EXPECTF'])) {
+                    $wanted_re = preg_quote($wanted_re, '/');
+                    // Stick to basics
+                    $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy
+                    $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re);
+                    $wanted_re = str_replace("%d", "[0-9]+", $wanted_re);
+                    $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re);
+                    $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re);
+                    $wanted_re = str_replace("%c", ".", $wanted_re);
+                    // %f allows two points "-.0.0" but that is the best *simple* expression
+                }
+
+    /* DEBUG YOUR REGEX HERE
+            var_dump($wanted_re);
+            print(str_repeat('=', 80) . "\n");
+            var_dump($output);
+    */
+                if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) {
+                    if (file_exists($temp_file)) {
+                        unlink($temp_file);
+                    }
+                    if (array_key_exists('FAIL', $section_text)) {
+                        break;
+                    }
+                    if (!isset($this->_options['quiet'])) {
+                        $this->_logger->log(0, "PASS $test_nr$tested$info");
+                    }
+                    if (isset($this->_options['tapoutput'])) {
+                        return array('ok', ' - ' . $tested);
+                    }
+                    return 'PASSED';
+                }
+            } else {
+                if (isset($section_text['EXPECTFILE'])) {
+                    $f = $temp_dir . '/' . trim($section_text['EXPECTFILE']);
+                    if (!($fp = @fopen($f, 'rb'))) {
+                        return PEAR::raiseError('--EXPECTFILE-- section file ' .
+                            $f . ' not found');
+                    }
+                    fclose($fp);
+                    $section_text['EXPECT'] = file_get_contents($f);
+                }
+
+                if (isset($section_text['EXPECT'])) {
+                    $wanted = preg_replace('/\r\n/', "\n", trim($section_text['EXPECT']));
+                } else {
+                    $wanted = '';
+                }
+
+                // compare and leave on success
+                if (!$returnfail && 0 == strcmp($output, $wanted)) {
+                    if (file_exists($temp_file)) {
+                        unlink($temp_file);
+                    }
+                    if (array_key_exists('FAIL', $section_text)) {
+                        break;
+                    }
+                    if (!isset($this->_options['quiet'])) {
+                        $this->_logger->log(0, "PASS $test_nr$tested$info");
+                    }
+                    if (isset($this->_options['tapoutput'])) {
+                        return array('ok', ' - ' . $tested);
+                    }
+                    return 'PASSED';
+                }
+            }
+        } while (false);
+
+        if (array_key_exists('FAIL', $section_text)) {
+            // we expect a particular failure
+            // this is only used for testing PEAR_RunTest
+            $expectf  = isset($section_text['EXPECTF']) ? $wanted_re : null;
+            $faildiff = $this->generate_diff($wanted, $output, null, $expectf);
+            $faildiff = preg_replace('/\r/', '', $faildiff);
+            $wanted   = preg_replace('/\r/', '', trim($section_text['FAIL']));
+            if ($faildiff == $wanted) {
+                if (!isset($this->_options['quiet'])) {
+                    $this->_logger->log(0, "PASS $test_nr$tested$info");
+                }
+                if (isset($this->_options['tapoutput'])) {
+                    return array('ok', ' - ' . $tested);
+                }
+                return 'PASSED';
+            }
+            unset($section_text['EXPECTF']);
+            $output = $faildiff;
+            if (isset($section_text['RETURNS'])) {
+                return PEAR::raiseError('Cannot have both RETURNS and FAIL in the same test: ' .
+                    $file);
+            }
+        }
+
+        // Test failed so we need to report details.
+        $txt = $warn ? 'WARN ' : 'FAIL ';
+        $this->_logger->log(0, $txt . $test_nr . $tested . $info);
+
+        // write .exp
+        $res = $this->_writeLog($exp_filename, $wanted);
+        if (PEAR::isError($res)) {
+            return $res;
+        }
+
+        // write .out
+        $res = $this->_writeLog($output_filename, $output);
+        if (PEAR::isError($res)) {
+            return $res;
+        }
+
+        // write .diff
+        $returns = isset($section_text['RETURNS']) ?
+                        array(trim($section_text['RETURNS']), $return_value) : null;
+        $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null;
+        $data = $this->generate_diff($wanted, $output, $returns, $expectf);
+        $res  = $this->_writeLog($diff_filename, $data);
+        if (PEAR::isError($res)) {
+            return $res;
+        }
+
+        // write .log
+        $data = "
+---- EXPECTED OUTPUT
+$wanted
+---- ACTUAL OUTPUT
+$output
+---- FAILED
+";
+
+        if ($returnfail) {
+            $data .= "
+---- EXPECTED RETURN
+$section_text[RETURNS]
+---- ACTUAL RETURN
+$return_value
+";
+        }
+
+        $res = $this->_writeLog($log_filename, $data);
+        if (PEAR::isError($res)) {
+            return $res;
+        }
+
+        if (isset($this->_options['tapoutput'])) {
+            $wanted = explode("\n", $wanted);
+            $wanted = "# Expected output:\n#\n#" . implode("\n#", $wanted);
+            $output = explode("\n", $output);
+            $output = "#\n#\n# Actual output:\n#\n#" . implode("\n#", $output);
+            return array($wanted . $output . 'not ok', ' - ' . $tested);
+        }
+        return $warn ? 'WARNED' : 'FAILED';
+    }
+
+    function generate_diff($wanted, $output, $rvalue, $wanted_re)
+    {
+        $w  = explode("\n", $wanted);
+        $o  = explode("\n", $output);
+        $wr = explode("\n", $wanted_re);
+        $w1 = array_diff_assoc($w, $o);
+        $o1 = array_diff_assoc($o, $w);
+        $o2 = $w2 = array();
+        foreach ($w1 as $idx => $val) {
+            if (!$wanted_re || !isset($wr[$idx]) || !isset($o1[$idx]) ||
+                  !preg_match('/^' . $wr[$idx] . '\\z/', $o1[$idx])) {
+                $w2[sprintf("%03d<", $idx)] = sprintf("%03d- ", $idx + 1) . $val;
+            }
+        }
+        foreach ($o1 as $idx => $val) {
+            if (!$wanted_re || !isset($wr[$idx]) ||
+                  !preg_match('/^' . $wr[$idx] . '\\z/', $val)) {
+                $o2[sprintf("%03d>", $idx)] = sprintf("%03d+ ", $idx + 1) . $val;
+            }
+        }
+        $diff = array_merge($w2, $o2);
+        ksort($diff);
+        $extra = $rvalue ? "##EXPECTED: $rvalue[0]\r\n##RETURNED: $rvalue[1]" : '';
+        return implode("\r\n", $diff) . $extra;
+    }
+
+    //  Write the given text to a temporary file, and return the filename.
+    function save_text($filename, $text)
+    {
+        if (!$fp = fopen($filename, 'w')) {
+            return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)");
+        }
+        fwrite($fp, $text);
+        fclose($fp);
+    if (1 < DETAILED) echo "
+FILE $filename {{{
+$text
+}}}
+";
+    }
+
+    function _cleanupOldFiles($file)
+    {
+        $temp_dir = realpath(dirname($file));
+        $mainFileName = basename($file, 'phpt');
+        $diff_filename     = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff';
+        $log_filename      = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log';
+        $exp_filename      = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp';
+        $output_filename   = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out';
+        $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem';
+        $temp_file         = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php';
+        $temp_skipif       = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php';
+        $temp_clean        = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php';
+        $tmp_post          = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');
+
+        // unlink old test results
+        @unlink($diff_filename);
+        @unlink($log_filename);
+        @unlink($exp_filename);
+        @unlink($output_filename);
+        @unlink($memcheck_filename);
+        @unlink($temp_file);
+        @unlink($temp_skipif);
+        @unlink($tmp_post);
+        @unlink($temp_clean);
+    }
+
+    function _runSkipIf($section_text, $temp_skipif, $tested, $ini_settings)
+    {
+        $info = '';
+        $warn = false;
+        if (array_key_exists('SKIPIF', $section_text) && trim($section_text['SKIPIF'])) {
+            $this->save_text($temp_skipif, $section_text['SKIPIF']);
+            $output = $this->system_with_timeout("$this->_php$ini_settings -f \"$temp_skipif\"");
+            $output = $output[1];
+            $loutput = ltrim($output);
+            unlink($temp_skipif);
+            if (!strncasecmp('skip', $loutput, 4)) {
+                $skipreason = "SKIP $tested";
+                if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) {
+                    $skipreason .= '(reason: ' . $m[1] . ')';
+                }
+                if (!isset($this->_options['quiet'])) {
+                    $this->_logger->log(0, $skipreason);
+                }
+                if (isset($this->_options['tapoutput'])) {
+                    return array('ok', ' # skip ' . $reason);
+                }
+                return 'SKIPPED';
+            }
+
+            if (!strncasecmp('info', $loutput, 4)
+                && preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) {
+                $info = " (info: $m[1])";
+            }
+
+            if (!strncasecmp('warn', $loutput, 4)
+                && preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) {
+                $warn = true; /* only if there is a reason */
+                $info = " (warn: $m[1])";
+            }
+        }
+
+        return array('warn' => $warn, 'info' => $info);
+    }
+
+    function _stripHeadersCGI($output)
+    {
+        $this->headers = array();
+        if (!empty($this->_options['cgi']) &&
+              $this->_php == $this->_options['cgi'] &&
+              preg_match("/^(.*?)(?:\n\n(.*)|\\z)/s", $output, $match)) {
+            $output = isset($match[2]) ? trim($match[2]) : '';
+            $this->_headers = $this->_processHeaders($match[1]);
+        }
+
+        return $output;
+    }
+
+    /**
+     * Return an array that can be used with array_diff() to compare headers
+     *
+     * @param string $text
+     */
+    function _processHeaders($text)
+    {
+        $headers = array();
+        $rh = preg_split("/[\n\r]+/", $text);
+        foreach ($rh as $line) {
+            if (strpos($line, ':')!== false) {
+                $line = explode(':', $line, 2);
+                $headers[trim($line[0])] = trim($line[1]);
+            }
+        }
+        return $headers;
+    }
+
+    function _readFile($file)
+    {
+        // Load the sections of the test file.
+        $section_text = array(
+            'TEST'   => '(unnamed test)',
+            'SKIPIF' => '',
+            'GET'    => '',
+            'COOKIE' => '',
+            'POST'   => '',
+            'ARGS'   => '',
+            'INI'    => '',
+            'CLEAN'  => '',
+        );
+
+        if (!is_file($file) || !$fp = fopen($file, "r")) {
+            return PEAR::raiseError("Cannot open test file: $file");
+        }
+
+        $section = '';
+        while (!feof($fp)) {
+            $line = fgets($fp);
+
+            // Match the beginning of a section.
+            if (preg_match('/^--([_A-Z]+)--/', $line, $r)) {
+                $section = $r[1];
+                $section_text[$section] = '';
+                continue;
+            } elseif (empty($section)) {
+                fclose($fp);
+                return PEAR::raiseError("Invalid sections formats in test file: $file");
+            }
+
+            // Add to the section text.
+            $section_text[$section] .= $line;
+        }
+        fclose($fp);
+
+        return $section_text;
+    }
+
+    function _writeLog($logname, $data)
+    {
+        if (!$log = fopen($logname, 'w')) {
+            return PEAR::raiseError("Cannot create test log - $logname");
+        }
+        fwrite($log, $data);
+        fclose($log);
+    }
+
+    function _resetEnv($section_text, $temp_file)
+    {
+        $env = $_ENV;
+        $env['REDIRECT_STATUS'] = '';
+        $env['QUERY_STRING']    = '';
+        $env['PATH_TRANSLATED'] = '';
+        $env['SCRIPT_FILENAME'] = '';
+        $env['REQUEST_METHOD']  = '';
+        $env['CONTENT_TYPE']    = '';
+        $env['CONTENT_LENGTH']  = '';
+        if (!empty($section_text['ENV'])) {
+            if (strpos($section_text['ENV'], '{PWD}') !== false) {
+                $section_text['ENV'] = str_replace('{PWD}', dirname($temp_file), $section_text['ENV']);
+            }
+            foreach (explode("\n", trim($section_text['ENV'])) as $e) {
+                $e = explode('=', trim($e), 2);
+                if (!empty($e[0]) && isset($e[1])) {
+                    $env[$e[0]] = $e[1];
+                }
+            }
+        }
+        if (array_key_exists('GET', $section_text)) {
+            $env['QUERY_STRING'] = trim($section_text['GET']);
+        } else {
+            $env['QUERY_STRING'] = '';
+        }
+        if (array_key_exists('COOKIE', $section_text)) {
+            $env['HTTP_COOKIE'] = trim($section_text['COOKIE']);
+        } else {
+            $env['HTTP_COOKIE'] = '';
+        }
+        $env['REDIRECT_STATUS'] = '1';
+        $env['PATH_TRANSLATED'] = $temp_file;
+        $env['SCRIPT_FILENAME'] = $temp_file;
+
+        return $env;
+    }
+
+    function _processUpload($section_text, $file)
+    {
+        if (array_key_exists('UPLOAD', $section_text) && !empty($section_text['UPLOAD'])) {
+            $upload_files = trim($section_text['UPLOAD']);
+            $upload_files = explode("\n", $upload_files);
+
+            $request = "Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737\n" .
+                       "-----------------------------20896060251896012921717172737\n";
+            foreach ($upload_files as $fileinfo) {
+                $fileinfo = explode('=', $fileinfo);
+                if (count($fileinfo) != 2) {
+                    return PEAR::raiseError("Invalid UPLOAD section in test file: $file");
+                }
+                if (!realpath(dirname($file) . '/' . $fileinfo[1])) {
+                    return PEAR::raiseError("File for upload does not exist: $fileinfo[1] " .
+                        "in test file: $file");
+                }
+                $file_contents = file_get_contents(dirname($file) . '/' . $fileinfo[1]);
+                $fileinfo[1] = basename($fileinfo[1]);
+                $request .= "Content-Disposition: form-data; name=\"$fileinfo[0]\"; filename=\"$fileinfo[1]\"\n";
+                $request .= "Content-Type: text/plain\n\n";
+                $request .= $file_contents . "\n" .
+                    "-----------------------------20896060251896012921717172737\n";
+            }
+
+            if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {
+                // encode POST raw
+                $post = trim($section_text['POST']);
+                $post = explode('&', $post);
+                foreach ($post as $i => $post_info) {
+                    $post_info = explode('=', $post_info);
+                    if (count($post_info) != 2) {
+                        return PEAR::raiseError("Invalid POST data in test file: $file");
+                    }
+                    $post_info[0] = rawurldecode($post_info[0]);
+                    $post_info[1] = rawurldecode($post_info[1]);
+                    $post[$i] = $post_info;
+                }
+                foreach ($post as $post_info) {
+                    $request .= "Content-Disposition: form-data; name=\"$post_info[0]\"\n\n";
+                    $request .= $post_info[1] . "\n" .
+                        "-----------------------------20896060251896012921717172737\n";
+                }
+                unset($section_text['POST']);
+            }
+            $section_text['POST_RAW'] = $request;
+        }
+
+        return $section_text;
+    }
+
+    function _testCleanup($section_text, $temp_clean)
+    {
+        if ($section_text['CLEAN']) {
+            // perform test cleanup
+            $this->save_text($temp_clean, $section_text['CLEAN']);
+            $output = $this->system_with_timeout("$this->_php $temp_clean  2>&1");
+            if (strlen($output[1])) {
+                echo "BORKED --CLEAN-- section! output:\n", $output[1];
+            }
+            if (file_exists($temp_clean)) {
+                unlink($temp_clean);
+            }
+        }
+    }
+}
Index: trunk/library/PEAR/PEAR/FixPHP5PEARWarnings.php
===================================================================
--- trunk/library/PEAR/PEAR/FixPHP5PEARWarnings.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/FixPHP5PEARWarnings.php	(revision 5146)
@@ -0,0 +1,7 @@
+<?php
+if ($skipmsg) {
+    $a = &new $ec($code, $mode, $options, $userinfo);
+} else {
+    $a = &new $ec($message, $code, $mode, $options, $userinfo);
+}
+?>
Index: trunk/library/PEAR/PEAR/PackageFile/Generator/v1.php
===================================================================
--- trunk/library/PEAR/PEAR/PackageFile/Generator/v1.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/PackageFile/Generator/v1.php	(revision 5146)
@@ -0,0 +1,1284 @@
+<?php
+/**
+ * package.xml generation class, package.xml version 1.0
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * needed for PEAR_VALIDATE_* constants
+ */
+require_once 'PEAR/Validate.php';
+require_once 'System.php';
+require_once 'PEAR/PackageFile/v2.php';
+/**
+ * This class converts a PEAR_PackageFile_v1 object into any output format.
+ *
+ * Supported output formats include array, XML string, and a PEAR_PackageFile_v2
+ * object, for converting package.xml 1.0 into package.xml 2.0 with no sweat.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_PackageFile_Generator_v1
+{
+    /**
+     * @var PEAR_PackageFile_v1
+     */
+    var $_packagefile;
+    function PEAR_PackageFile_Generator_v1(&$packagefile)
+    {
+        $this->_packagefile = &$packagefile;
+    }
+
+    function getPackagerVersion()
+    {
+        return '1.9.4';
+    }
+
+    /**
+     * @param PEAR_Packager
+     * @param bool if true, a .tgz is written, otherwise a .tar is written
+     * @param string|null directory in which to save the .tgz
+     * @return string|PEAR_Error location of package or error object
+     */
+    function toTgz(&$packager, $compress = true, $where = null)
+    {
+        require_once 'Archive/Tar.php';
+        if ($where === null) {
+            if (!($where = System::mktemp(array('-d')))) {
+                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: mktemp failed');
+            }
+        } elseif (!@System::mkDir(array('-p', $where))) {
+            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: "' . $where . '" could' .
+                ' not be created');
+        }
+        if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') &&
+              !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) {
+            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: unable to save package.xml as' .
+                ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"');
+        }
+        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
+            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: invalid package file');
+        }
+        $pkginfo = $this->_packagefile->getArray();
+        $ext = $compress ? '.tgz' : '.tar';
+        $pkgver = $pkginfo['package'] . '-' . $pkginfo['version'];
+        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
+        if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) &&
+              !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) {
+            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: cannot create tgz file "' .
+                getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"');
+        }
+        if ($pkgfile = $this->_packagefile->getPackageFile()) {
+            $pkgdir = dirname(realpath($pkgfile));
+            $pkgfile = basename($pkgfile);
+        } else {
+            return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: package file object must ' .
+                'be created from a real file');
+        }
+        // {{{ Create the package file list
+        $filelist = array();
+        $i = 0;
+
+        foreach ($this->_packagefile->getFilelist() as $fname => $atts) {
+            $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
+            if (!file_exists($file)) {
+                return PEAR::raiseError("File does not exist: $fname");
+            } else {
+                $filelist[$i++] = $file;
+                if (!isset($atts['md5sum'])) {
+                    $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($file));
+                }
+                $packager->log(2, "Adding file $fname");
+            }
+        }
+        // }}}
+        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
+        if ($packagexml) {
+            $tar =& new Archive_Tar($dest_package, $compress);
+            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
+            // ----- Creates with the package.xml file
+            $ok = $tar->createModify(array($packagexml), '', $where);
+            if (PEAR::isError($ok)) {
+                return $ok;
+            } elseif (!$ok) {
+                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
+            }
+            // ----- Add the content of the package
+            if (!$tar->addModify($filelist, $pkgver, $pkgdir)) {
+                return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
+            }
+            return $dest_package;
+        }
+    }
+
+    /**
+     * @param string|null directory to place the package.xml in, or null for a temporary dir
+     * @param int one of the PEAR_VALIDATE_* constants
+     * @param string name of the generated file
+     * @param bool if true, then no analysis will be performed on role="php" files
+     * @return string|PEAR_Error path to the created file on success
+     */
+    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml',
+                           $nofilechecking = false)
+    {
+        if (!$this->_packagefile->validate($state, $nofilechecking)) {
+            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: invalid package.xml',
+                null, null, null, $this->_packagefile->getValidationWarnings());
+        }
+        if ($where === null) {
+            if (!($where = System::mktemp(array('-d')))) {
+                return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: mktemp failed');
+            }
+        } elseif (!@System::mkDir(array('-p', $where))) {
+            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: "' . $where . '" could' .
+                ' not be created');
+        }
+        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
+        $np = @fopen($newpkgfile, 'wb');
+        if (!$np) {
+            return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: unable to save ' .
+               "$name as $newpkgfile");
+        }
+        fwrite($np, $this->toXml($state, true));
+        fclose($np);
+        return $newpkgfile;
+    }
+
+    /**
+     * fix both XML encoding to be UTF8, and replace standard XML entities < > " & '
+     *
+     * @param string $string
+     * @return string
+     * @access private
+     */
+    function _fixXmlEncoding($string)
+    {
+        if (version_compare(phpversion(), '5.0.0', 'lt')) {
+            $string = utf8_encode($string);
+        }
+        return strtr($string, array(
+                                          '&'  => '&amp;',
+                                          '>'  => '&gt;',
+                                          '<'  => '&lt;',
+                                          '"'  => '&quot;',
+                                          '\'' => '&apos;' ));
+    }
+
+    /**
+     * Return an XML document based on the package info (as returned
+     * by the PEAR_Common::infoFrom* methods).
+     *
+     * @return string XML data
+     */
+    function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false)
+    {
+        $this->_packagefile->setDate(date('Y-m-d'));
+        if (!$this->_packagefile->validate($state, $nofilevalidation)) {
+            return false;
+        }
+        $pkginfo = $this->_packagefile->getArray();
+        static $maint_map = array(
+            "handle" => "user",
+            "name" => "name",
+            "email" => "email",
+            "role" => "role",
+            );
+        $ret = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
+        $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
+        $ret .= "<package version=\"1.0\" packagerversion=\"1.9.4\">\n" .
+" <name>$pkginfo[package]</name>";
+        if (isset($pkginfo['extends'])) {
+            $ret .= "\n<extends>$pkginfo[extends]</extends>";
+        }
+        $ret .=
+ "\n <summary>".$this->_fixXmlEncoding($pkginfo['summary'])."</summary>\n" .
+" <description>".trim($this->_fixXmlEncoding($pkginfo['description']))."\n </description>\n" .
+" <maintainers>\n";
+        foreach ($pkginfo['maintainers'] as $maint) {
+            $ret .= "  <maintainer>\n";
+            foreach ($maint_map as $idx => $elm) {
+                $ret .= "   <$elm>";
+                $ret .= $this->_fixXmlEncoding($maint[$idx]);
+                $ret .= "</$elm>\n";
+            }
+            $ret .= "  </maintainer>\n";
+        }
+        $ret .= "  </maintainers>\n";
+        $ret .= $this->_makeReleaseXml($pkginfo, false, $state);
+        if (isset($pkginfo['changelog']) && count($pkginfo['changelog']) > 0) {
+            $ret .= " <changelog>\n";
+            foreach ($pkginfo['changelog'] as $oldrelease) {
+                $ret .= $this->_makeReleaseXml($oldrelease, true);
+            }
+            $ret .= " </changelog>\n";
+        }
+        $ret .= "</package>\n";
+        return $ret;
+    }
+
+    // }}}
+    // {{{ _makeReleaseXml()
+
+    /**
+     * Generate part of an XML description with release information.
+     *
+     * @param array  $pkginfo    array with release information
+     * @param bool   $changelog  whether the result will be in a changelog element
+     *
+     * @return string XML data
+     *
+     * @access private
+     */
+    function _makeReleaseXml($pkginfo, $changelog = false, $state = PEAR_VALIDATE_NORMAL)
+    {
+        // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
+        $indent = $changelog ? "  " : "";
+        $ret = "$indent <release>\n";
+        if (!empty($pkginfo['version'])) {
+            $ret .= "$indent  <version>$pkginfo[version]</version>\n";
+        }
+        if (!empty($pkginfo['release_date'])) {
+            $ret .= "$indent  <date>$pkginfo[release_date]</date>\n";
+        }
+        if (!empty($pkginfo['release_license'])) {
+            $ret .= "$indent  <license>$pkginfo[release_license]</license>\n";
+        }
+        if (!empty($pkginfo['release_state'])) {
+            $ret .= "$indent  <state>$pkginfo[release_state]</state>\n";
+        }
+        if (!empty($pkginfo['release_notes'])) {
+            $ret .= "$indent  <notes>".trim($this->_fixXmlEncoding($pkginfo['release_notes']))
+            ."\n$indent  </notes>\n";
+        }
+        if (!empty($pkginfo['release_warnings'])) {
+            $ret .= "$indent  <warnings>".$this->_fixXmlEncoding($pkginfo['release_warnings'])."</warnings>\n";
+        }
+        if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
+            $ret .= "$indent  <deps>\n";
+            foreach ($pkginfo['release_deps'] as $dep) {
+                $ret .= "$indent   <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
+                if (isset($dep['version'])) {
+                    $ret .= " version=\"$dep[version]\"";
+                }
+                if (isset($dep['optional'])) {
+                    $ret .= " optional=\"$dep[optional]\"";
+                }
+                if (isset($dep['name'])) {
+                    $ret .= ">$dep[name]</dep>\n";
+                } else {
+                    $ret .= "/>\n";
+                }
+            }
+            $ret .= "$indent  </deps>\n";
+        }
+        if (isset($pkginfo['configure_options'])) {
+            $ret .= "$indent  <configureoptions>\n";
+            foreach ($pkginfo['configure_options'] as $c) {
+                $ret .= "$indent   <configureoption name=\"".
+                    $this->_fixXmlEncoding($c['name']) . "\"";
+                if (isset($c['default'])) {
+                    $ret .= " default=\"" . $this->_fixXmlEncoding($c['default']) . "\"";
+                }
+                $ret .= " prompt=\"" . $this->_fixXmlEncoding($c['prompt']) . "\"";
+                $ret .= "/>\n";
+            }
+            $ret .= "$indent  </configureoptions>\n";
+        }
+        if (isset($pkginfo['provides'])) {
+            foreach ($pkginfo['provides'] as $key => $what) {
+                $ret .= "$indent  <provides type=\"$what[type]\" ";
+                $ret .= "name=\"$what[name]\" ";
+                if (isset($what['extends'])) {
+                    $ret .= "extends=\"$what[extends]\" ";
+                }
+                $ret .= "/>\n";
+            }
+        }
+        if (isset($pkginfo['filelist'])) {
+            $ret .= "$indent  <filelist>\n";
+            if ($state ^ PEAR_VALIDATE_PACKAGING) {
+                $ret .= $this->recursiveXmlFilelist($pkginfo['filelist']);
+            } else {
+                foreach ($pkginfo['filelist'] as $file => $fa) {
+                    if (!isset($fa['role'])) {
+                        $fa['role'] = '';
+                    }
+                    $ret .= "$indent   <file role=\"$fa[role]\"";
+                    if (isset($fa['baseinstalldir'])) {
+                        $ret .= ' baseinstalldir="' .
+                            $this->_fixXmlEncoding($fa['baseinstalldir']) . '"';
+                    }
+                    if (isset($fa['md5sum'])) {
+                        $ret .= " md5sum=\"$fa[md5sum]\"";
+                    }
+                    if (isset($fa['platform'])) {
+                        $ret .= " platform=\"$fa[platform]\"";
+                    }
+                    if (!empty($fa['install-as'])) {
+                        $ret .= ' install-as="' .
+                            $this->_fixXmlEncoding($fa['install-as']) . '"';
+                    }
+                    $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
+                    if (empty($fa['replacements'])) {
+                        $ret .= "/>\n";
+                    } else {
+                        $ret .= ">\n";
+                        foreach ($fa['replacements'] as $r) {
+                            $ret .= "$indent    <replace";
+                            foreach ($r as $k => $v) {
+                                $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
+                            }
+                            $ret .= "/>\n";
+                        }
+                        $ret .= "$indent   </file>\n";
+                    }
+                }
+            }
+            $ret .= "$indent  </filelist>\n";
+        }
+        $ret .= "$indent </release>\n";
+        return $ret;
+    }
+
+    /**
+     * @param array
+     * @access protected
+     */
+    function recursiveXmlFilelist($list)
+    {
+        $this->_dirs = array();
+        foreach ($list as $file => $attributes) {
+            $this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes);
+        }
+        return $this->_formatDir($this->_dirs);
+    }
+
+    /**
+     * @param array
+     * @param array
+     * @param string|null
+     * @param array|null
+     * @access private
+     */
+    function _addDir(&$dirs, $dir, $file = null, $attributes = null)
+    {
+        if ($dir == array() || $dir == array('.')) {
+            $dirs['files'][basename($file)] = $attributes;
+            return;
+        }
+        $curdir = array_shift($dir);
+        if (!isset($dirs['dirs'][$curdir])) {
+            $dirs['dirs'][$curdir] = array();
+        }
+        $this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes);
+    }
+
+    /**
+     * @param array
+     * @param string
+     * @param string
+     * @access private
+     */
+    function _formatDir($dirs, $indent = '', $curdir = '')
+    {
+        $ret = '';
+        if (!count($dirs)) {
+            return '';
+        }
+        if (isset($dirs['dirs'])) {
+            uksort($dirs['dirs'], 'strnatcasecmp');
+            foreach ($dirs['dirs'] as $dir => $contents) {
+                $usedir = "$curdir/$dir";
+                $ret .= "$indent   <dir name=\"$dir\">\n";
+                $ret .= $this->_formatDir($contents, "$indent ", $usedir);
+                $ret .= "$indent   </dir> <!-- $usedir -->\n";
+            }
+        }
+        if (isset($dirs['files'])) {
+            uksort($dirs['files'], 'strnatcasecmp');
+            foreach ($dirs['files'] as $file => $attribs) {
+                $ret .= $this->_formatFile($file, $attribs, $indent);
+            }
+        }
+        return $ret;
+    }
+
+    /**
+     * @param string
+     * @param array
+     * @param string
+     * @access private
+     */
+    function _formatFile($file, $attributes, $indent)
+    {
+        $ret = "$indent   <file role=\"$attributes[role]\"";
+        if (isset($attributes['baseinstalldir'])) {
+            $ret .= ' baseinstalldir="' .
+                $this->_fixXmlEncoding($attributes['baseinstalldir']) . '"';
+        }
+        if (isset($attributes['md5sum'])) {
+            $ret .= " md5sum=\"$attributes[md5sum]\"";
+        }
+        if (isset($attributes['platform'])) {
+            $ret .= " platform=\"$attributes[platform]\"";
+        }
+        if (!empty($attributes['install-as'])) {
+            $ret .= ' install-as="' .
+                $this->_fixXmlEncoding($attributes['install-as']) . '"';
+        }
+        $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
+        if (empty($attributes['replacements'])) {
+            $ret .= "/>\n";
+        } else {
+            $ret .= ">\n";
+            foreach ($attributes['replacements'] as $r) {
+                $ret .= "$indent    <replace";
+                foreach ($r as $k => $v) {
+                    $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
+                }
+                $ret .= "/>\n";
+            }
+            $ret .= "$indent   </file>\n";
+        }
+        return $ret;
+    }
+
+    // {{{ _unIndent()
+
+    /**
+     * Unindent given string (?)
+     *
+     * @param string $str The string that has to be unindented.
+     * @return string
+     * @access private
+     */
+    function _unIndent($str)
+    {
+        // remove leading newlines
+        $str = preg_replace('/^[\r\n]+/', '', $str);
+        // find whitespace at the beginning of the first line
+        $indent_len = strspn($str, " \t");
+        $indent = substr($str, 0, $indent_len);
+        $data = '';
+        // remove the same amount of whitespace from following lines
+        foreach (explode("\n", $str) as $line) {
+            if (substr($line, 0, $indent_len) == $indent) {
+                $data .= substr($line, $indent_len) . "\n";
+            }
+        }
+        return $data;
+    }
+
+    /**
+     * @return array
+     */
+    function dependenciesToV2()
+    {
+        $arr = array();
+        $this->_convertDependencies2_0($arr);
+        return $arr['dependencies'];
+    }
+
+    /**
+     * Convert a package.xml version 1.0 into version 2.0
+     *
+     * Note that this does a basic conversion, to allow more advanced
+     * features like bundles and multiple releases
+     * @param string the classname to instantiate and return.  This must be
+     *               PEAR_PackageFile_v2 or a descendant
+     * @param boolean if true, only valid, deterministic package.xml 1.0 as defined by the
+     *                strictest parameters will be converted
+     * @return PEAR_PackageFile_v2|PEAR_Error
+     */
+    function &toV2($class = 'PEAR_PackageFile_v2', $strict = false)
+    {
+        if ($strict) {
+            if (!$this->_packagefile->validate()) {
+                $a = PEAR::raiseError('invalid package.xml version 1.0 cannot be converted' .
+                    ' to version 2.0', null, null, null,
+                    $this->_packagefile->getValidationWarnings(true));
+                return $a;
+            }
+        }
+
+        $arr = array(
+            'attribs' => array(
+                             'version' => '2.0',
+                             'xmlns' => 'http://pear.php.net/dtd/package-2.0',
+                             'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
+                             'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
+                             'xsi:schemaLocation' => "http://pear.php.net/dtd/tasks-1.0\n" .
+"http://pear.php.net/dtd/tasks-1.0.xsd\n" .
+"http://pear.php.net/dtd/package-2.0\n" .
+'http://pear.php.net/dtd/package-2.0.xsd',
+                         ),
+            'name' => $this->_packagefile->getPackage(),
+            'channel' => 'pear.php.net',
+        );
+        $arr['summary'] = $this->_packagefile->getSummary();
+        $arr['description'] = $this->_packagefile->getDescription();
+        $maintainers = $this->_packagefile->getMaintainers();
+        foreach ($maintainers as $maintainer) {
+            if ($maintainer['role'] != 'lead') {
+                continue;
+            }
+            $new = array(
+                'name' => $maintainer['name'],
+                'user' => $maintainer['handle'],
+                'email' => $maintainer['email'],
+                'active' => 'yes',
+            );
+            $arr['lead'][] = $new;
+        }
+
+        if (!isset($arr['lead'])) { // some people... you know?
+            $arr['lead'] = array(
+                'name' => 'unknown',
+                'user' => 'unknown',
+                'email' => 'noleadmaintainer@example.com',
+                'active' => 'no',
+            );
+        }
+
+        if (count($arr['lead']) == 1) {
+            $arr['lead'] = $arr['lead'][0];
+        }
+
+        foreach ($maintainers as $maintainer) {
+            if ($maintainer['role'] == 'lead') {
+                continue;
+            }
+            $new = array(
+                'name' => $maintainer['name'],
+                'user' => $maintainer['handle'],
+                'email' => $maintainer['email'],
+                'active' => 'yes',
+            );
+            $arr[$maintainer['role']][] = $new;
+        }
+
+        if (isset($arr['developer']) && count($arr['developer']) == 1) {
+            $arr['developer'] = $arr['developer'][0];
+        }
+
+        if (isset($arr['contributor']) && count($arr['contributor']) == 1) {
+            $arr['contributor'] = $arr['contributor'][0];
+        }
+
+        if (isset($arr['helper']) && count($arr['helper']) == 1) {
+            $arr['helper'] = $arr['helper'][0];
+        }
+
+        $arr['date'] = $this->_packagefile->getDate();
+        $arr['version'] =
+            array(
+                'release' => $this->_packagefile->getVersion(),
+                'api' => $this->_packagefile->getVersion(),
+            );
+        $arr['stability'] =
+            array(
+                'release' => $this->_packagefile->getState(),
+                'api' => $this->_packagefile->getState(),
+            );
+        $licensemap =
+            array(
+                'php' => 'http://www.php.net/license',
+                'php license' => 'http://www.php.net/license',
+                'lgpl' => 'http://www.gnu.org/copyleft/lesser.html',
+                'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
+                'bsd style' => 'http://www.opensource.org/licenses/bsd-license.php',
+                'bsd-style' => 'http://www.opensource.org/licenses/bsd-license.php',
+                'mit' => 'http://www.opensource.org/licenses/mit-license.php',
+                'gpl' => 'http://www.gnu.org/copyleft/gpl.html',
+                'apache' => 'http://www.opensource.org/licenses/apache2.0.php'
+            );
+
+        if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) {
+            $arr['license'] = array(
+                'attribs' => array('uri' =>
+                    $licensemap[strtolower($this->_packagefile->getLicense())]),
+                '_content' => $this->_packagefile->getLicense()
+                );
+        } else {
+            // don't use bogus uri
+            $arr['license'] = $this->_packagefile->getLicense();
+        }
+
+        $arr['notes'] = $this->_packagefile->getNotes();
+        $temp = array();
+        $arr['contents'] = $this->_convertFilelist2_0($temp);
+        $this->_convertDependencies2_0($arr);
+        $release = ($this->_packagefile->getConfigureOptions() || $this->_isExtension) ?
+            'extsrcrelease' : 'phprelease';
+        if ($release == 'extsrcrelease') {
+            $arr['channel'] = 'pecl.php.net';
+            $arr['providesextension'] = $arr['name']; // assumption
+        }
+
+        $arr[$release] = array();
+        if ($this->_packagefile->getConfigureOptions()) {
+            $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions();
+            foreach ($arr[$release]['configureoption'] as $i => $opt) {
+                $arr[$release]['configureoption'][$i] = array('attribs' => $opt);
+            }
+            if (count($arr[$release]['configureoption']) == 1) {
+                $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0];
+            }
+        }
+
+        $this->_convertRelease2_0($arr[$release], $temp);
+        if ($release == 'extsrcrelease' && count($arr[$release]) > 1) {
+            // multiple extsrcrelease tags added in PEAR 1.4.1
+            $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1';
+        }
+
+        if ($cl = $this->_packagefile->getChangelog()) {
+            foreach ($cl as $release) {
+                $rel = array();
+                $rel['version'] =
+                    array(
+                        'release' => $release['version'],
+                        'api' => $release['version'],
+                    );
+                if (!isset($release['release_state'])) {
+                    $release['release_state'] = 'stable';
+                }
+
+                $rel['stability'] =
+                    array(
+                        'release' => $release['release_state'],
+                        'api' => $release['release_state'],
+                    );
+                if (isset($release['release_date'])) {
+                    $rel['date'] = $release['release_date'];
+                } else {
+                    $rel['date'] = date('Y-m-d');
+                }
+
+                if (isset($release['release_license'])) {
+                    if (isset($licensemap[strtolower($release['release_license'])])) {
+                        $uri = $licensemap[strtolower($release['release_license'])];
+                    } else {
+                        $uri = 'http://www.example.com';
+                    }
+                    $rel['license'] = array(
+                            'attribs' => array('uri' => $uri),
+                            '_content' => $release['release_license']
+                        );
+                } else {
+                    $rel['license'] = $arr['license'];
+                }
+
+                if (!isset($release['release_notes'])) {
+                    $release['release_notes'] = 'no release notes';
+                }
+
+                $rel['notes'] = $release['release_notes'];
+                $arr['changelog']['release'][] = $rel;
+            }
+        }
+
+        $ret = new $class;
+        $ret->setConfig($this->_packagefile->_config);
+        if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) {
+            $ret->setLogger($this->_packagefile->_logger);
+        }
+
+        $ret->fromArray($arr);
+        return $ret;
+    }
+
+    /**
+     * @param array
+     * @param bool
+     * @access private
+     */
+    function _convertDependencies2_0(&$release, $internal = false)
+    {
+        $peardep = array('pearinstaller' =>
+            array('min' => '1.4.0b1')); // this is a lot safer
+        $required = $optional = array();
+        $release['dependencies'] = array('required' => array());
+        if ($this->_packagefile->hasDeps()) {
+            foreach ($this->_packagefile->getDeps() as $dep) {
+                if (!isset($dep['optional']) || $dep['optional'] == 'no') {
+                    $required[] = $dep;
+                } else {
+                    $optional[] = $dep;
+                }
+            }
+            foreach (array('required', 'optional') as $arr) {
+                $deps = array();
+                foreach ($$arr as $dep) {
+                    // organize deps by dependency type and name
+                    if (!isset($deps[$dep['type']])) {
+                        $deps[$dep['type']] = array();
+                    }
+                    if (isset($dep['name'])) {
+                        $deps[$dep['type']][$dep['name']][] = $dep;
+                    } else {
+                        $deps[$dep['type']][] = $dep;
+                    }
+                }
+                do {
+                    if (isset($deps['php'])) {
+                        $php = array();
+                        if (count($deps['php']) > 1) {
+                            $php = $this->_processPhpDeps($deps['php']);
+                        } else {
+                            if (!isset($deps['php'][0])) {
+                                list($key, $blah) = each ($deps['php']); // stupid buggy versions
+                                $deps['php'] = array($blah[0]);
+                            }
+                            $php = $this->_processDep($deps['php'][0]);
+                            if (!$php) {
+                                break; // poor mans throw
+                            }
+                        }
+                        $release['dependencies'][$arr]['php'] = $php;
+                    }
+                } while (false);
+                do {
+                    if (isset($deps['pkg'])) {
+                        $pkg = array();
+                        $pkg = $this->_processMultipleDepsName($deps['pkg']);
+                        if (!$pkg) {
+                            break; // poor mans throw
+                        }
+                        $release['dependencies'][$arr]['package'] = $pkg;
+                    }
+                } while (false);
+                do {
+                    if (isset($deps['ext'])) {
+                        $pkg = array();
+                        $pkg = $this->_processMultipleDepsName($deps['ext']);
+                        $release['dependencies'][$arr]['extension'] = $pkg;
+                    }
+                } while (false);
+                // skip sapi - it's not supported so nobody will have used it
+                // skip os - it's not supported in 1.0
+            }
+        }
+        if (isset($release['dependencies']['required'])) {
+            $release['dependencies']['required'] =
+                array_merge($peardep, $release['dependencies']['required']);
+        } else {
+            $release['dependencies']['required'] = $peardep;
+        }
+        if (!isset($release['dependencies']['required']['php'])) {
+            $release['dependencies']['required']['php'] =
+                array('min' => '4.0.0');
+        }
+        $order = array();
+        $bewm = $release['dependencies']['required'];
+        $order['php'] = $bewm['php'];
+        $order['pearinstaller'] = $bewm['pearinstaller'];
+        isset($bewm['package']) ? $order['package'] = $bewm['package'] :0;
+        isset($bewm['extension']) ? $order['extension'] = $bewm['extension'] :0;
+        $release['dependencies']['required'] = $order;
+    }
+
+    /**
+     * @param array
+     * @access private
+     */
+    function _convertFilelist2_0(&$package)
+    {
+        $ret = array('dir' =>
+                    array(
+                        'attribs' => array('name' => '/'),
+                        'file' => array()
+                        )
+                    );
+        $package['platform'] =
+        $package['install-as'] = array();
+        $this->_isExtension = false;
+        foreach ($this->_packagefile->getFilelist() as $name => $file) {
+            $file['name'] = $name;
+            if (isset($file['role']) && $file['role'] == 'src') {
+                $this->_isExtension = true;
+            }
+            if (isset($file['replacements'])) {
+                $repl = $file['replacements'];
+                unset($file['replacements']);
+            } else {
+                unset($repl);
+            }
+            if (isset($file['install-as'])) {
+                $package['install-as'][$name] = $file['install-as'];
+                unset($file['install-as']);
+            }
+            if (isset($file['platform'])) {
+                $package['platform'][$name] = $file['platform'];
+                unset($file['platform']);
+            }
+            $file = array('attribs' => $file);
+            if (isset($repl)) {
+                foreach ($repl as $replace ) {
+                    $file['tasks:replace'][] = array('attribs' => $replace);
+                }
+                if (count($repl) == 1) {
+                    $file['tasks:replace'] = $file['tasks:replace'][0];
+                }
+            }
+            $ret['dir']['file'][] = $file;
+        }
+        return $ret;
+    }
+
+    /**
+     * Post-process special files with install-as/platform attributes and
+     * make the release tag.
+     *
+     * This complex method follows this work-flow to create the release tags:
+     *
+     * <pre>
+     * - if any install-as/platform exist, create a generic release and fill it with
+     *   o <install as=..> tags for <file name=... install-as=...>
+     *   o <install as=..> tags for <file name=... platform=!... install-as=..>
+     *   o <ignore> tags for <file name=... platform=...>
+     *   o <ignore> tags for <file name=... platform=... install-as=..>
+     * - create a release for each platform encountered and fill with
+     *   o <install as..> tags for <file name=... install-as=...>
+     *   o <install as..> tags for <file name=... platform=this platform install-as=..>
+     *   o <install as..> tags for <file name=... platform=!other platform install-as=..>
+     *   o <ignore> tags for <file name=... platform=!this platform>
+     *   o <ignore> tags for <file name=... platform=other platform>
+     *   o <ignore> tags for <file name=... platform=other platform install-as=..>
+     *   o <ignore> tags for <file name=... platform=!this platform install-as=..>
+     * </pre>
+     *
+     * It does this by accessing the $package parameter, which contains an array with
+     * indices:
+     *
+     *  - platform: mapping of file => OS the file should be installed on
+     *  - install-as: mapping of file => installed name
+     *  - osmap: mapping of OS => list of files that should be installed
+     *    on that OS
+     *  - notosmap: mapping of OS => list of files that should not be
+     *    installed on that OS
+     *
+     * @param array
+     * @param array
+     * @access private
+     */
+    function _convertRelease2_0(&$release, $package)
+    {
+        //- if any install-as/platform exist, create a generic release and fill it with
+        if (count($package['platform']) || count($package['install-as'])) {
+            $generic = array();
+            $genericIgnore = array();
+            foreach ($package['install-as'] as $file => $as) {
+                //o <install as=..> tags for <file name=... install-as=...>
+                if (!isset($package['platform'][$file])) {
+                    $generic[] = $file;
+                    continue;
+                }
+                //o <install as=..> tags for <file name=... platform=!... install-as=..>
+                if (isset($package['platform'][$file]) &&
+                      $package['platform'][$file]{0} == '!') {
+                    $generic[] = $file;
+                    continue;
+                }
+                //o <ignore> tags for <file name=... platform=... install-as=..>
+                if (isset($package['platform'][$file]) &&
+                      $package['platform'][$file]{0} != '!') {
+                    $genericIgnore[] = $file;
+                    continue;
+                }
+            }
+            foreach ($package['platform'] as $file => $platform) {
+                if (isset($package['install-as'][$file])) {
+                    continue;
+                }
+                if ($platform{0} != '!') {
+                    //o <ignore> tags for <file name=... platform=...>
+                    $genericIgnore[] = $file;
+                }
+            }
+            if (count($package['platform'])) {
+                $oses = $notplatform = $platform = array();
+                foreach ($package['platform'] as $file => $os) {
+                    // get a list of oses
+                    if ($os{0} == '!') {
+                        if (isset($oses[substr($os, 1)])) {
+                            continue;
+                        }
+                        $oses[substr($os, 1)] = count($oses);
+                    } else {
+                        if (isset($oses[$os])) {
+                            continue;
+                        }
+                        $oses[$os] = count($oses);
+                    }
+                }
+                //- create a release for each platform encountered and fill with
+                foreach ($oses as $os => $releaseNum) {
+                    $release[$releaseNum]['installconditions']['os']['name'] = $os;
+                    $release[$releaseNum]['filelist'] = array('install' => array(),
+                        'ignore' => array());
+                    foreach ($package['install-as'] as $file => $as) {
+                        //o <install as=..> tags for <file name=... install-as=...>
+                        if (!isset($package['platform'][$file])) {
+                            $release[$releaseNum]['filelist']['install'][] =
+                                array(
+                                    'attribs' => array(
+                                        'name' => $file,
+                                        'as' => $as,
+                                    ),
+                                );
+                            continue;
+                        }
+                        //o <install as..> tags for
+                        //  <file name=... platform=this platform install-as=..>
+                        if (isset($package['platform'][$file]) &&
+                              $package['platform'][$file] == $os) {
+                            $release[$releaseNum]['filelist']['install'][] =
+                                array(
+                                    'attribs' => array(
+                                        'name' => $file,
+                                        'as' => $as,
+                                    ),
+                                );
+                            continue;
+                        }
+                        //o <install as..> tags for
+                        //  <file name=... platform=!other platform install-as=..>
+                        if (isset($package['platform'][$file]) &&
+                              $package['platform'][$file] != "!$os" &&
+                              $package['platform'][$file]{0} == '!') {
+                            $release[$releaseNum]['filelist']['install'][] =
+                                array(
+                                    'attribs' => array(
+                                        'name' => $file,
+                                        'as' => $as,
+                                    ),
+                                );
+                            continue;
+                        }
+                        //o <ignore> tags for
+                        //  <file name=... platform=!this platform install-as=..>
+                        if (isset($package['platform'][$file]) &&
+                              $package['platform'][$file] == "!$os") {
+                            $release[$releaseNum]['filelist']['ignore'][] =
+                                array(
+                                    'attribs' => array(
+                                        'name' => $file,
+                                    ),
+                                );
+                            continue;
+                        }
+                        //o <ignore> tags for
+                        //  <file name=... platform=other platform install-as=..>
+                        if (isset($package['platform'][$file]) &&
+                              $package['platform'][$file]{0} != '!' &&
+                              $package['platform'][$file] != $os) {
+                            $release[$releaseNum]['filelist']['ignore'][] =
+                                array(
+                                    'attribs' => array(
+                                        'name' => $file,
+                                    ),
+                                );
+                            continue;
+                        }
+                    }
+                    foreach ($package['platform'] as $file => $platform) {
+                        if (isset($package['install-as'][$file])) {
+                            continue;
+                        }
+                        //o <ignore> tags for <file name=... platform=!this platform>
+                        if ($platform == "!$os") {
+                            $release[$releaseNum]['filelist']['ignore'][] =
+                                array(
+                                    'attribs' => array(
+                                        'name' => $file,
+                                    ),
+                                );
+                            continue;
+                        }
+                        //o <ignore> tags for <file name=... platform=other platform>
+                        if ($platform{0} != '!' && $platform != $os) {
+                            $release[$releaseNum]['filelist']['ignore'][] =
+                                array(
+                                    'attribs' => array(
+                                        'name' => $file,
+                                    ),
+                                );
+                        }
+                    }
+                    if (!count($release[$releaseNum]['filelist']['install'])) {
+                        unset($release[$releaseNum]['filelist']['install']);
+                    }
+                    if (!count($release[$releaseNum]['filelist']['ignore'])) {
+                        unset($release[$releaseNum]['filelist']['ignore']);
+                    }
+                }
+                if (count($generic) || count($genericIgnore)) {
+                    $release[count($oses)] = array();
+                    if (count($generic)) {
+                        foreach ($generic as $file) {
+                            if (isset($package['install-as'][$file])) {
+                                $installas = $package['install-as'][$file];
+                            } else {
+                                $installas = $file;
+                            }
+                            $release[count($oses)]['filelist']['install'][] =
+                                array(
+                                    'attribs' => array(
+                                        'name' => $file,
+                                        'as' => $installas,
+                                    )
+                                );
+                        }
+                    }
+                    if (count($genericIgnore)) {
+                        foreach ($genericIgnore as $file) {
+                            $release[count($oses)]['filelist']['ignore'][] =
+                                array(
+                                    'attribs' => array(
+                                        'name' => $file,
+                                    )
+                                );
+                        }
+                    }
+                }
+                // cleanup
+                foreach ($release as $i => $rel) {
+                    if (isset($rel['filelist']['install']) &&
+                          count($rel['filelist']['install']) == 1) {
+                        $release[$i]['filelist']['install'] =
+                            $release[$i]['filelist']['install'][0];
+                    }
+                    if (isset($rel['filelist']['ignore']) &&
+                          count($rel['filelist']['ignore']) == 1) {
+                        $release[$i]['filelist']['ignore'] =
+                            $release[$i]['filelist']['ignore'][0];
+                    }
+                }
+                if (count($release) == 1) {
+                    $release = $release[0];
+                }
+            } else {
+                // no platform atts, but some install-as atts
+                foreach ($package['install-as'] as $file => $value) {
+                    $release['filelist']['install'][] =
+                        array(
+                            'attribs' => array(
+                                'name' => $file,
+                                'as' => $value
+                            )
+                        );
+                }
+                if (count($release['filelist']['install']) == 1) {
+                    $release['filelist']['install'] = $release['filelist']['install'][0];
+                }
+            }
+        }
+    }
+
+    /**
+     * @param array
+     * @return array
+     * @access private
+     */
+    function _processDep($dep)
+    {
+        if ($dep['type'] == 'php') {
+            if ($dep['rel'] == 'has') {
+                // come on - everyone has php!
+                return false;
+            }
+        }
+        $php = array();
+        if ($dep['type'] != 'php') {
+            $php['name'] = $dep['name'];
+            if ($dep['type'] == 'pkg') {
+                $php['channel'] = 'pear.php.net';
+            }
+        }
+        switch ($dep['rel']) {
+            case 'gt' :
+                $php['min'] = $dep['version'];
+                $php['exclude'] = $dep['version'];
+            break;
+            case 'ge' :
+                if (!isset($dep['version'])) {
+                    if ($dep['type'] == 'php') {
+                        if (isset($dep['name'])) {
+                            $dep['version'] = $dep['name'];
+                        }
+                    }
+                }
+                $php['min'] = $dep['version'];
+            break;
+            case 'lt' :
+                $php['max'] = $dep['version'];
+                $php['exclude'] = $dep['version'];
+            break;
+            case 'le' :
+                $php['max'] = $dep['version'];
+            break;
+            case 'eq' :
+                $php['min'] = $dep['version'];
+                $php['max'] = $dep['version'];
+            break;
+            case 'ne' :
+                $php['exclude'] = $dep['version'];
+            break;
+            case 'not' :
+                $php['conflicts'] = 'yes';
+            break;
+        }
+        return $php;
+    }
+
+    /**
+     * @param array
+     * @return array
+     */
+    function _processPhpDeps($deps)
+    {
+        $test = array();
+        foreach ($deps as $dep) {
+            $test[] = $this->_processDep($dep);
+        }
+        $min = array();
+        $max = array();
+        foreach ($test as $dep) {
+            if (!$dep) {
+                continue;
+            }
+            if (isset($dep['min'])) {
+                $min[$dep['min']] = count($min);
+            }
+            if (isset($dep['max'])) {
+                $max[$dep['max']] = count($max);
+            }
+        }
+        if (count($min) > 0) {
+            uksort($min, 'version_compare');
+        }
+        if (count($max) > 0) {
+            uksort($max, 'version_compare');
+        }
+        if (count($min)) {
+            // get the highest minimum
+            $min = array_pop($a = array_flip($min));
+        } else {
+            $min = false;
+        }
+        if (count($max)) {
+            // get the lowest maximum
+            $max = array_shift($a = array_flip($max));
+        } else {
+            $max = false;
+        }
+        if ($min) {
+            $php['min'] = $min;
+        }
+        if ($max) {
+            $php['max'] = $max;
+        }
+        $exclude = array();
+        foreach ($test as $dep) {
+            if (!isset($dep['exclude'])) {
+                continue;
+            }
+            $exclude[] = $dep['exclude'];
+        }
+        if (count($exclude)) {
+            $php['exclude'] = $exclude;
+        }
+        return $php;
+    }
+
+    /**
+     * process multiple dependencies that have a name, like package deps
+     * @param array
+     * @return array
+     * @access private
+     */
+    function _processMultipleDepsName($deps)
+    {
+        $ret = $tests = array();
+        foreach ($deps as $name => $dep) {
+            foreach ($dep as $d) {
+                $tests[$name][] = $this->_processDep($d);
+            }
+        }
+
+        foreach ($tests as $name => $test) {
+            $max = $min = $php = array();
+            $php['name'] = $name;
+            foreach ($test as $dep) {
+                if (!$dep) {
+                    continue;
+                }
+                if (isset($dep['channel'])) {
+                    $php['channel'] = 'pear.php.net';
+                }
+                if (isset($dep['conflicts']) && $dep['conflicts'] == 'yes') {
+                    $php['conflicts'] = 'yes';
+                }
+                if (isset($dep['min'])) {
+                    $min[$dep['min']] = count($min);
+                }
+                if (isset($dep['max'])) {
+                    $max[$dep['max']] = count($max);
+                }
+            }
+            if (count($min) > 0) {
+                uksort($min, 'version_compare');
+            }
+            if (count($max) > 0) {
+                uksort($max, 'version_compare');
+            }
+            if (count($min)) {
+                // get the highest minimum
+                $min = array_pop($a = array_flip($min));
+            } else {
+                $min = false;
+            }
+            if (count($max)) {
+                // get the lowest maximum
+                $max = array_shift($a = array_flip($max));
+            } else {
+                $max = false;
+            }
+            if ($min) {
+                $php['min'] = $min;
+            }
+            if ($max) {
+                $php['max'] = $max;
+            }
+            $exclude = array();
+            foreach ($test as $dep) {
+                if (!isset($dep['exclude'])) {
+                    continue;
+                }
+                $exclude[] = $dep['exclude'];
+            }
+            if (count($exclude)) {
+                $php['exclude'] = $exclude;
+            }
+            $ret[] = $php;
+        }
+        return $ret;
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/PackageFile/Generator/v2.php
===================================================================
--- trunk/library/PEAR/PEAR/PackageFile/Generator/v2.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/PackageFile/Generator/v2.php	(revision 5146)
@@ -0,0 +1,893 @@
+<?php
+/**
+ * package.xml generation class, package.xml version 2.0
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @author     Stephan Schmidt (original XML_Serializer code)
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * file/dir manipulation routines
+ */
+require_once 'System.php';
+require_once 'XML/Util.php';
+
+/**
+ * This class converts a PEAR_PackageFile_v2 object into any output format.
+ *
+ * Supported output formats include array, XML string (using S. Schmidt's
+ * XML_Serializer, slightly customized)
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @author     Stephan Schmidt (original XML_Serializer code)
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_PackageFile_Generator_v2
+{
+   /**
+    * default options for the serialization
+    * @access private
+    * @var array $_defaultOptions
+    */
+    var $_defaultOptions = array(
+        'indent'             => ' ',                    // string used for indentation
+        'linebreak'          => "\n",                  // string used for newlines
+        'typeHints'          => false,                 // automatically add type hin attributes
+        'addDecl'            => true,                 // add an XML declaration
+        'defaultTagName'     => 'XML_Serializer_Tag',  // tag used for indexed arrays or invalid names
+        'classAsTagName'     => false,                 // use classname for objects in indexed arrays
+        'keyAttribute'       => '_originalKey',        // attribute where original key is stored
+        'typeAttribute'      => '_type',               // attribute for type (only if typeHints => true)
+        'classAttribute'     => '_class',              // attribute for class of objects (only if typeHints => true)
+        'scalarAsAttributes' => false,                 // scalar values (strings, ints,..) will be serialized as attribute
+        'prependAttributes'  => '',                    // prepend string for attributes
+        'indentAttributes'   => false,                 // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
+        'mode'               => 'simplexml',             // use 'simplexml' to use parent name as tagname if transforming an indexed array
+        'addDoctype'         => false,                 // add a doctype declaration
+        'doctype'            => null,                  // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
+        'rootName'           => 'package',                  // name of the root tag
+        'rootAttributes'     => array(
+            'version' => '2.0',
+            'xmlns' => 'http://pear.php.net/dtd/package-2.0',
+            'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
+            'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
+            'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd',
+        ),               // attributes of the root tag
+        'attributesArray'    => 'attribs',                  // all values in this key will be treated as attributes
+        'contentName'        => '_content',                   // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
+        'beautifyFilelist'   => false,
+        'encoding' => 'UTF-8',
+    );
+
+   /**
+    * options for the serialization
+    * @access private
+    * @var array $options
+    */
+    var $options = array();
+
+   /**
+    * current tag depth
+    * @var integer $_tagDepth
+    */
+    var $_tagDepth = 0;
+
+   /**
+    * serilialized representation of the data
+    * @var string $_serializedData
+    */
+    var $_serializedData = null;
+    /**
+     * @var PEAR_PackageFile_v2
+     */
+    var $_packagefile;
+    /**
+     * @param PEAR_PackageFile_v2
+     */
+    function PEAR_PackageFile_Generator_v2(&$packagefile)
+    {
+        $this->_packagefile = &$packagefile;
+        if (isset($this->_packagefile->encoding)) {
+            $this->_defaultOptions['encoding'] = $this->_packagefile->encoding;
+        }
+    }
+
+    /**
+     * @return string
+     */
+    function getPackagerVersion()
+    {
+        return '1.9.4';
+    }
+
+    /**
+     * @param PEAR_Packager
+     * @param bool generate a .tgz or a .tar
+     * @param string|null temporary directory to package in
+     */
+    function toTgz(&$packager, $compress = true, $where = null)
+    {
+        $a = null;
+        return $this->toTgz2($packager, $a, $compress, $where);
+    }
+
+    /**
+     * Package up both a package.xml and package2.xml for the same release
+     * @param PEAR_Packager
+     * @param PEAR_PackageFile_v1
+     * @param bool generate a .tgz or a .tar
+     * @param string|null temporary directory to package in
+     */
+    function toTgz2(&$packager, &$pf1, $compress = true, $where = null)
+    {
+        require_once 'Archive/Tar.php';
+        if (!$this->_packagefile->isEquivalent($pf1)) {
+            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' .
+                basename($pf1->getPackageFile()) .
+                '" is not equivalent to "' . basename($this->_packagefile->getPackageFile())
+                . '"');
+        }
+
+        if ($where === null) {
+            if (!($where = System::mktemp(array('-d')))) {
+                return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed');
+            }
+        } elseif (!@System::mkDir(array('-p', $where))) {
+            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' .
+                ' not be created');
+        }
+
+        $file = $where . DIRECTORY_SEPARATOR . 'package.xml';
+        if (file_exists($file) && !is_file($file)) {
+            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' .
+                ' "' . $file  .'"');
+        }
+
+        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
+            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml');
+        }
+
+        $ext = $compress ? '.tgz' : '.tar';
+        $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion();
+        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
+        if (file_exists($dest_package) && !is_file($dest_package)) {
+            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' .
+                $dest_package . '"');
+        }
+
+        $pkgfile = $this->_packagefile->getPackageFile();
+        if (!$pkgfile) {
+            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' .
+                'be created from a real file');
+        }
+
+        $pkgdir  = dirname(realpath($pkgfile));
+        $pkgfile = basename($pkgfile);
+
+        // {{{ Create the package file list
+        $filelist = array();
+        $i = 0;
+        $this->_packagefile->flattenFilelist();
+        $contents = $this->_packagefile->getContents();
+        if (isset($contents['bundledpackage'])) { // bundles of packages
+            $contents = $contents['bundledpackage'];
+            if (!isset($contents[0])) {
+                $contents = array($contents);
+            }
+
+            $packageDir = $where;
+            foreach ($contents as $i => $package) {
+                $fname = $package;
+                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
+                if (!file_exists($file)) {
+                    return $packager->raiseError("File does not exist: $fname");
+                }
+
+                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
+                System::mkdir(array('-p', dirname($tfile)));
+                copy($file, $tfile);
+                $filelist[$i++] = $tfile;
+                $packager->log(2, "Adding package $fname");
+            }
+        } else { // normal packages
+            $contents = $contents['dir']['file'];
+            if (!isset($contents[0])) {
+                $contents = array($contents);
+            }
+
+            $packageDir = $where;
+            foreach ($contents as $i => $file) {
+                $fname = $file['attribs']['name'];
+                $atts = $file['attribs'];
+                $orig = $file;
+                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
+                if (!file_exists($file)) {
+                    return $packager->raiseError("File does not exist: $fname");
+                }
+
+                $origperms = fileperms($file);
+                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
+                unset($orig['attribs']);
+                if (count($orig)) { // file with tasks
+                    // run any package-time tasks
+                    $contents = file_get_contents($file);
+                    foreach ($orig as $tag => $raw) {
+                        $tag = str_replace(
+                            array($this->_packagefile->getTasksNs() . ':', '-'),
+                            array('', '_'), $tag);
+                        $task = "PEAR_Task_$tag";
+                        $task = &new $task($this->_packagefile->_config,
+                            $this->_packagefile->_logger,
+                            PEAR_TASK_PACKAGE);
+                        $task->init($raw, $atts, null);
+                        $res = $task->startSession($this->_packagefile, $contents, $tfile);
+                        if (!$res) {
+                            continue; // skip this task
+                        }
+
+                        if (PEAR::isError($res)) {
+                            return $res;
+                        }
+
+                        $contents = $res; // save changes
+                        System::mkdir(array('-p', dirname($tfile)));
+                        $wp = fopen($tfile, "wb");
+                        fwrite($wp, $contents);
+                        fclose($wp);
+                    }
+                }
+
+                if (!file_exists($tfile)) {
+                    System::mkdir(array('-p', dirname($tfile)));
+                    copy($file, $tfile);
+                }
+
+                chmod($tfile, $origperms);
+                $filelist[$i++] = $tfile;
+                $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1);
+                $packager->log(2, "Adding file $fname");
+            }
+        }
+            // }}}
+
+        $name       = $pf1 !== null ? 'package2.xml' : 'package.xml';
+        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name);
+        if ($packagexml) {
+            $tar =& new Archive_Tar($dest_package, $compress);
+            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
+            // ----- Creates with the package.xml file
+            $ok = $tar->createModify(array($packagexml), '', $where);
+            if (PEAR::isError($ok)) {
+                return $packager->raiseError($ok);
+            } elseif (!$ok) {
+                return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name .
+                    ' failed');
+            }
+
+            // ----- Add the content of the package
+            if (!$tar->addModify($filelist, $pkgver, $where)) {
+                return $packager->raiseError(
+                    'PEAR_Packagefile_v2::toTgz(): tarball creation failed');
+            }
+
+            // add the package.xml version 1.0
+            if ($pf1 !== null) {
+                $pfgen = &$pf1->getDefaultGenerator();
+                $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
+                if (!$tar->addModify(array($packagexml1), '', $where)) {
+                    return $packager->raiseError(
+                        'PEAR_Packagefile_v2::toTgz(): adding package.xml failed');
+                }
+            }
+
+            return $dest_package;
+        }
+    }
+
+    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml')
+    {
+        if (!$this->_packagefile->validate($state)) {
+            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml',
+                null, null, null, $this->_packagefile->getValidationWarnings());
+        }
+
+        if ($where === null) {
+            if (!($where = System::mktemp(array('-d')))) {
+                return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed');
+            }
+        } elseif (!@System::mkDir(array('-p', $where))) {
+            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' .
+                ' not be created');
+        }
+
+        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
+        $np = @fopen($newpkgfile, 'wb');
+        if (!$np) {
+            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' .
+               "$name as $newpkgfile");
+        }
+        fwrite($np, $this->toXml($state));
+        fclose($np);
+        return $newpkgfile;
+    }
+
+    function &toV2()
+    {
+        return $this->_packagefile;
+    }
+
+    /**
+     * Return an XML document based on the package info (as returned
+     * by the PEAR_Common::infoFrom* methods).
+     *
+     * @return string XML data
+     */
+    function toXml($state = PEAR_VALIDATE_NORMAL, $options = array())
+    {
+        $this->_packagefile->setDate(date('Y-m-d'));
+        $this->_packagefile->setTime(date('H:i:s'));
+        if (!$this->_packagefile->validate($state)) {
+            return false;
+        }
+
+        if (is_array($options)) {
+            $this->options = array_merge($this->_defaultOptions, $options);
+        } else {
+            $this->options = $this->_defaultOptions;
+        }
+
+        $arr = $this->_packagefile->getArray();
+        if (isset($arr['filelist'])) {
+            unset($arr['filelist']);
+        }
+
+        if (isset($arr['_lastversion'])) {
+            unset($arr['_lastversion']);
+        }
+
+        // Fix the notes a little bit
+        if (isset($arr['notes'])) {
+            // This trims out the indenting, needs fixing
+            $arr['notes'] = "\n" . trim($arr['notes']) . "\n";
+        }
+
+        if (isset($arr['changelog']) && !empty($arr['changelog'])) {
+            // Fix for inconsistency how the array is filled depending on the changelog release amount
+            if (!isset($arr['changelog']['release'][0])) {
+                $release = $arr['changelog']['release'];
+                unset($arr['changelog']['release']);
+
+                $arr['changelog']['release']    = array();
+                $arr['changelog']['release'][0] = $release;
+            }
+
+            foreach (array_keys($arr['changelog']['release']) as $key) {
+                $c =& $arr['changelog']['release'][$key];
+                if (isset($c['notes'])) {
+                    // This trims out the indenting, needs fixing
+                    $c['notes'] = "\n" . trim($c['notes']) . "\n";
+                }
+            }
+        }
+
+        if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) {
+            $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']);
+            unset($arr['contents']['dir']['file']);
+            if (isset($use['dir'])) {
+                $arr['contents']['dir']['dir'] = $use['dir'];
+            }
+            if (isset($use['file'])) {
+                $arr['contents']['dir']['file'] = $use['file'];
+            }
+            $this->options['beautifyFilelist'] = true;
+        }
+
+        $arr['attribs']['packagerversion'] = '1.9.4';
+        if ($this->serialize($arr, $options)) {
+            return $this->_serializedData . "\n";
+        }
+
+        return false;
+    }
+
+
+    function _recursiveXmlFilelist($list)
+    {
+        $dirs = array();
+        if (isset($list['attribs'])) {
+            $file = $list['attribs']['name'];
+            unset($list['attribs']['name']);
+            $attributes = $list['attribs'];
+            $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes);
+        } else {
+            foreach ($list as $a) {
+                $file = $a['attribs']['name'];
+                $attributes = $a['attribs'];
+                unset($a['attribs']);
+                $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a);
+            }
+        }
+        $this->_formatDir($dirs);
+        $this->_deFormat($dirs);
+        return $dirs;
+    }
+
+    function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null)
+    {
+        if (!$tasks) {
+            $tasks = array();
+        }
+        if ($dir == array() || $dir == array('.')) {
+            $dirs['file'][basename($file)] = $tasks;
+            $attributes['name'] = basename($file);
+            $dirs['file'][basename($file)]['attribs'] = $attributes;
+            return;
+        }
+        $curdir = array_shift($dir);
+        if (!isset($dirs['dir'][$curdir])) {
+            $dirs['dir'][$curdir] = array();
+        }
+        $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks);
+    }
+
+    function _formatDir(&$dirs)
+    {
+        if (!count($dirs)) {
+            return array();
+        }
+        $newdirs = array();
+        if (isset($dirs['dir'])) {
+            $newdirs['dir'] = $dirs['dir'];
+        }
+        if (isset($dirs['file'])) {
+            $newdirs['file'] = $dirs['file'];
+        }
+        $dirs = $newdirs;
+        if (isset($dirs['dir'])) {
+            uksort($dirs['dir'], 'strnatcasecmp');
+            foreach ($dirs['dir'] as $dir => $contents) {
+                $this->_formatDir($dirs['dir'][$dir]);
+            }
+        }
+        if (isset($dirs['file'])) {
+            uksort($dirs['file'], 'strnatcasecmp');
+        };
+    }
+
+    function _deFormat(&$dirs)
+    {
+        if (!count($dirs)) {
+            return array();
+        }
+        $newdirs = array();
+        if (isset($dirs['dir'])) {
+            foreach ($dirs['dir'] as $dir => $contents) {
+                $newdir = array();
+                $newdir['attribs']['name'] = $dir;
+                $this->_deFormat($contents);
+                foreach ($contents as $tag => $val) {
+                    $newdir[$tag] = $val;
+                }
+                $newdirs['dir'][] = $newdir;
+            }
+            if (count($newdirs['dir']) == 1) {
+                $newdirs['dir'] = $newdirs['dir'][0];
+            }
+        }
+        if (isset($dirs['file'])) {
+            foreach ($dirs['file'] as $name => $file) {
+                $newdirs['file'][] = $file;
+            }
+            if (count($newdirs['file']) == 1) {
+                $newdirs['file'] = $newdirs['file'][0];
+            }
+        }
+        $dirs = $newdirs;
+    }
+
+    /**
+    * reset all options to default options
+    *
+    * @access   public
+    * @see      setOption(), XML_Unserializer()
+    */
+    function resetOptions()
+    {
+        $this->options = $this->_defaultOptions;
+    }
+
+   /**
+    * set an option
+    *
+    * You can use this method if you do not want to set all options in the constructor
+    *
+    * @access   public
+    * @see      resetOption(), XML_Serializer()
+    */
+    function setOption($name, $value)
+    {
+        $this->options[$name] = $value;
+    }
+
+   /**
+    * sets several options at once
+    *
+    * You can use this method if you do not want to set all options in the constructor
+    *
+    * @access   public
+    * @see      resetOption(), XML_Unserializer(), setOption()
+    */
+    function setOptions($options)
+    {
+        $this->options = array_merge($this->options, $options);
+    }
+
+   /**
+    * serialize data
+    *
+    * @access   public
+    * @param    mixed    $data data to serialize
+    * @return   boolean  true on success, pear error on failure
+    */
+    function serialize($data, $options = null)
+    {
+        // if options have been specified, use them instead
+        // of the previously defined ones
+        if (is_array($options)) {
+            $optionsBak = $this->options;
+            if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
+                $this->options = array_merge($this->_defaultOptions, $options);
+            } else {
+                $this->options = array_merge($this->options, $options);
+            }
+        } else {
+            $optionsBak = null;
+        }
+
+        //  start depth is zero
+        $this->_tagDepth = 0;
+        $this->_serializedData = '';
+        // serialize an array
+        if (is_array($data)) {
+            $tagName = isset($this->options['rootName']) ? $this->options['rootName'] : 'array';
+            $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']);
+        }
+
+        // add doctype declaration
+        if ($this->options['addDoctype'] === true) {
+            $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype'])
+                                   . $this->options['linebreak']
+                                   . $this->_serializedData;
+        }
+
+        //  build xml declaration
+        if ($this->options['addDecl']) {
+            $atts = array();
+            $encoding = isset($this->options['encoding']) ? $this->options['encoding'] : null;
+            $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $encoding)
+                                   . $this->options['linebreak']
+                                   . $this->_serializedData;
+        }
+
+
+        if ($optionsBak !== null) {
+            $this->options = $optionsBak;
+        }
+
+        return  true;
+    }
+
+   /**
+    * get the result of the serialization
+    *
+    * @access public
+    * @return string serialized XML
+    */
+    function getSerializedData()
+    {
+        if ($this->_serializedData === null) {
+            return  $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
+        }
+        return $this->_serializedData;
+    }
+
+   /**
+    * serialize any value
+    *
+    * This method checks for the type of the value and calls the appropriate method
+    *
+    * @access private
+    * @param  mixed     $value
+    * @param  string    $tagName
+    * @param  array     $attributes
+    * @return string
+    */
+    function _serializeValue($value, $tagName = null, $attributes = array())
+    {
+        if (is_array($value)) {
+            $xml = $this->_serializeArray($value, $tagName, $attributes);
+        } elseif (is_object($value)) {
+            $xml = $this->_serializeObject($value, $tagName);
+        } else {
+            $tag = array(
+                          'qname'      => $tagName,
+                          'attributes' => $attributes,
+                          'content'    => $value
+                        );
+            $xml = $this->_createXMLTag($tag);
+        }
+        return $xml;
+    }
+
+   /**
+    * serialize an array
+    *
+    * @access   private
+    * @param    array   $array       array to serialize
+    * @param    string  $tagName     name of the root tag
+    * @param    array   $attributes  attributes for the root tag
+    * @return   string  $string      serialized data
+    * @uses     XML_Util::isValidName() to check, whether key has to be substituted
+    */
+    function _serializeArray(&$array, $tagName = null, $attributes = array())
+    {
+        $_content = null;
+
+        /**
+         * check for special attributes
+         */
+        if ($this->options['attributesArray'] !== null) {
+            if (isset($array[$this->options['attributesArray']])) {
+                $attributes = $array[$this->options['attributesArray']];
+                unset($array[$this->options['attributesArray']]);
+            }
+            /**
+             * check for special content
+             */
+            if ($this->options['contentName'] !== null) {
+                if (isset($array[$this->options['contentName']])) {
+                    $_content = $array[$this->options['contentName']];
+                    unset($array[$this->options['contentName']]);
+                }
+            }
+        }
+
+        /*
+        * if mode is set to simpleXML, check whether
+        * the array is associative or indexed
+        */
+        if (is_array($array) && $this->options['mode'] == 'simplexml') {
+            $indexed = true;
+            if (!count($array)) {
+                $indexed = false;
+            }
+            foreach ($array as $key => $val) {
+                if (!is_int($key)) {
+                    $indexed = false;
+                    break;
+                }
+            }
+
+            if ($indexed && $this->options['mode'] == 'simplexml') {
+                $string = '';
+                foreach ($array as $key => $val) {
+                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
+                        if (!isset($this->_curdir)) {
+                            $this->_curdir = '';
+                        }
+                        $savedir = $this->_curdir;
+                        if (isset($val['attribs'])) {
+                            if ($val['attribs']['name'] == '/') {
+                                $this->_curdir = '/';
+                            } else {
+                                if ($this->_curdir == '/') {
+                                    $this->_curdir = '';
+                                }
+                                $this->_curdir .= '/' . $val['attribs']['name'];
+                            }
+                        }
+                    }
+                    $string .= $this->_serializeValue( $val, $tagName, $attributes);
+                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
+                        $string .= ' <!-- ' . $this->_curdir . ' -->';
+                        if (empty($savedir)) {
+                            unset($this->_curdir);
+                        } else {
+                            $this->_curdir = $savedir;
+                        }
+                    }
+
+                    $string .= $this->options['linebreak'];
+                    // do indentation
+                    if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
+                        $string .= str_repeat($this->options['indent'], $this->_tagDepth);
+                    }
+                }
+                return rtrim($string);
+            }
+        }
+
+        if ($this->options['scalarAsAttributes'] === true) {
+            foreach ($array as $key => $value) {
+                if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
+                    unset($array[$key]);
+                    $attributes[$this->options['prependAttributes'].$key] = $value;
+                }
+            }
+        }
+
+        // check for empty array => create empty tag
+        if (empty($array)) {
+            $tag = array(
+                            'qname'      => $tagName,
+                            'content'    => $_content,
+                            'attributes' => $attributes
+                        );
+
+        } else {
+            $this->_tagDepth++;
+            $tmp = $this->options['linebreak'];
+            foreach ($array as $key => $value) {
+                // do indentation
+                if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
+                    $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
+                }
+
+                // copy key
+                $origKey = $key;
+                // key cannot be used as tagname => use default tag
+                $valid = XML_Util::isValidName($key);
+                if (PEAR::isError($valid)) {
+                    if ($this->options['classAsTagName'] && is_object($value)) {
+                        $key = get_class($value);
+                    } else {
+                        $key = $this->options['defaultTagName'];
+                    }
+                }
+                $atts = array();
+                if ($this->options['typeHints'] === true) {
+                    $atts[$this->options['typeAttribute']] = gettype($value);
+                    if ($key !== $origKey) {
+                        $atts[$this->options['keyAttribute']] = (string)$origKey;
+                    }
+
+                }
+                if ($this->options['beautifyFilelist'] && $key == 'dir') {
+                    if (!isset($this->_curdir)) {
+                        $this->_curdir = '';
+                    }
+                    $savedir = $this->_curdir;
+                    if (isset($value['attribs'])) {
+                        if ($value['attribs']['name'] == '/') {
+                            $this->_curdir = '/';
+                        } else {
+                            $this->_curdir .= '/' . $value['attribs']['name'];
+                        }
+                    }
+                }
+
+                if (is_string($value) && $value && ($value{strlen($value) - 1} == "\n")) {
+                    $value .= str_repeat($this->options['indent'], $this->_tagDepth);
+                }
+                $tmp .= $this->_createXMLTag(array(
+                                                    'qname'      => $key,
+                                                    'attributes' => $atts,
+                                                    'content'    => $value )
+                                            );
+                if ($this->options['beautifyFilelist'] && $key == 'dir') {
+                    if (isset($value['attribs'])) {
+                        $tmp .= ' <!-- ' . $this->_curdir . ' -->';
+                        if (empty($savedir)) {
+                            unset($this->_curdir);
+                        } else {
+                            $this->_curdir = $savedir;
+                        }
+                    }
+                }
+                $tmp .= $this->options['linebreak'];
+            }
+
+            $this->_tagDepth--;
+            if ($this->options['indent']!==null && $this->_tagDepth>0) {
+                $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
+            }
+
+            if (trim($tmp) === '') {
+                $tmp = null;
+            }
+
+            $tag = array(
+                'qname'      => $tagName,
+                'content'    => $tmp,
+                'attributes' => $attributes
+            );
+        }
+        if ($this->options['typeHints'] === true) {
+            if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
+                $tag['attributes'][$this->options['typeAttribute']] = 'array';
+            }
+        }
+
+        $string = $this->_createXMLTag($tag, false);
+        return $string;
+    }
+
+   /**
+    * create a tag from an array
+    * this method awaits an array in the following format
+    * array(
+    *       'qname'        => $tagName,
+    *       'attributes'   => array(),
+    *       'content'      => $content,      // optional
+    *       'namespace'    => $namespace     // optional
+    *       'namespaceUri' => $namespaceUri  // optional
+    *   )
+    *
+    * @access   private
+    * @param    array   $tag tag definition
+    * @param    boolean $replaceEntities whether to replace XML entities in content or not
+    * @return   string  $string XML tag
+    */
+    function _createXMLTag($tag, $replaceEntities = true)
+    {
+        if ($this->options['indentAttributes'] !== false) {
+            $multiline = true;
+            $indent    = str_repeat($this->options['indent'], $this->_tagDepth);
+
+            if ($this->options['indentAttributes'] == '_auto') {
+                $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
+
+            } else {
+                $indent .= $this->options['indentAttributes'];
+            }
+        } else {
+            $indent = $multiline = false;
+        }
+
+        if (is_array($tag['content'])) {
+            if (empty($tag['content'])) {
+                $tag['content'] = '';
+            }
+        } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
+            $tag['content'] = '';
+        }
+
+        if (is_scalar($tag['content']) || is_null($tag['content'])) {
+            if ($this->options['encoding'] == 'UTF-8' &&
+                  version_compare(phpversion(), '5.0.0', 'lt')
+            ) {
+                $tag['content'] = utf8_encode($tag['content']);
+            }
+
+            if ($replaceEntities === true) {
+                $replaceEntities = XML_UTIL_ENTITIES_XML;
+            }
+
+            $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak']);
+        } elseif (is_array($tag['content'])) {
+            $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
+        } elseif (is_object($tag['content'])) {
+            $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
+        } elseif (is_resource($tag['content'])) {
+            settype($tag['content'], 'string');
+            $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
+        }
+        return  $tag;
+    }
+}
Index: trunk/library/PEAR/PEAR/PackageFile/v2/Validator.php
===================================================================
--- trunk/library/PEAR/PEAR/PackageFile/v2/Validator.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/PackageFile/v2/Validator.php	(revision 5146)
@@ -0,0 +1,2154 @@
+<?php
+/**
+ * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Validator.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a8
+ */
+/**
+ * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its
+ * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a8
+ * @access private
+ */
+class PEAR_PackageFile_v2_Validator
+{
+    /**
+     * @var array
+     */
+    var $_packageInfo;
+    /**
+     * @var PEAR_PackageFile_v2
+     */
+    var $_pf;
+    /**
+     * @var PEAR_ErrorStack
+     */
+    var $_stack;
+    /**
+     * @var int
+     */
+    var $_isValid = 0;
+    /**
+     * @var int
+     */
+    var $_filesValid = 0;
+    /**
+     * @var int
+     */
+    var $_curState = 0;
+    /**
+     * @param PEAR_PackageFile_v2
+     * @param int
+     */
+    function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
+    {
+        $this->_pf = &$pf;
+        $this->_curState = $state;
+        $this->_packageInfo = $this->_pf->getArray();
+        $this->_isValid = $this->_pf->_isValid;
+        $this->_filesValid = $this->_pf->_filesValid;
+        $this->_stack = &$pf->_stack;
+        $this->_stack->getErrors(true);
+        if (($this->_isValid & $state) == $state) {
+            return true;
+        }
+        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
+            return false;
+        }
+        if (!isset($this->_packageInfo['attribs']['version']) ||
+              ($this->_packageInfo['attribs']['version'] != '2.0' &&
+               $this->_packageInfo['attribs']['version'] != '2.1')
+        ) {
+            $this->_noPackageVersion();
+        }
+        $structure =
+        array(
+            'name',
+            'channel|uri',
+            '*extends', // can't be multiple, but this works fine
+            'summary',
+            'description',
+            '+lead', // these all need content checks
+            '*developer',
+            '*contributor',
+            '*helper',
+            'date',
+            '*time',
+            'version',
+            'stability',
+            'license->?uri->?filesource',
+            'notes',
+            'contents', //special validation needed
+            '*compatible',
+            'dependencies', //special validation needed
+            '*usesrole',
+            '*usestask', // reserve these for 1.4.0a1 to implement
+                         // this will allow a package.xml to gracefully say it
+                         // needs a certain package installed in order to implement a role or task
+            '*providesextension',
+            '*srcpackage|*srcuri',
+            '+phprelease|+extsrcrelease|+extbinrelease|' .
+                '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed
+            '*changelog',
+        );
+        $test = $this->_packageInfo;
+        if (isset($test['dependencies']) &&
+              isset($test['dependencies']['required']) &&
+              isset($test['dependencies']['required']['pearinstaller']) &&
+              isset($test['dependencies']['required']['pearinstaller']['min']) &&
+              version_compare('1.9.4',
+                $test['dependencies']['required']['pearinstaller']['min'], '<')
+        ) {
+            $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
+            return false;
+        }
+        // ignore post-installation array fields
+        if (array_key_exists('filelist', $test)) {
+            unset($test['filelist']);
+        }
+        if (array_key_exists('_lastmodified', $test)) {
+            unset($test['_lastmodified']);
+        }
+        if (array_key_exists('#binarypackage', $test)) {
+            unset($test['#binarypackage']);
+        }
+        if (array_key_exists('old', $test)) {
+            unset($test['old']);
+        }
+        if (array_key_exists('_lastversion', $test)) {
+            unset($test['_lastversion']);
+        }
+        if (!$this->_stupidSchemaValidate($structure, $test, '<package>')) {
+            return false;
+        }
+        if (empty($this->_packageInfo['name'])) {
+            $this->_tagCannotBeEmpty('name');
+        }
+        $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel';
+        if (empty($this->_packageInfo[$test])) {
+            $this->_tagCannotBeEmpty($test);
+        }
+        if (is_array($this->_packageInfo['license']) &&
+              (!isset($this->_packageInfo['license']['_content']) ||
+              empty($this->_packageInfo['license']['_content']))) {
+            $this->_tagCannotBeEmpty('license');
+        } elseif (empty($this->_packageInfo['license'])) {
+            $this->_tagCannotBeEmpty('license');
+        }
+        if (empty($this->_packageInfo['summary'])) {
+            $this->_tagCannotBeEmpty('summary');
+        }
+        if (empty($this->_packageInfo['description'])) {
+            $this->_tagCannotBeEmpty('description');
+        }
+        if (empty($this->_packageInfo['date'])) {
+            $this->_tagCannotBeEmpty('date');
+        }
+        if (empty($this->_packageInfo['notes'])) {
+            $this->_tagCannotBeEmpty('notes');
+        }
+        if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) {
+            $this->_tagCannotBeEmpty('time');
+        }
+        if (isset($this->_packageInfo['dependencies'])) {
+            $this->_validateDependencies();
+        }
+        if (isset($this->_packageInfo['compatible'])) {
+            $this->_validateCompatible();
+        }
+        if (!isset($this->_packageInfo['bundle'])) {
+            if (empty($this->_packageInfo['contents'])) {
+                $this->_tagCannotBeEmpty('contents');
+            }
+            if (!isset($this->_packageInfo['contents']['dir'])) {
+                $this->_filelistMustContainDir('contents');
+                return false;
+            }
+            if (isset($this->_packageInfo['contents']['file'])) {
+                $this->_filelistCannotContainFile('contents');
+                return false;
+            }
+        }
+        $this->_validateMaintainers();
+        $this->_validateStabilityVersion();
+        $fail = false;
+        if (array_key_exists('usesrole', $this->_packageInfo)) {
+            $roles = $this->_packageInfo['usesrole'];
+            if (!is_array($roles) || !isset($roles[0])) {
+                $roles = array($roles);
+            }
+            foreach ($roles as $role) {
+                if (!isset($role['role'])) {
+                    $this->_usesroletaskMustHaveRoleTask('usesrole', 'role');
+                    $fail = true;
+                } else {
+                    if (!isset($role['channel'])) {
+                        if (!isset($role['uri'])) {
+                            $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole');
+                            $fail = true;
+                        }
+                    } elseif (!isset($role['package'])) {
+                        $this->_usesroletaskMustHavePackage($role['role'], 'usesrole');
+                        $fail = true;
+                    }
+                }
+            }
+        }
+        if (array_key_exists('usestask', $this->_packageInfo)) {
+            $roles = $this->_packageInfo['usestask'];
+            if (!is_array($roles) || !isset($roles[0])) {
+                $roles = array($roles);
+            }
+            foreach ($roles as $role) {
+                if (!isset($role['task'])) {
+                    $this->_usesroletaskMustHaveRoleTask('usestask', 'task');
+                    $fail = true;
+                } else {
+                    if (!isset($role['channel'])) {
+                        if (!isset($role['uri'])) {
+                            $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask');
+                            $fail = true;
+                        }
+                    } elseif (!isset($role['package'])) {
+                        $this->_usesroletaskMustHavePackage($role['task'], 'usestask');
+                        $fail = true;
+                    }
+                }
+            }
+        }
+
+        if ($fail) {
+            return false;
+        }
+
+        $list = $this->_packageInfo['contents'];
+        if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) {
+            $this->_multipleToplevelDirNotAllowed();
+            return $this->_isValid = 0;
+        }
+
+        $this->_validateFilelist();
+        $this->_validateRelease();
+        if (!$this->_stack->hasErrors()) {
+            $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true);
+            if (PEAR::isError($chan)) {
+                $this->_unknownChannel($this->_pf->getChannel());
+            } else {
+                $valpack = $chan->getValidationPackage();
+                // for channel validator packages, always use the default PEAR validator.
+                // otherwise, they can't be installed or packaged
+                $validator = $chan->getValidationObject($this->_pf->getPackage());
+                if (!$validator) {
+                    $this->_stack->push(__FUNCTION__, 'error',
+                        array('channel' => $chan->getName(),
+                              'package' => $this->_pf->getPackage(),
+                              'name'    => $valpack['_content'],
+                              'version' => $valpack['attribs']['version']),
+                        'package "%channel%/%package%" cannot be properly validated without ' .
+                        'validation package "%channel%/%name%-%version%"');
+                    return $this->_isValid = 0;
+                }
+                $validator->setPackageFile($this->_pf);
+                $validator->validate($state);
+                $failures = $validator->getFailures();
+                foreach ($failures['errors'] as $error) {
+                    $this->_stack->push(__FUNCTION__, 'error', $error,
+                        'Channel validator error: field "%field%" - %reason%');
+                }
+                foreach ($failures['warnings'] as $warning) {
+                    $this->_stack->push(__FUNCTION__, 'warning', $warning,
+                        'Channel validator warning: field "%field%" - %reason%');
+                }
+            }
+        }
+
+        $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error');
+        if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) {
+            if ($this->_pf->getPackageType() == 'bundle') {
+                if ($this->_analyzeBundledPackages()) {
+                    $this->_filesValid = $this->_pf->_filesValid = true;
+                } else {
+                    $this->_pf->_isValid = $this->_isValid = 0;
+                }
+            } else {
+                if (!$this->_analyzePhpFiles()) {
+                    $this->_pf->_isValid = $this->_isValid = 0;
+                } else {
+                    $this->_filesValid = $this->_pf->_filesValid = true;
+                }
+            }
+        }
+
+        if ($this->_isValid) {
+            return $this->_pf->_isValid = $this->_isValid = $state;
+        }
+
+        return $this->_pf->_isValid = $this->_isValid = 0;
+    }
+
+    function _stupidSchemaValidate($structure, $xml, $root)
+    {
+        if (!is_array($xml)) {
+            $xml = array();
+        }
+        $keys = array_keys($xml);
+        reset($keys);
+        $key = current($keys);
+        while ($key == 'attribs' || $key == '_contents') {
+            $key = next($keys);
+        }
+        $unfoundtags = $optionaltags = array();
+        $ret = true;
+        $mismatch = false;
+        foreach ($structure as $struc) {
+            if ($key) {
+                $tag = $xml[$key];
+            }
+            $test = $this->_processStructure($struc);
+            if (isset($test['choices'])) {
+                $loose = true;
+                foreach ($test['choices'] as $choice) {
+                    if ($key == $choice['tag']) {
+                        $key = next($keys);
+                        while ($key == 'attribs' || $key == '_contents') {
+                            $key = next($keys);
+                        }
+                        $unfoundtags = $optionaltags = array();
+                        $mismatch = false;
+                        if ($key && $key != $choice['tag'] && isset($choice['multiple'])) {
+                            $unfoundtags[] = $choice['tag'];
+                            $optionaltags[] = $choice['tag'];
+                            if ($key) {
+                                $mismatch = true;
+                            }
+                        }
+                        $ret &= $this->_processAttribs($choice, $tag, $root);
+                        continue 2;
+                    } else {
+                        $unfoundtags[] = $choice['tag'];
+                        $mismatch = true;
+                    }
+                    if (!isset($choice['multiple']) || $choice['multiple'] != '*') {
+                        $loose = false;
+                    } else {
+                        $optionaltags[] = $choice['tag'];
+                    }
+                }
+                if (!$loose) {
+                    $this->_invalidTagOrder($unfoundtags, $key, $root);
+                    return false;
+                }
+            } else {
+                if ($key != $test['tag']) {
+                    if (isset($test['multiple']) && $test['multiple'] != '*') {
+                        $unfoundtags[] = $test['tag'];
+                        $this->_invalidTagOrder($unfoundtags, $key, $root);
+                        return false;
+                    } else {
+                        if ($key) {
+                            $mismatch = true;
+                        }
+                        $unfoundtags[] = $test['tag'];
+                        $optionaltags[] = $test['tag'];
+                    }
+                    if (!isset($test['multiple'])) {
+                        $this->_invalidTagOrder($unfoundtags, $key, $root);
+                        return false;
+                    }
+                    continue;
+                } else {
+                    $unfoundtags = $optionaltags = array();
+                    $mismatch = false;
+                }
+                $key = next($keys);
+                while ($key == 'attribs' || $key == '_contents') {
+                    $key = next($keys);
+                }
+                if ($key && $key != $test['tag'] && isset($test['multiple'])) {
+                    $unfoundtags[] = $test['tag'];
+                    $optionaltags[] = $test['tag'];
+                    $mismatch = true;
+                }
+                $ret &= $this->_processAttribs($test, $tag, $root);
+                continue;
+            }
+        }
+        if (!$mismatch && count($optionaltags)) {
+            // don't error out on any optional tags
+            $unfoundtags = array_diff($unfoundtags, $optionaltags);
+        }
+        if (count($unfoundtags)) {
+            $this->_invalidTagOrder($unfoundtags, $key, $root);
+        } elseif ($key) {
+            // unknown tags
+            $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
+            while ($key = next($keys)) {
+                $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
+            }
+        }
+        return $ret;
+    }
+
+    function _processAttribs($choice, $tag, $context)
+    {
+        if (isset($choice['attribs'])) {
+            if (!is_array($tag)) {
+                $tag = array($tag);
+            }
+            $tags = $tag;
+            if (!isset($tags[0])) {
+                $tags = array($tags);
+            }
+            $ret = true;
+            foreach ($tags as $i => $tag) {
+                if (!is_array($tag) || !isset($tag['attribs'])) {
+                    foreach ($choice['attribs'] as $attrib) {
+                        if ($attrib{0} != '?') {
+                            $ret &= $this->_tagHasNoAttribs($choice['tag'],
+                                $context);
+                            continue 2;
+                        }
+                    }
+                }
+                foreach ($choice['attribs'] as $attrib) {
+                    if ($attrib{0} != '?') {
+                        if (!isset($tag['attribs'][$attrib])) {
+                            $ret &= $this->_tagMissingAttribute($choice['tag'],
+                                $attrib, $context);
+                        }
+                    }
+                }
+            }
+            return $ret;
+        }
+        return true;
+    }
+
+    function _processStructure($key)
+    {
+        $ret = array();
+        if (count($pieces = explode('|', $key)) > 1) {
+            $ret['choices'] = array();
+            foreach ($pieces as $piece) {
+                $ret['choices'][] = $this->_processStructure($piece);
+            }
+            return $ret;
+        }
+        $multi = $key{0};
+        if ($multi == '+' || $multi == '*') {
+            $ret['multiple'] = $key{0};
+            $key = substr($key, 1);
+        }
+        if (count($attrs = explode('->', $key)) > 1) {
+            $ret['tag'] = array_shift($attrs);
+            $ret['attribs'] = $attrs;
+        } else {
+            $ret['tag'] = $key;
+        }
+        return $ret;
+    }
+
+    function _validateStabilityVersion()
+    {
+        $structure = array('release', 'api');
+        $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], '<version>');
+        $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], '<stability>');
+        if ($a) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $this->_packageInfo['version']['release'])) {
+                $this->_invalidVersion('release', $this->_packageInfo['version']['release']);
+            }
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $this->_packageInfo['version']['api'])) {
+                $this->_invalidVersion('api', $this->_packageInfo['version']['api']);
+            }
+            if (!in_array($this->_packageInfo['stability']['release'],
+                  array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) {
+                $this->_invalidState('release', $this->_packageInfo['stability']['release']);
+            }
+            if (!in_array($this->_packageInfo['stability']['api'],
+                  array('devel', 'alpha', 'beta', 'stable'))) {
+                $this->_invalidState('api', $this->_packageInfo['stability']['api']);
+            }
+        }
+    }
+
+    function _validateMaintainers()
+    {
+        $structure =
+            array(
+                'name',
+                'user',
+                'email',
+                'active',
+            );
+        foreach (array('lead', 'developer', 'contributor', 'helper') as $type) {
+            if (!isset($this->_packageInfo[$type])) {
+                continue;
+            }
+            if (isset($this->_packageInfo[$type][0])) {
+                foreach ($this->_packageInfo[$type] as $lead) {
+                    $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>');
+                }
+            } else {
+                $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type],
+                    '<' . $type . '>');
+            }
+        }
+    }
+
+    function _validatePhpDep($dep, $installcondition = false)
+    {
+        $structure = array(
+            'min',
+            '*max',
+            '*exclude',
+        );
+        $type = $installcondition ? '<installcondition><php>' : '<dependencies><required><php>';
+        $this->_stupidSchemaValidate($structure, $dep, $type);
+        if (isset($dep['min'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
+                  $dep['min'])) {
+                $this->_invalidVersion($type . '<min>', $dep['min']);
+            }
+        }
+        if (isset($dep['max'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
+                  $dep['max'])) {
+                $this->_invalidVersion($type . '<max>', $dep['max']);
+            }
+        }
+        if (isset($dep['exclude'])) {
+            if (!is_array($dep['exclude'])) {
+                $dep['exclude'] = array($dep['exclude']);
+            }
+            foreach ($dep['exclude'] as $exclude) {
+                if (!preg_match(
+                     '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
+                     $exclude)) {
+                    $this->_invalidVersion($type . '<exclude>', $exclude);
+                }
+            }
+        }
+    }
+
+    function _validatePearinstallerDep($dep)
+    {
+        $structure = array(
+            'min',
+            '*max',
+            '*recommended',
+            '*exclude',
+        );
+        $this->_stupidSchemaValidate($structure, $dep, '<dependencies><required><pearinstaller>');
+        if (isset($dep['min'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $dep['min'])) {
+                $this->_invalidVersion('<dependencies><required><pearinstaller><min>',
+                    $dep['min']);
+            }
+        }
+        if (isset($dep['max'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $dep['max'])) {
+                $this->_invalidVersion('<dependencies><required><pearinstaller><max>',
+                    $dep['max']);
+            }
+        }
+        if (isset($dep['recommended'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $dep['recommended'])) {
+                $this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
+                    $dep['recommended']);
+            }
+        }
+        if (isset($dep['exclude'])) {
+            if (!is_array($dep['exclude'])) {
+                $dep['exclude'] = array($dep['exclude']);
+            }
+            foreach ($dep['exclude'] as $exclude) {
+                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                      $exclude)) {
+                    $this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
+                        $exclude);
+                }
+            }
+        }
+    }
+
+    function _validatePackageDep($dep, $group, $type = '<package>')
+    {
+        if (isset($dep['uri'])) {
+            if (isset($dep['conflicts'])) {
+                $structure = array(
+                    'name',
+                    'uri',
+                    'conflicts',
+                    '*providesextension',
+                );
+            } else {
+                $structure = array(
+                    'name',
+                    'uri',
+                    '*providesextension',
+                );
+            }
+        } else {
+            if (isset($dep['conflicts'])) {
+                $structure = array(
+                    'name',
+                    'channel',
+                    '*min',
+                    '*max',
+                    '*exclude',
+                    'conflicts',
+                    '*providesextension',
+                );
+            } else {
+                $structure = array(
+                    'name',
+                    'channel',
+                    '*min',
+                    '*max',
+                    '*recommended',
+                    '*exclude',
+                    '*nodefault',
+                    '*providesextension',
+                );
+            }
+        }
+        if (isset($dep['name'])) {
+            $type .= '<name>' . $dep['name'] . '</name>';
+        }
+        $this->_stupidSchemaValidate($structure, $dep, '<dependencies>' . $group . $type);
+        if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) ||
+              isset($dep['recommended']) || isset($dep['exclude']))) {
+            $this->_uriDepsCannotHaveVersioning('<dependencies>' . $group . $type);
+        }
+        if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') {
+            $this->_DepchannelCannotBeUri('<dependencies>' . $group . $type);
+        }
+        if (isset($dep['min'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $dep['min'])) {
+                $this->_invalidVersion('<dependencies>' . $group . $type . '<min>', $dep['min']);
+            }
+        }
+        if (isset($dep['max'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $dep['max'])) {
+                $this->_invalidVersion('<dependencies>' . $group . $type . '<max>', $dep['max']);
+            }
+        }
+        if (isset($dep['recommended'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $dep['recommended'])) {
+                $this->_invalidVersion('<dependencies>' . $group . $type . '<recommended>',
+                    $dep['recommended']);
+            }
+        }
+        if (isset($dep['exclude'])) {
+            if (!is_array($dep['exclude'])) {
+                $dep['exclude'] = array($dep['exclude']);
+            }
+            foreach ($dep['exclude'] as $exclude) {
+                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                      $exclude)) {
+                    $this->_invalidVersion('<dependencies>' . $group . $type . '<exclude>',
+                        $exclude);
+                }
+            }
+        }
+    }
+
+    function _validateSubpackageDep($dep, $group)
+    {
+        $this->_validatePackageDep($dep, $group, '<subpackage>');
+        if (isset($dep['providesextension'])) {
+            $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : '');
+        }
+        if (isset($dep['conflicts'])) {
+            $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : '');
+        }
+    }
+
+    function _validateExtensionDep($dep, $group = false, $installcondition = false)
+    {
+        if (isset($dep['conflicts'])) {
+            $structure = array(
+                'name',
+                '*min',
+                '*max',
+                '*exclude',
+                'conflicts',
+            );
+        } else {
+            $structure = array(
+                'name',
+                '*min',
+                '*max',
+                '*recommended',
+                '*exclude',
+            );
+        }
+        if ($installcondition) {
+            $type = '<installcondition><extension>';
+        } else {
+            $type = '<dependencies>' . $group . '<extension>';
+        }
+        if (isset($dep['name'])) {
+            $type .= '<name>' . $dep['name'] . '</name>';
+        }
+        $this->_stupidSchemaValidate($structure, $dep, $type);
+        if (isset($dep['min'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $dep['min'])) {
+                $this->_invalidVersion(substr($type, 1) . '<min', $dep['min']);
+            }
+        }
+        if (isset($dep['max'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $dep['max'])) {
+                $this->_invalidVersion(substr($type, 1) . '<max', $dep['max']);
+            }
+        }
+        if (isset($dep['recommended'])) {
+            if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                  $dep['recommended'])) {
+                $this->_invalidVersion(substr($type, 1) . '<recommended', $dep['recommended']);
+            }
+        }
+        if (isset($dep['exclude'])) {
+            if (!is_array($dep['exclude'])) {
+                $dep['exclude'] = array($dep['exclude']);
+            }
+            foreach ($dep['exclude'] as $exclude) {
+                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                      $exclude)) {
+                    $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
+                }
+            }
+        }
+    }
+
+    function _validateOsDep($dep, $installcondition = false)
+    {
+        $structure = array(
+            'name',
+            '*conflicts',
+        );
+        $type = $installcondition ? '<installcondition><os>' : '<dependencies><required><os>';
+        if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
+            if ($dep['name'] == '*') {
+                if (array_key_exists('conflicts', $dep)) {
+                    $this->_cannotConflictWithAllOs($type);
+                }
+            }
+        }
+    }
+
+    function _validateArchDep($dep, $installcondition = false)
+    {
+        $structure = array(
+            'pattern',
+            '*conflicts',
+        );
+        $type = $installcondition ? '<installcondition><arch>' : '<dependencies><required><arch>';
+        $this->_stupidSchemaValidate($structure, $dep, $type);
+    }
+
+    function _validateInstallConditions($cond, $release)
+    {
+        $structure = array(
+            '*php',
+            '*extension',
+            '*os',
+            '*arch',
+        );
+        if (!$this->_stupidSchemaValidate($structure,
+              $cond, $release)) {
+            return false;
+        }
+        foreach (array('php', 'extension', 'os', 'arch') as $type) {
+            if (isset($cond[$type])) {
+                $iter = $cond[$type];
+                if (!is_array($iter) || !isset($iter[0])) {
+                    $iter = array($iter);
+                }
+                foreach ($iter as $package) {
+                    if ($type == 'extension') {
+                        $this->{"_validate{$type}Dep"}($package, false, true);
+                    } else {
+                        $this->{"_validate{$type}Dep"}($package, true);
+                    }
+                }
+            }
+        }
+    }
+
+    function _validateDependencies()
+    {
+        $structure = array(
+            'required',
+            '*optional',
+            '*group->name->hint'
+        );
+        if (!$this->_stupidSchemaValidate($structure,
+              $this->_packageInfo['dependencies'], '<dependencies>')) {
+            return false;
+        }
+        foreach (array('required', 'optional') as $simpledep) {
+            if (isset($this->_packageInfo['dependencies'][$simpledep])) {
+                if ($simpledep == 'optional') {
+                    $structure = array(
+                        '*package',
+                        '*subpackage',
+                        '*extension',
+                    );
+                } else {
+                    $structure = array(
+                        'php',
+                        'pearinstaller',
+                        '*package',
+                        '*subpackage',
+                        '*extension',
+                        '*os',
+                        '*arch',
+                    );
+                }
+                if ($this->_stupidSchemaValidate($structure,
+                      $this->_packageInfo['dependencies'][$simpledep],
+                      "<dependencies><$simpledep>")) {
+                    foreach (array('package', 'subpackage', 'extension') as $type) {
+                        if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
+                            $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
+                            if (!isset($iter[0])) {
+                                $iter = array($iter);
+                            }
+                            foreach ($iter as $package) {
+                                if ($type != 'extension') {
+                                    if (isset($package['uri'])) {
+                                        if (isset($package['channel'])) {
+                                            $this->_UrlOrChannel($type,
+                                                $package['name']);
+                                        }
+                                    } else {
+                                        if (!isset($package['channel'])) {
+                                            $this->_NoChannel($type, $package['name']);
+                                        }
+                                    }
+                                }
+                                $this->{"_validate{$type}Dep"}($package, "<$simpledep>");
+                            }
+                        }
+                    }
+                    if ($simpledep == 'optional') {
+                        continue;
+                    }
+                    foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) {
+                        if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
+                            $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
+                            if (!isset($iter[0])) {
+                                $iter = array($iter);
+                            }
+                            foreach ($iter as $package) {
+                                $this->{"_validate{$type}Dep"}($package);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (isset($this->_packageInfo['dependencies']['group'])) {
+            $groups = $this->_packageInfo['dependencies']['group'];
+            if (!isset($groups[0])) {
+                $groups = array($groups);
+            }
+            $structure = array(
+                '*package',
+                '*subpackage',
+                '*extension',
+            );
+            foreach ($groups as $group) {
+                if ($this->_stupidSchemaValidate($structure, $group, '<group>')) {
+                    if (!PEAR_Validate::validGroupName($group['attribs']['name'])) {
+                        $this->_invalidDepGroupName($group['attribs']['name']);
+                    }
+                    foreach (array('package', 'subpackage', 'extension') as $type) {
+                        if (isset($group[$type])) {
+                            $iter = $group[$type];
+                            if (!isset($iter[0])) {
+                                $iter = array($iter);
+                            }
+                            foreach ($iter as $package) {
+                                if ($type != 'extension') {
+                                    if (isset($package['uri'])) {
+                                        if (isset($package['channel'])) {
+                                            $this->_UrlOrChannelGroup($type,
+                                                $package['name'],
+                                                $group['name']);
+                                        }
+                                    } else {
+                                        if (!isset($package['channel'])) {
+                                            $this->_NoChannelGroup($type,
+                                                $package['name'],
+                                                $group['name']);
+                                        }
+                                    }
+                                }
+                                $this->{"_validate{$type}Dep"}($package, '<group name="' .
+                                    $group['attribs']['name'] . '">');
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    function _validateCompatible()
+    {
+        $compat = $this->_packageInfo['compatible'];
+        if (!isset($compat[0])) {
+            $compat = array($compat);
+        }
+        $required = array('name', 'channel', 'min', 'max', '*exclude');
+        foreach ($compat as $package) {
+            $type = '<compatible>';
+            if (is_array($package) && array_key_exists('name', $package)) {
+                $type .= '<name>' . $package['name'] . '</name>';
+            }
+            $this->_stupidSchemaValidate($required, $package, $type);
+            if (is_array($package) && array_key_exists('min', $package)) {
+                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                      $package['min'])) {
+                    $this->_invalidVersion(substr($type, 1) . '<min', $package['min']);
+                }
+            }
+            if (is_array($package) && array_key_exists('max', $package)) {
+                if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                      $package['max'])) {
+                    $this->_invalidVersion(substr($type, 1) . '<max', $package['max']);
+                }
+            }
+            if (is_array($package) && array_key_exists('exclude', $package)) {
+                if (!is_array($package['exclude'])) {
+                    $package['exclude'] = array($package['exclude']);
+                }
+                foreach ($package['exclude'] as $exclude) {
+                    if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
+                          $exclude)) {
+                        $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
+                    }
+                }
+            }
+        }
+    }
+
+    function _validateBundle($list)
+    {
+        if (!is_array($list) || !isset($list['bundledpackage'])) {
+            return $this->_NoBundledPackages();
+        }
+        if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) {
+            return $this->_AtLeast2BundledPackages();
+        }
+        foreach ($list['bundledpackage'] as $package) {
+            if (!is_string($package)) {
+                $this->_bundledPackagesMustBeFilename();
+            }
+        }
+    }
+
+    function _validateFilelist($list = false, $allowignore = false, $dirs = '')
+    {
+        $iscontents = false;
+        if (!$list) {
+            $iscontents = true;
+            $list = $this->_packageInfo['contents'];
+            if (isset($this->_packageInfo['bundle'])) {
+                return $this->_validateBundle($list);
+            }
+        }
+        if ($allowignore) {
+            $struc = array(
+                '*install->name->as',
+                '*ignore->name'
+            );
+        } else {
+            $struc = array(
+                '*dir->name->?baseinstalldir',
+                '*file->name->role->?baseinstalldir->?md5sum'
+            );
+            if (isset($list['dir']) && isset($list['file'])) {
+                // stave off validation errors without requiring a set order.
+                $_old = $list;
+                if (isset($list['attribs'])) {
+                    $list = array('attribs' => $_old['attribs']);
+                }
+                $list['dir'] = $_old['dir'];
+                $list['file'] = $_old['file'];
+            }
+        }
+        if (!isset($list['attribs']) || !isset($list['attribs']['name'])) {
+            $unknown = $allowignore ? '<filelist>' : '<dir name="*unknown*">';
+            $dirname = $iscontents ? '<contents>' : $unknown;
+        } else {
+            $dirname = '<dir name="' . $list['attribs']['name'] . '">';
+            if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
+                          str_replace('\\', '/', $list['attribs']['name']))) {
+                // file contains .. parent directory or . cur directory
+                $this->_invalidDirName($list['attribs']['name']);
+            }
+        }
+        $res = $this->_stupidSchemaValidate($struc, $list, $dirname);
+        if ($allowignore && $res) {
+            $ignored_or_installed = array();
+            $this->_pf->getFilelist();
+            $fcontents = $this->_pf->getContents();
+            $filelist = array();
+            if (!isset($fcontents['dir']['file'][0])) {
+                $fcontents['dir']['file'] = array($fcontents['dir']['file']);
+            }
+            foreach ($fcontents['dir']['file'] as $file) {
+                $filelist[$file['attribs']['name']] = true;
+            }
+            if (isset($list['install'])) {
+                if (!isset($list['install'][0])) {
+                    $list['install'] = array($list['install']);
+                }
+                foreach ($list['install'] as $file) {
+                    if (!isset($filelist[$file['attribs']['name']])) {
+                        $this->_notInContents($file['attribs']['name'], 'install');
+                        continue;
+                    }
+                    if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
+                        $this->_multipleInstallAs($file['attribs']['name']);
+                    }
+                    if (!isset($ignored_or_installed[$file['attribs']['name']])) {
+                        $ignored_or_installed[$file['attribs']['name']] = array();
+                    }
+                    $ignored_or_installed[$file['attribs']['name']][] = 1;
+                    if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
+                                  str_replace('\\', '/', $file['attribs']['as']))) {
+                        // file contains .. parent directory or . cur directory references
+                        $this->_invalidFileInstallAs($file['attribs']['name'],
+                            $file['attribs']['as']);
+                    }
+                }
+            }
+            if (isset($list['ignore'])) {
+                if (!isset($list['ignore'][0])) {
+                    $list['ignore'] = array($list['ignore']);
+                }
+                foreach ($list['ignore'] as $file) {
+                    if (!isset($filelist[$file['attribs']['name']])) {
+                        $this->_notInContents($file['attribs']['name'], 'ignore');
+                        continue;
+                    }
+                    if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
+                        $this->_ignoreAndInstallAs($file['attribs']['name']);
+                    }
+                }
+            }
+        }
+        if (!$allowignore && isset($list['file'])) {
+            if (is_string($list['file'])) {
+                $this->_oldStyleFileNotAllowed();
+                return false;
+            }
+            if (!isset($list['file'][0])) {
+                // single file
+                $list['file'] = array($list['file']);
+            }
+            foreach ($list['file'] as $i => $file)
+            {
+                if (isset($file['attribs']) && isset($file['attribs']['name'])) {
+                    if ($file['attribs']['name']{0} == '.' &&
+                          $file['attribs']['name']{1} == '/') {
+                        // name is something like "./doc/whatever.txt"
+                        $this->_invalidFileName($file['attribs']['name'], $dirname);
+                    }
+                    if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
+                                  str_replace('\\', '/', $file['attribs']['name']))) {
+                        // file contains .. parent directory or . cur directory
+                        $this->_invalidFileName($file['attribs']['name'], $dirname);
+                    }
+                }
+                if (isset($file['attribs']) && isset($file['attribs']['role'])) {
+                    if (!$this->_validateRole($file['attribs']['role'])) {
+                        if (isset($this->_packageInfo['usesrole'])) {
+                            $roles = $this->_packageInfo['usesrole'];
+                            if (!isset($roles[0])) {
+                                $roles = array($roles);
+                            }
+                            foreach ($roles as $role) {
+                                if ($role['role'] = $file['attribs']['role']) {
+                                    $msg = 'This package contains role "%role%" and requires ' .
+                                        'package "%package%" to be used';
+                                    if (isset($role['uri'])) {
+                                        $params = array('role' => $role['role'],
+                                            'package' => $role['uri']);
+                                    } else {
+                                        $params = array('role' => $role['role'],
+                                            'package' => $this->_pf->_registry->
+                                            parsedPackageNameToString(array('package' =>
+                                                $role['package'], 'channel' => $role['channel']),
+                                                true));
+                                    }
+                                    $this->_stack->push('_mustInstallRole', 'error', $params, $msg);
+                                }
+                            }
+                        }
+                        $this->_invalidFileRole($file['attribs']['name'],
+                            $dirname, $file['attribs']['role']);
+                    }
+                }
+                if (!isset($file['attribs'])) {
+                    continue;
+                }
+                $save = $file['attribs'];
+                if ($dirs) {
+                    $save['name'] = $dirs . '/' . $save['name'];
+                }
+                unset($file['attribs']);
+                if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks
+                    foreach ($file as $task => $value) {
+                        if ($tagClass = $this->_pf->getTask($task)) {
+                            if (!is_array($value) || !isset($value[0])) {
+                                $value = array($value);
+                            }
+                            foreach ($value as $v) {
+                                $ret = call_user_func(array($tagClass, 'validateXml'),
+                                    $this->_pf, $v, $this->_pf->_config, $save);
+                                if (is_array($ret)) {
+                                    $this->_invalidTask($task, $ret, isset($save['name']) ?
+                                        $save['name'] : '');
+                                }
+                            }
+                        } else {
+                            if (isset($this->_packageInfo['usestask'])) {
+                                $roles = $this->_packageInfo['usestask'];
+                                if (!isset($roles[0])) {
+                                    $roles = array($roles);
+                                }
+                                foreach ($roles as $role) {
+                                    if ($role['task'] = $task) {
+                                        $msg = 'This package contains task "%task%" and requires ' .
+                                            'package "%package%" to be used';
+                                        if (isset($role['uri'])) {
+                                            $params = array('task' => $role['task'],
+                                                'package' => $role['uri']);
+                                        } else {
+                                            $params = array('task' => $role['task'],
+                                                'package' => $this->_pf->_registry->
+                                                parsedPackageNameToString(array('package' =>
+                                                    $role['package'], 'channel' => $role['channel']),
+                                                    true));
+                                        }
+                                        $this->_stack->push('_mustInstallTask', 'error',
+                                            $params, $msg);
+                                    }
+                                }
+                            }
+                            $this->_unknownTask($task, $save['name']);
+                        }
+                    }
+                }
+            }
+        }
+        if (isset($list['ignore'])) {
+            if (!$allowignore) {
+                $this->_ignoreNotAllowed('ignore');
+            }
+        }
+        if (isset($list['install'])) {
+            if (!$allowignore) {
+                $this->_ignoreNotAllowed('install');
+            }
+        }
+        if (isset($list['file'])) {
+            if ($allowignore) {
+                $this->_fileNotAllowed('file');
+            }
+        }
+        if (isset($list['dir'])) {
+            if ($allowignore) {
+                $this->_fileNotAllowed('dir');
+            } else {
+                if (!isset($list['dir'][0])) {
+                    $list['dir'] = array($list['dir']);
+                }
+                foreach ($list['dir'] as $dir) {
+                    if (isset($dir['attribs']) && isset($dir['attribs']['name'])) {
+                        if ($dir['attribs']['name'] == '/' ||
+                              !isset($this->_packageInfo['contents']['dir']['dir'])) {
+                            // always use nothing if the filelist has already been flattened
+                            $newdirs = '';
+                        } elseif ($dirs == '') {
+                            $newdirs = $dir['attribs']['name'];
+                        } else {
+                            $newdirs = $dirs . '/' . $dir['attribs']['name'];
+                        }
+                    } else {
+                        $newdirs = $dirs;
+                    }
+                    $this->_validateFilelist($dir, $allowignore, $newdirs);
+                }
+            }
+        }
+    }
+
+    function _validateRelease()
+    {
+        if (isset($this->_packageInfo['phprelease'])) {
+            $release = 'phprelease';
+            if (isset($this->_packageInfo['providesextension'])) {
+                $this->_cannotProvideExtension($release);
+            }
+            if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
+                $this->_cannotHaveSrcpackage($release);
+            }
+            $releases = $this->_packageInfo['phprelease'];
+            if (!is_array($releases)) {
+                return true;
+            }
+            if (!isset($releases[0])) {
+                $releases = array($releases);
+            }
+            foreach ($releases as $rel) {
+                $this->_stupidSchemaValidate(array(
+                    '*installconditions',
+                    '*filelist',
+                ), $rel, '<phprelease>');
+            }
+        }
+        foreach (array('', 'zend') as $prefix) {
+            $releasetype = $prefix . 'extsrcrelease';
+            if (isset($this->_packageInfo[$releasetype])) {
+                $release = $releasetype;
+                if (!isset($this->_packageInfo['providesextension'])) {
+                    $this->_mustProvideExtension($release);
+                }
+                if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
+                    $this->_cannotHaveSrcpackage($release);
+                }
+                $releases = $this->_packageInfo[$releasetype];
+                if (!is_array($releases)) {
+                    return true;
+                }
+                if (!isset($releases[0])) {
+                    $releases = array($releases);
+                }
+                foreach ($releases as $rel) {
+                    $this->_stupidSchemaValidate(array(
+                        '*installconditions',
+                        '*configureoption->name->prompt->?default',
+                        '*binarypackage',
+                        '*filelist',
+                    ), $rel, '<' . $releasetype . '>');
+                    if (isset($rel['binarypackage'])) {
+                        if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) {
+                            $rel['binarypackage'] = array($rel['binarypackage']);
+                        }
+                        foreach ($rel['binarypackage'] as $bin) {
+                            if (!is_string($bin)) {
+                                $this->_binaryPackageMustBePackagename();
+                            }
+                        }
+                    }
+                }
+            }
+            $releasetype = 'extbinrelease';
+            if (isset($this->_packageInfo[$releasetype])) {
+                $release = $releasetype;
+                if (!isset($this->_packageInfo['providesextension'])) {
+                    $this->_mustProvideExtension($release);
+                }
+                if (isset($this->_packageInfo['channel']) &&
+                      !isset($this->_packageInfo['srcpackage'])) {
+                    $this->_mustSrcPackage($release);
+                }
+                if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) {
+                    $this->_mustSrcuri($release);
+                }
+                $releases = $this->_packageInfo[$releasetype];
+                if (!is_array($releases)) {
+                    return true;
+                }
+                if (!isset($releases[0])) {
+                    $releases = array($releases);
+                }
+                foreach ($releases as $rel) {
+                    $this->_stupidSchemaValidate(array(
+                        '*installconditions',
+                        '*filelist',
+                    ), $rel, '<' . $releasetype . '>');
+                }
+            }
+        }
+        if (isset($this->_packageInfo['bundle'])) {
+            $release = 'bundle';
+            if (isset($this->_packageInfo['providesextension'])) {
+                $this->_cannotProvideExtension($release);
+            }
+            if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
+                $this->_cannotHaveSrcpackage($release);
+            }
+            $releases = $this->_packageInfo['bundle'];
+            if (!is_array($releases) || !isset($releases[0])) {
+                $releases = array($releases);
+            }
+            foreach ($releases as $rel) {
+                $this->_stupidSchemaValidate(array(
+                    '*installconditions',
+                    '*filelist',
+                ), $rel, '<bundle>');
+            }
+        }
+        foreach ($releases as $rel) {
+            if (is_array($rel) && array_key_exists('installconditions', $rel)) {
+                $this->_validateInstallConditions($rel['installconditions'],
+                    "<$release><installconditions>");
+            }
+            if (is_array($rel) && array_key_exists('filelist', $rel)) {
+                if ($rel['filelist']) {
+
+                    $this->_validateFilelist($rel['filelist'], true);
+                }
+            }
+        }
+    }
+
+    /**
+     * This is here to allow role extension through plugins
+     * @param string
+     */
+    function _validateRole($role)
+    {
+        return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType()));
+    }
+
+    function _pearVersionTooLow($version)
+    {
+        $this->_stack->push(__FUNCTION__, 'error',
+            array('version' => $version),
+            'This package.xml requires PEAR version %version% to parse properly, we are ' .
+            'version 1.9.4');
+    }
+
+    function _invalidTagOrder($oktags, $actual, $root)
+    {
+        $this->_stack->push(__FUNCTION__, 'error',
+            array('oktags' => $oktags, 'actual' => $actual, 'root' => $root),
+            'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"');
+    }
+
+    function _ignoreNotAllowed($type)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
+            '<%type%> is not allowed inside global <contents>, only inside ' .
+            '<phprelease>/<extbinrelease>/<zendextbinrelease>, use <dir> and <file> only');
+    }
+
+    function _fileNotAllowed($type)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
+            '<%type%> is not allowed inside release <filelist>, only inside ' .
+            '<contents>, use <ignore> and <install> only');
+    }
+
+    function _oldStyleFileNotAllowed()
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(),
+            'Old-style <file>name</file> is not allowed.  Use' .
+            '<file name="name" role="role"/>');
+    }
+
+    function _tagMissingAttribute($tag, $attr, $context)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
+            'attribute' => $attr, 'context' => $context),
+            'tag <%tag%> in context "%context%" has no attribute "%attribute%"');
+    }
+
+    function _tagHasNoAttribs($tag, $context)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
+            'context' => $context),
+            'tag <%tag%> has no attributes in context "%context%"');
+    }
+
+    function _invalidInternalStructure()
+    {
+        $this->_stack->push(__FUNCTION__, 'exception', array(),
+            'internal array was not generated by compatible parser, or extreme parser error, cannot continue');
+    }
+
+    function _invalidFileRole($file, $dir, $role)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(
+            'file' => $file, 'dir' => $dir, 'role' => $role,
+            'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())),
+            'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%');
+    }
+
+    function _invalidFileName($file, $dir)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(
+            'file' => $file),
+            'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."');
+    }
+
+    function _invalidFileInstallAs($file, $as)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(
+            'file' => $file, 'as' => $as),
+            'File "%file%" <install as="%as%"/> cannot contain "./" or contain ".."');
+    }
+
+    function _invalidDirName($dir)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(
+            'dir' => $file),
+            'Directory "%dir%" cannot begin with "./" or contain ".."');
+    }
+
+    function _filelistCannotContainFile($filelist)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
+            '<%tag%> can only contain <dir>, contains <file>.  Use ' .
+            '<dir name="/"> as the first dir element');
+    }
+
+    function _filelistMustContainDir($filelist)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
+            '<%tag%> must contain <dir>.  Use <dir name="/"> as the ' .
+            'first dir element');
+    }
+
+    function _tagCannotBeEmpty($tag)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
+            '<%tag%> cannot be empty (<%tag%/>)');
+    }
+
+    function _UrlOrChannel($type, $name)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
+            'name' => $name),
+            'Required dependency <%type%> "%name%" can have either url OR ' .
+            'channel attributes, and not both');
+    }
+
+    function _NoChannel($type, $name)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
+            'name' => $name),
+            'Required dependency <%type%> "%name%" must have either url OR ' .
+            'channel attributes');
+    }
+
+    function _UrlOrChannelGroup($type, $name, $group)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
+            'name' => $name, 'group' => $group),
+            'Group "%group%" dependency <%type%> "%name%" can have either url OR ' .
+            'channel attributes, and not both');
+    }
+
+    function _NoChannelGroup($type, $name, $group)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
+            'name' => $name, 'group' => $group),
+            'Group "%group%" dependency <%type%> "%name%" must have either url OR ' .
+            'channel attributes');
+    }
+
+    function _unknownChannel($channel)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel),
+            'Unknown channel "%channel%"');
+    }
+
+    function _noPackageVersion()
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(),
+            'package.xml <package> tag has no version attribute, or version is not 2.0');
+    }
+
+    function _NoBundledPackages()
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(),
+            'No <bundledpackage> tag was found in <contents>, required for bundle packages');
+    }
+
+    function _AtLeast2BundledPackages()
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(),
+            'At least 2 packages must be bundled in a bundle package');
+    }
+
+    function _ChannelOrUri($name)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
+            'Bundled package "%name%" can have either a uri or a channel, not both');
+    }
+
+    function _noChildTag($child, $tag)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag),
+            'Tag <%tag%> is missing child tag <%child%>');
+    }
+
+    function _invalidVersion($type, $value)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value),
+            'Version type <%type%> is not a valid version (%value%)');
+    }
+
+    function _invalidState($type, $value)
+    {
+        $states = array('stable', 'beta', 'alpha', 'devel');
+        if ($type != 'api') {
+            $states[] = 'snapshot';
+        }
+        if (strtolower($value) == 'rc') {
+            $this->_stack->push(__FUNCTION__, 'error',
+                array('version' => $this->_packageInfo['version']['release']),
+                'RC is not a state, it is a version postfix, try %version%RC1, stability beta');
+        }
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value,
+            'types' => $states),
+            'Stability type <%type%> is not a valid stability (%value%), must be one of ' .
+            '%types%');
+    }
+
+    function _invalidTask($task, $ret, $file)
+    {
+        switch ($ret[0]) {
+            case PEAR_TASK_ERROR_MISSING_ATTRIB :
+                $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file);
+                $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%';
+            break;
+            case PEAR_TASK_ERROR_NOATTRIBS :
+                $info = array('task' => $task, 'file' => $file);
+                $msg = 'task <%task%> has no attributes in file %file%';
+            break;
+            case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE :
+                $info = array('attrib' => $ret[1], 'values' => $ret[3],
+                    'was' => $ret[2], 'task' => $task, 'file' => $file);
+                $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '.
+                    'in file %file%, expecting one of "%values%"';
+            break;
+            case PEAR_TASK_ERROR_INVALID :
+                $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file);
+                $msg = 'task <%task%> in file %file% is invalid because of "%reason%"';
+            break;
+        }
+        $this->_stack->push(__FUNCTION__, 'error', $info, $msg);
+    }
+
+    function _unknownTask($task, $file)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file),
+            'Unknown task "%task%" passed in file <file name="%file%">');
+    }
+
+    function _subpackageCannotProvideExtension($name)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
+            'Subpackage dependency "%name%" cannot use <providesextension>, ' .
+            'only package dependencies can use this tag');
+    }
+
+    function _subpackagesCannotConflict($name)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
+            'Subpackage dependency "%name%" cannot use <conflicts/>, ' .
+            'only package dependencies can use this tag');
+    }
+
+    function _cannotProvideExtension($release)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
+            '<%release%> packages cannot use <providesextension>, only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension');
+    }
+
+    function _mustProvideExtension($release)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
+            '<%release%> packages must use <providesextension> to indicate which PHP extension is provided');
+    }
+
+    function _cannotHaveSrcpackage($release)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
+            '<%release%> packages cannot specify a source code package, only extension binaries may use the <srcpackage> tag');
+    }
+
+    function _mustSrcPackage($release)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
+            '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcpackage>');
+    }
+
+    function _mustSrcuri($release)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
+            '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcuri>');
+    }
+
+    function _uriDepsCannotHaveVersioning($type)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
+            '%type%: dependencies with a <uri> tag cannot have any versioning information');
+    }
+
+    function _conflictingDepsCannotHaveVersioning($type)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
+            '%type%: conflicting dependencies cannot have versioning info, use <exclude> to ' .
+            'exclude specific versions of a dependency');
+    }
+
+    function _DepchannelCannotBeUri($type)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
+            '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' .
+            'dependencies only');
+    }
+
+    function _bundledPackagesMustBeFilename()
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(),
+            '<bundledpackage> tags must contain only the filename of a package release ' .
+            'in the bundle');
+    }
+
+    function _binaryPackageMustBePackagename()
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(),
+            '<binarypackage> tags must contain the name of a package that is ' .
+            'a compiled version of this extsrc/zendextsrc package');
+    }
+
+    function _fileNotFound($file)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
+            'File "%file%" in package.xml does not exist');
+    }
+
+    function _notInContents($file, $tag)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag),
+            '<%tag% name="%file%"> is invalid, file is not in <contents>');
+    }
+
+    function _cannotValidateNoPathSet()
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(),
+            'Cannot validate files, no path to package file is set (use setPackageFile())');
+    }
+
+    function _usesroletaskMustHaveChannelOrUri($role, $tag)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
+            '<%tag%> for role "%role%" must contain either <uri>, or <channel> and <package>');
+    }
+
+    function _usesroletaskMustHavePackage($role, $tag)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
+            '<%tag%> for role "%role%" must contain <package>');
+    }
+
+    function _usesroletaskMustHaveRoleTask($tag, $type)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type),
+            '<%tag%> must contain <%type%> defining the %type% to be used');
+    }
+
+    function _cannotConflictWithAllOs($type)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
+            '%tag% cannot conflict with all OSes');
+    }
+
+    function _invalidDepGroupName($name)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
+            'Invalid dependency group name "%name%"');
+    }
+
+    function _multipleToplevelDirNotAllowed()
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array(),
+            'Multiple top-level <dir> tags are not allowed.  Enclose them ' .
+                'in a <dir name="/">');
+    }
+
+    function _multipleInstallAs($file)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
+            'Only one <install> tag is allowed for file "%file%"');
+    }
+
+    function _ignoreAndInstallAs($file)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
+            'Cannot have both <ignore> and <install> tags for file "%file%"');
+    }
+
+    function _analyzeBundledPackages()
+    {
+        if (!$this->_isValid) {
+            return false;
+        }
+        if (!$this->_pf->getPackageType() == 'bundle') {
+            return false;
+        }
+        if (!isset($this->_pf->_packageFile)) {
+            return false;
+        }
+        $dir_prefix = dirname($this->_pf->_packageFile);
+        $common = new PEAR_Common;
+        $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
+            array($common, 'log');
+        $info = $this->_pf->getContents();
+        $info = $info['bundledpackage'];
+        if (!is_array($info)) {
+            $info = array($info);
+        }
+        $pkg = &new PEAR_PackageFile($this->_pf->_config);
+        foreach ($info as $package) {
+            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) {
+                $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package);
+                $this->_isValid = 0;
+                continue;
+            }
+            call_user_func_array($log, array(1, "Analyzing bundled package $package"));
+            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+            $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package,
+                PEAR_VALIDATE_NORMAL);
+            PEAR::popErrorHandling();
+            if (PEAR::isError($ret)) {
+                call_user_func_array($log, array(0, "ERROR: package $package is not a valid " .
+                    'package'));
+                $inf = $ret->getUserInfo();
+                if (is_array($inf)) {
+                    foreach ($inf as $err) {
+                        call_user_func_array($log, array(1, $err['message']));
+                    }
+                }
+                return false;
+            }
+        }
+        return true;
+    }
+
+    function _analyzePhpFiles()
+    {
+        if (!$this->_isValid) {
+            return false;
+        }
+        if (!isset($this->_pf->_packageFile)) {
+            $this->_cannotValidateNoPathSet();
+            return false;
+        }
+        $dir_prefix = dirname($this->_pf->_packageFile);
+        $common = new PEAR_Common;
+        $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
+            array(&$common, 'log');
+        $info = $this->_pf->getContents();
+        if (!$info || !isset($info['dir']['file'])) {
+            $this->_tagCannotBeEmpty('contents><dir');
+            return false;
+        }
+        $info = $info['dir']['file'];
+        if (isset($info['attribs'])) {
+            $info = array($info);
+        }
+        $provides = array();
+        foreach ($info as $fa) {
+            $fa = $fa['attribs'];
+            $file = $fa['name'];
+            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
+                $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file);
+                $this->_isValid = 0;
+                continue;
+            }
+            if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) {
+                call_user_func_array($log, array(1, "Analyzing $file"));
+                $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
+                if ($srcinfo) {
+                    $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo));
+                }
+            }
+        }
+        $this->_packageName = $pn = $this->_pf->getPackage();
+        $pnl = strlen($pn);
+        foreach ($provides as $key => $what) {
+            if (isset($what['explicit']) || !$what) {
+                // skip conformance checks if the provides entry is
+                // specified in the package.xml file
+                continue;
+            }
+            extract($what);
+            if ($type == 'class') {
+                if (!strncasecmp($name, $pn, $pnl)) {
+                    continue;
+                }
+                $this->_stack->push(__FUNCTION__, 'warning',
+                    array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
+                    'in %file%: %type% "%name%" not prefixed with package name "%package%"');
+            } elseif ($type == 'function') {
+                if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
+                    continue;
+                }
+                $this->_stack->push(__FUNCTION__, 'warning',
+                    array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
+                    'in %file%: %type% "%name%" not prefixed with package name "%package%"');
+            }
+        }
+        return $this->_isValid;
+    }
+
+    /**
+     * Analyze the source code of the given PHP file
+     *
+     * @param  string Filename of the PHP file
+     * @param  boolean whether to analyze $file as the file contents
+     * @return mixed
+     */
+    function analyzeSourceCode($file, $string = false)
+    {
+        if (!function_exists("token_get_all")) {
+            $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
+                'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer');
+            return false;
+        }
+
+        if (!defined('T_DOC_COMMENT')) {
+            define('T_DOC_COMMENT', T_COMMENT);
+        }
+
+        if (!defined('T_INTERFACE')) {
+            define('T_INTERFACE', -1);
+        }
+
+        if (!defined('T_IMPLEMENTS')) {
+            define('T_IMPLEMENTS', -1);
+        }
+
+        if ($string) {
+            $contents = $file;
+        } else {
+            if (!$fp = @fopen($file, "r")) {
+                return false;
+            }
+            fclose($fp);
+            $contents = file_get_contents($file);
+        }
+
+        // Silence this function so we can catch PHP Warnings and show our own custom message
+        $tokens = @token_get_all($contents);
+        if (isset($php_errormsg)) {
+            if (isset($this->_stack)) {
+                $pn = $this->_pf->getPackage();
+                $this->_stack->push(__FUNCTION__, 'warning',
+                        array('file' => $file, 'package' => $pn),
+                        'in %file%: Could not process file for unkown reasons,' .
+                        ' possibly a PHP parse error in %file% from %package%');
+            }
+        }
+/*
+        for ($i = 0; $i < sizeof($tokens); $i++) {
+            @list($token, $data) = $tokens[$i];
+            if (is_string($token)) {
+                var_dump($token);
+            } else {
+                print token_name($token) . ' ';
+                var_dump(rtrim($data));
+            }
+        }
+*/
+        $look_for = 0;
+        $paren_level = 0;
+        $bracket_level = 0;
+        $brace_level = 0;
+        $lastphpdoc = '';
+        $current_class = '';
+        $current_interface = '';
+        $current_class_level = -1;
+        $current_function = '';
+        $current_function_level = -1;
+        $declared_classes = array();
+        $declared_interfaces = array();
+        $declared_functions = array();
+        $declared_methods = array();
+        $used_classes = array();
+        $used_functions = array();
+        $extends = array();
+        $implements = array();
+        $nodeps = array();
+        $inquote = false;
+        $interface = false;
+        for ($i = 0; $i < sizeof($tokens); $i++) {
+            if (is_array($tokens[$i])) {
+                list($token, $data) = $tokens[$i];
+            } else {
+                $token = $tokens[$i];
+                $data = '';
+            }
+
+            if ($inquote) {
+                if ($token != '"' && $token != T_END_HEREDOC) {
+                    continue;
+                } else {
+                    $inquote = false;
+                    continue;
+                }
+            }
+
+            switch ($token) {
+                case T_WHITESPACE :
+                    continue;
+                case ';':
+                    if ($interface) {
+                        $current_function = '';
+                        $current_function_level = -1;
+                    }
+                    break;
+                case '"':
+                case T_START_HEREDOC:
+                    $inquote = true;
+                    break;
+                case T_CURLY_OPEN:
+                case T_DOLLAR_OPEN_CURLY_BRACES:
+                case '{': $brace_level++; continue 2;
+                case '}':
+                    $brace_level--;
+                    if ($current_class_level == $brace_level) {
+                        $current_class = '';
+                        $current_class_level = -1;
+                    }
+                    if ($current_function_level == $brace_level) {
+                        $current_function = '';
+                        $current_function_level = -1;
+                    }
+                    continue 2;
+                case '[': $bracket_level++; continue 2;
+                case ']': $bracket_level--; continue 2;
+                case '(': $paren_level++;   continue 2;
+                case ')': $paren_level--;   continue 2;
+                case T_INTERFACE:
+                    $interface = true;
+                case T_CLASS:
+                    if (($current_class_level != -1) || ($current_function_level != -1)) {
+                        if (isset($this->_stack)) {
+                            $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
+                            'Parser error: invalid PHP found in file "%file%"');
+                        } else {
+                            PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
+                                PEAR_COMMON_ERROR_INVALIDPHP);
+                        }
+
+                        return false;
+                    }
+                case T_FUNCTION:
+                case T_NEW:
+                case T_EXTENDS:
+                case T_IMPLEMENTS:
+                    $look_for = $token;
+                    continue 2;
+                case T_STRING:
+                    if (version_compare(zend_version(), '2.0', '<')) {
+                        if (in_array(strtolower($data),
+                            array('public', 'private', 'protected', 'abstract',
+                                  'interface', 'implements', 'throw')
+                                 )
+                        ) {
+                            if (isset($this->_stack)) {
+                                $this->_stack->push(__FUNCTION__, 'warning', array(
+                                    'file' => $file),
+                                    'Error, PHP5 token encountered in %file%,' .
+                                    ' analysis should be in PHP5');
+                            } else {
+                                PEAR::raiseError('Error: PHP5 token encountered in ' . $file .
+                                    'packaging should be done in PHP 5');
+                                return false;
+                            }
+                        }
+                    }
+
+                    if ($look_for == T_CLASS) {
+                        $current_class = $data;
+                        $current_class_level = $brace_level;
+                        $declared_classes[] = $current_class;
+                    } elseif ($look_for == T_INTERFACE) {
+                        $current_interface = $data;
+                        $current_class_level = $brace_level;
+                        $declared_interfaces[] = $current_interface;
+                    } elseif ($look_for == T_IMPLEMENTS) {
+                        $implements[$current_class] = $data;
+                    } elseif ($look_for == T_EXTENDS) {
+                        $extends[$current_class] = $data;
+                    } elseif ($look_for == T_FUNCTION) {
+                        if ($current_class) {
+                            $current_function = "$current_class::$data";
+                            $declared_methods[$current_class][] = $data;
+                        } elseif ($current_interface) {
+                            $current_function = "$current_interface::$data";
+                            $declared_methods[$current_interface][] = $data;
+                        } else {
+                            $current_function = $data;
+                            $declared_functions[] = $current_function;
+                        }
+
+                        $current_function_level = $brace_level;
+                        $m = array();
+                    } elseif ($look_for == T_NEW) {
+                        $used_classes[$data] = true;
+                    }
+
+                    $look_for = 0;
+                    continue 2;
+                case T_VARIABLE:
+                    $look_for = 0;
+                    continue 2;
+                case T_DOC_COMMENT:
+                case T_COMMENT:
+                    if (preg_match('!^/\*\*\s!', $data)) {
+                        $lastphpdoc = $data;
+                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
+                            $nodeps = array_merge($nodeps, $m[1]);
+                        }
+                    }
+                    continue 2;
+                case T_DOUBLE_COLON:
+                    $token = $tokens[$i - 1][0];
+                    if (!($token == T_WHITESPACE || $token == T_STRING || $token == T_STATIC)) {
+                        if (isset($this->_stack)) {
+                            $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file),
+                                'Parser error: invalid PHP found in file "%file%"');
+                        } else {
+                            PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
+                                PEAR_COMMON_ERROR_INVALIDPHP);
+                        }
+
+                        return false;
+                    }
+
+                    $class = $tokens[$i - 1][1];
+                    if (strtolower($class) != 'parent') {
+                        $used_classes[$class] = true;
+                    }
+
+                    continue 2;
+            }
+        }
+
+        return array(
+            "source_file" => $file,
+            "declared_classes" => $declared_classes,
+            "declared_interfaces" => $declared_interfaces,
+            "declared_methods" => $declared_methods,
+            "declared_functions" => $declared_functions,
+            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
+            "inheritance" => $extends,
+            "implements" => $implements,
+        );
+    }
+
+    /**
+     * Build a "provides" array from data returned by
+     * analyzeSourceCode().  The format of the built array is like
+     * this:
+     *
+     *  array(
+     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
+     *    ...
+     *  )
+     *
+     *
+     * @param array $srcinfo array with information about a source file
+     * as returned by the analyzeSourceCode() method.
+     *
+     * @return void
+     *
+     * @access private
+     *
+     */
+    function _buildProvidesArray($srcinfo)
+    {
+        if (!$this->_isValid) {
+            return array();
+        }
+
+        $providesret = array();
+        $file        = basename($srcinfo['source_file']);
+        $pn          = isset($this->_pf) ? $this->_pf->getPackage() : '';
+        $pnl         = strlen($pn);
+        foreach ($srcinfo['declared_classes'] as $class) {
+            $key = "class;$class";
+            if (isset($providesret[$key])) {
+                continue;
+            }
+
+            $providesret[$key] =
+                array('file'=> $file, 'type' => 'class', 'name' => $class);
+            if (isset($srcinfo['inheritance'][$class])) {
+                $providesret[$key]['extends'] =
+                    $srcinfo['inheritance'][$class];
+            }
+        }
+
+        foreach ($srcinfo['declared_methods'] as $class => $methods) {
+            foreach ($methods as $method) {
+                $function = "$class::$method";
+                $key = "function;$function";
+                if ($method{0} == '_' || !strcasecmp($method, $class) ||
+                    isset($providesret[$key])) {
+                    continue;
+                }
+
+                $providesret[$key] =
+                    array('file'=> $file, 'type' => 'function', 'name' => $function);
+            }
+        }
+
+        foreach ($srcinfo['declared_functions'] as $function) {
+            $key = "function;$function";
+            if ($function{0} == '_' || isset($providesret[$key])) {
+                continue;
+            }
+
+            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
+                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
+            }
+
+            $providesret[$key] =
+                array('file'=> $file, 'type' => 'function', 'name' => $function);
+        }
+
+        return $providesret;
+    }
+}
Index: trunk/library/PEAR/PEAR/PackageFile/v2/rw.php
===================================================================
--- trunk/library/PEAR/PEAR/PackageFile/v2/rw.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/PackageFile/v2/rw.php	(revision 5146)
@@ -0,0 +1,1604 @@
+<?php
+/**
+ * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a8
+ */
+/**
+ * For base class
+ */
+require_once 'PEAR/PackageFile/v2.php';
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a8
+ */
+class PEAR_PackageFile_v2_rw extends PEAR_PackageFile_v2
+{
+    /**
+     * @param string Extension name
+     * @return bool success of operation
+     */
+    function setProvidesExtension($extension)
+    {
+        if (in_array($this->getPackageType(),
+              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
+            if (!isset($this->_packageInfo['providesextension'])) {
+                // ensure that the channel tag is set up in the right location
+                $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                    array('usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
+                    'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                    'bundle', 'changelog'),
+                    $extension, 'providesextension');
+            }
+            $this->_packageInfo['providesextension'] = $extension;
+            return true;
+        }
+        return false;
+    }
+
+    function setPackage($package)
+    {
+        $this->_isValid = 0;
+        if (!isset($this->_packageInfo['attribs'])) {
+            $this->_packageInfo = array_merge(array('attribs' => array(
+                                 'version' => '2.0',
+                                 'xmlns' => 'http://pear.php.net/dtd/package-2.0',
+                                 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
+                                 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
+                                 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
+    http://pear.php.net/dtd/tasks-1.0.xsd
+    http://pear.php.net/dtd/package-2.0
+    http://pear.php.net/dtd/package-2.0.xsd',
+                             )), $this->_packageInfo);
+        }
+        if (!isset($this->_packageInfo['name'])) {
+            return $this->_packageInfo = array_merge(array('name' => $package),
+                $this->_packageInfo);
+        }
+        $this->_packageInfo['name'] = $package;
+    }
+
+    /**
+     * set this as a package.xml version 2.1
+     * @access private
+     */
+    function _setPackageVersion2_1()
+    {
+        $info = array(
+                                 'version' => '2.1',
+                                 'xmlns' => 'http://pear.php.net/dtd/package-2.1',
+                                 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
+                                 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
+                                 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
+    http://pear.php.net/dtd/tasks-1.0.xsd
+    http://pear.php.net/dtd/package-2.1
+    http://pear.php.net/dtd/package-2.1.xsd',
+                             );
+        if (!isset($this->_packageInfo['attribs'])) {
+            $this->_packageInfo = array_merge(array('attribs' => $info), $this->_packageInfo);
+        } else {
+            $this->_packageInfo['attribs'] = $info;
+        }
+    }
+
+    function setUri($uri)
+    {
+        unset($this->_packageInfo['channel']);
+        $this->_isValid = 0;
+        if (!isset($this->_packageInfo['uri'])) {
+            // ensure that the uri tag is set up in the right location
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                array('extends', 'summary', 'description', 'lead',
+                'developer', 'contributor', 'helper', 'date', 'time', 'version',
+                'stability', 'license', 'notes', 'contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'), $uri, 'uri');
+        }
+        $this->_packageInfo['uri'] = $uri;
+    }
+
+    function setChannel($channel)
+    {
+        unset($this->_packageInfo['uri']);
+        $this->_isValid = 0;
+        if (!isset($this->_packageInfo['channel'])) {
+            // ensure that the channel tag is set up in the right location
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                array('extends', 'summary', 'description', 'lead',
+                'developer', 'contributor', 'helper', 'date', 'time', 'version',
+                'stability', 'license', 'notes', 'contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'), $channel, 'channel');
+        }
+        $this->_packageInfo['channel'] = $channel;
+    }
+
+    function setExtends($extends)
+    {
+        $this->_isValid = 0;
+        if (!isset($this->_packageInfo['extends'])) {
+            // ensure that the extends tag is set up in the right location
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                array('summary', 'description', 'lead',
+                'developer', 'contributor', 'helper', 'date', 'time', 'version',
+                'stability', 'license', 'notes', 'contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'), $extends, 'extends');
+        }
+        $this->_packageInfo['extends'] = $extends;
+    }
+
+    function setSummary($summary)
+    {
+        $this->_isValid = 0;
+        if (!isset($this->_packageInfo['summary'])) {
+            // ensure that the summary tag is set up in the right location
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                array('description', 'lead',
+                'developer', 'contributor', 'helper', 'date', 'time', 'version',
+                'stability', 'license', 'notes', 'contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'), $summary, 'summary');
+        }
+        $this->_packageInfo['summary'] = $summary;
+    }
+
+    function setDescription($desc)
+    {
+        $this->_isValid = 0;
+        if (!isset($this->_packageInfo['description'])) {
+            // ensure that the description tag is set up in the right location
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                array('lead',
+                'developer', 'contributor', 'helper', 'date', 'time', 'version',
+                'stability', 'license', 'notes', 'contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'), $desc, 'description');
+        }
+        $this->_packageInfo['description'] = $desc;
+    }
+
+    /**
+     * Adds a new maintainer - no checking of duplicates is performed, use
+     * updatemaintainer for that purpose.
+     */
+    function addMaintainer($role, $handle, $name, $email, $active = 'yes')
+    {
+        if (!in_array($role, array('lead', 'developer', 'contributor', 'helper'))) {
+            return false;
+        }
+        if (isset($this->_packageInfo[$role])) {
+            if (!isset($this->_packageInfo[$role][0])) {
+                $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
+            }
+            $this->_packageInfo[$role][] =
+                array(
+                    'name' => $name,
+                    'user' => $handle,
+                    'email' => $email,
+                    'active' => $active,
+                );
+        } else {
+            $testarr = array('lead',
+                    'developer', 'contributor', 'helper', 'date', 'time', 'version',
+                    'stability', 'license', 'notes', 'contents', 'compatible',
+                    'dependencies', 'providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
+                    'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog');
+            foreach (array('lead', 'developer', 'contributor', 'helper') as $testrole) {
+                array_shift($testarr);
+                if ($role == $testrole) {
+                    break;
+                }
+            }
+            if (!isset($this->_packageInfo[$role])) {
+                // ensure that the extends tag is set up in the right location
+                $this->_packageInfo = $this->_insertBefore($this->_packageInfo, $testarr,
+                    array(), $role);
+            }
+            $this->_packageInfo[$role] =
+                array(
+                    'name' => $name,
+                    'user' => $handle,
+                    'email' => $email,
+                    'active' => $active,
+                );
+        }
+        $this->_isValid = 0;
+    }
+
+    function updateMaintainer($newrole, $handle, $name, $email, $active = 'yes')
+    {
+        $found = false;
+        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
+            if (!isset($this->_packageInfo[$role])) {
+                continue;
+            }
+            $info = $this->_packageInfo[$role];
+            if (!isset($info[0])) {
+                if ($info['user'] == $handle) {
+                    $found = true;
+                    break;
+                }
+            }
+            foreach ($info as $i => $maintainer) {
+                if ($maintainer['user'] == $handle) {
+                    $found = $i;
+                    break 2;
+                }
+            }
+        }
+        if ($found === false) {
+            return $this->addMaintainer($newrole, $handle, $name, $email, $active);
+        }
+        if ($found !== false) {
+            if ($found === true) {
+                unset($this->_packageInfo[$role]);
+            } else {
+                unset($this->_packageInfo[$role][$found]);
+                $this->_packageInfo[$role] = array_values($this->_packageInfo[$role]);
+            }
+        }
+        $this->addMaintainer($newrole, $handle, $name, $email, $active);
+        $this->_isValid = 0;
+    }
+
+    function deleteMaintainer($handle)
+    {
+        $found = false;
+        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
+            if (!isset($this->_packageInfo[$role])) {
+                continue;
+            }
+            if (!isset($this->_packageInfo[$role][0])) {
+                $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
+            }
+            foreach ($this->_packageInfo[$role] as $i => $maintainer) {
+                if ($maintainer['user'] == $handle) {
+                    $found = $i;
+                    break;
+                }
+            }
+            if ($found !== false) {
+                unset($this->_packageInfo[$role][$found]);
+                if (!count($this->_packageInfo[$role]) && $role == 'lead') {
+                    $this->_isValid = 0;
+                }
+                if (!count($this->_packageInfo[$role])) {
+                    unset($this->_packageInfo[$role]);
+                    return true;
+                }
+                $this->_packageInfo[$role] =
+                    array_values($this->_packageInfo[$role]);
+                if (count($this->_packageInfo[$role]) == 1) {
+                    $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
+                }
+                return true;
+            }
+            if (count($this->_packageInfo[$role]) == 1) {
+                $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
+            }
+        }
+        return false;
+    }
+
+    function setReleaseVersion($version)
+    {
+        if (isset($this->_packageInfo['version']) &&
+              isset($this->_packageInfo['version']['release'])) {
+            unset($this->_packageInfo['version']['release']);
+        }
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
+            'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'),
+            'release' => array('api')));
+        $this->_isValid = 0;
+    }
+
+    function setAPIVersion($version)
+    {
+        if (isset($this->_packageInfo['version']) &&
+              isset($this->_packageInfo['version']['api'])) {
+            unset($this->_packageInfo['version']['api']);
+        }
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
+            'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'),
+            'api' => array()));
+        $this->_isValid = 0;
+    }
+
+    /**
+     * snapshot|devel|alpha|beta|stable
+     */
+    function setReleaseStability($state)
+    {
+        if (isset($this->_packageInfo['stability']) &&
+              isset($this->_packageInfo['stability']['release'])) {
+            unset($this->_packageInfo['stability']['release']);
+        }
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
+            'stability' => array('license', 'notes', 'contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'),
+            'release' => array('api')));
+        $this->_isValid = 0;
+    }
+
+    /**
+     * @param devel|alpha|beta|stable
+     */
+    function setAPIStability($state)
+    {
+        if (isset($this->_packageInfo['stability']) &&
+              isset($this->_packageInfo['stability']['api'])) {
+            unset($this->_packageInfo['stability']['api']);
+        }
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
+            'stability' => array('license', 'notes', 'contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'),
+            'api' => array()));
+        $this->_isValid = 0;
+    }
+
+    function setLicense($license, $uri = false, $filesource = false)
+    {
+        if (!isset($this->_packageInfo['license'])) {
+            // ensure that the license tag is set up in the right location
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                array('notes', 'contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'), 0, 'license');
+        }
+        if ($uri || $filesource) {
+            $attribs = array();
+            if ($uri) {
+                $attribs['uri'] = $uri;
+            }
+            $uri = true; // for test below
+            if ($filesource) {
+                $attribs['filesource'] = $filesource;
+            }
+        }
+        $license = $uri ? array('attribs' => $attribs, '_content' => $license) : $license;
+        $this->_packageInfo['license'] = $license;
+        $this->_isValid = 0;
+    }
+
+    function setNotes($notes)
+    {
+        $this->_isValid = 0;
+        if (!isset($this->_packageInfo['notes'])) {
+            // ensure that the notes tag is set up in the right location
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                array('contents', 'compatible',
+                'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
+                'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'extbinrelease', 'bundle', 'changelog'), $notes, 'notes');
+        }
+        $this->_packageInfo['notes'] = $notes;
+    }
+
+    /**
+     * This is only used at install-time, after all serialization
+     * is over.
+     * @param string file name
+     * @param string installed path
+     */
+    function setInstalledAs($file, $path)
+    {
+        if ($path) {
+            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
+        }
+        unset($this->_packageInfo['filelist'][$file]['installed_as']);
+    }
+
+    /**
+     * This is only used at install-time, after all serialization
+     * is over.
+     */
+    function installedFile($file, $atts)
+    {
+        if (isset($this->_packageInfo['filelist'][$file])) {
+            $this->_packageInfo['filelist'][$file] =
+                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
+        } else {
+            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
+        }
+    }
+
+    /**
+     * Reset the listing of package contents
+     * @param string base installation dir for the whole package, if any
+     */
+    function clearContents($baseinstall = false)
+    {
+        $this->_filesValid = false;
+        $this->_isValid = 0;
+        if (!isset($this->_packageInfo['contents'])) {
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                array('compatible',
+                    'dependencies', 'providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
+                    'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                    'bundle', 'changelog'), array(), 'contents');
+        }
+        if ($this->getPackageType() != 'bundle') {
+            $this->_packageInfo['contents'] =
+                array('dir' => array('attribs' => array('name' => '/')));
+            if ($baseinstall) {
+                $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'] = $baseinstall;
+            }
+        } else {
+            $this->_packageInfo['contents'] = array('bundledpackage' => array());
+        }
+    }
+
+    /**
+     * @param string relative path of the bundled package.
+     */
+    function addBundledPackage($path)
+    {
+        if ($this->getPackageType() != 'bundle') {
+            return false;
+        }
+        $this->_filesValid = false;
+        $this->_isValid = 0;
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $path, array(
+                'contents' => array('compatible', 'dependencies', 'providesextension',
+                'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
+                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'bundle', 'changelog'),
+                'bundledpackage' => array()));
+    }
+
+    /**
+     * @param string file name
+     * @param PEAR_Task_Common a read/write task
+     */
+    function addTaskToFile($filename, $task)
+    {
+        if (!method_exists($task, 'getXml')) {
+            return false;
+        }
+        if (!method_exists($task, 'getName')) {
+            return false;
+        }
+        if (!method_exists($task, 'validate')) {
+            return false;
+        }
+        if (!$task->validate()) {
+            return false;
+        }
+        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
+            return false;
+        }
+        $this->getTasksNs(); // discover the tasks namespace if not done already
+        $files = $this->_packageInfo['contents']['dir']['file'];
+        if (!isset($files[0])) {
+            $files = array($files);
+            $ind = false;
+        } else {
+            $ind = true;
+        }
+        foreach ($files as $i => $file) {
+            if (isset($file['attribs'])) {
+                if ($file['attribs']['name'] == $filename) {
+                    if ($ind) {
+                        $t = isset($this->_packageInfo['contents']['dir']['file'][$i]
+                              ['attribs'][$this->_tasksNs .
+                              ':' . $task->getName()]) ?
+                              $this->_packageInfo['contents']['dir']['file'][$i]
+                              ['attribs'][$this->_tasksNs .
+                              ':' . $task->getName()] : false;
+                        if ($t && !isset($t[0])) {
+                            $this->_packageInfo['contents']['dir']['file'][$i]
+                                [$this->_tasksNs . ':' . $task->getName()] = array($t);
+                        }
+                        $this->_packageInfo['contents']['dir']['file'][$i][$this->_tasksNs .
+                            ':' . $task->getName()][] = $task->getXml();
+                    } else {
+                        $t = isset($this->_packageInfo['contents']['dir']['file']
+                              ['attribs'][$this->_tasksNs .
+                              ':' . $task->getName()]) ? $this->_packageInfo['contents']['dir']['file']
+                              ['attribs'][$this->_tasksNs .
+                              ':' . $task->getName()] : false;
+                        if ($t && !isset($t[0])) {
+                            $this->_packageInfo['contents']['dir']['file']
+                                [$this->_tasksNs . ':' . $task->getName()] = array($t);
+                        }
+                        $this->_packageInfo['contents']['dir']['file'][$this->_tasksNs .
+                            ':' . $task->getName()][] = $task->getXml();
+                    }
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @param string path to the file
+     * @param string filename
+     * @param array extra attributes
+     */
+    function addFile($dir, $file, $attrs)
+    {
+        if ($this->getPackageType() == 'bundle') {
+            return false;
+        }
+        $this->_filesValid = false;
+        $this->_isValid = 0;
+        $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
+        if ($dir == '/' || $dir == '') {
+            $dir = '';
+        } else {
+            $dir .= '/';
+        }
+        $attrs['name'] = $dir . $file;
+        if (!isset($this->_packageInfo['contents'])) {
+            // ensure that the contents tag is set up
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask',
+                'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
+                'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'bundle', 'changelog'), array(), 'contents');
+        }
+        if (isset($this->_packageInfo['contents']['dir']['file'])) {
+            if (!isset($this->_packageInfo['contents']['dir']['file'][0])) {
+                $this->_packageInfo['contents']['dir']['file'] =
+                    array($this->_packageInfo['contents']['dir']['file']);
+            }
+            $this->_packageInfo['contents']['dir']['file'][]['attribs'] = $attrs;
+        } else {
+            $this->_packageInfo['contents']['dir']['file']['attribs'] = $attrs;
+        }
+    }
+
+    /**
+     * @param string Dependent package name
+     * @param string Dependent package's channel name
+     * @param string minimum version of specified package that this release is guaranteed to be
+     *               compatible with
+     * @param string maximum version of specified package that this release is guaranteed to be
+     *               compatible with
+     * @param string versions of specified package that this release is not compatible with
+     */
+    function addCompatiblePackage($name, $channel, $min, $max, $exclude = false)
+    {
+        $this->_isValid = 0;
+        $set = array(
+            'name' => $name,
+            'channel' => $channel,
+            'min' => $min,
+            'max' => $max,
+        );
+        if ($exclude) {
+            $set['exclude'] = $exclude;
+        }
+        $this->_isValid = 0;
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
+                'compatible' => array('dependencies', 'providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
+            ));
+    }
+
+    /**
+     * Removes the <usesrole> tag entirely
+     */
+    function resetUsesrole()
+    {
+        if (isset($this->_packageInfo['usesrole'])) {
+            unset($this->_packageInfo['usesrole']);
+        }
+    }
+
+    /**
+     * @param string
+     * @param string package name or uri
+     * @param string channel name if non-uri
+     */
+    function addUsesrole($role, $packageOrUri, $channel = false) {
+        $set = array('role' => $role);
+        if ($channel) {
+            $set['package'] = $packageOrUri;
+            $set['channel'] = $channel;
+        } else {
+            $set['uri'] = $packageOrUri;
+        }
+        $this->_isValid = 0;
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
+                'usesrole' => array('usestask', 'srcpackage', 'srcuri',
+                    'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
+            ));
+    }
+
+    /**
+     * Removes the <usestask> tag entirely
+     */
+    function resetUsestask()
+    {
+        if (isset($this->_packageInfo['usestask'])) {
+            unset($this->_packageInfo['usestask']);
+        }
+    }
+
+
+    /**
+     * @param string
+     * @param string package name or uri
+     * @param string channel name if non-uri
+     */
+    function addUsestask($task, $packageOrUri, $channel = false) {
+        $set = array('task' => $task);
+        if ($channel) {
+            $set['package'] = $packageOrUri;
+            $set['channel'] = $channel;
+        } else {
+            $set['uri'] = $packageOrUri;
+        }
+        $this->_isValid = 0;
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
+                'usestask' => array('srcpackage', 'srcuri',
+                    'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
+            ));
+    }
+
+    /**
+     * Remove all compatible tags
+     */
+    function clearCompatible()
+    {
+        unset($this->_packageInfo['compatible']);
+    }
+
+    /**
+     * Reset dependencies prior to adding new ones
+     */
+    function clearDeps()
+    {
+        if (!isset($this->_packageInfo['dependencies'])) {
+            $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
+                array(
+                    'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                        'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                        'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')));
+        }
+        $this->_packageInfo['dependencies'] = array();
+    }
+
+    /**
+     * @param string minimum PHP version allowed
+     * @param string maximum PHP version allowed
+     * @param array $exclude incompatible PHP versions
+     */
+    function setPhpDep($min, $max = false, $exclude = false)
+    {
+        $this->_isValid = 0;
+        $dep =
+            array(
+                'min' => $min,
+            );
+        if ($max) {
+            $dep['max'] = $max;
+        }
+        if ($exclude) {
+            if (count($exclude) == 1) {
+                $exclude = $exclude[0];
+            }
+            $dep['exclude'] = $exclude;
+        }
+        if (isset($this->_packageInfo['dependencies']['required']['php'])) {
+            $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
+            $this->_packageInfo['dependencies']['required']['php']),
+                'warning: PHP dependency already exists, overwriting');
+            unset($this->_packageInfo['dependencies']['required']['php']);
+        }
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                'required' => array('optional', 'group'),
+                'php' => array('pearinstaller', 'package', 'subpackage', 'extension', 'os', 'arch')
+            ));
+        return true;
+    }
+
+    /**
+     * @param string minimum allowed PEAR installer version
+     * @param string maximum allowed PEAR installer version
+     * @param string recommended PEAR installer version
+     * @param array incompatible version of the PEAR installer
+     */
+    function setPearinstallerDep($min, $max = false, $recommended = false, $exclude = false)
+    {
+        $this->_isValid = 0;
+        $dep =
+            array(
+                'min' => $min,
+            );
+        if ($max) {
+            $dep['max'] = $max;
+        }
+        if ($recommended) {
+            $dep['recommended'] = $recommended;
+        }
+        if ($exclude) {
+            if (count($exclude) == 1) {
+                $exclude = $exclude[0];
+            }
+            $dep['exclude'] = $exclude;
+        }
+        if (isset($this->_packageInfo['dependencies']['required']['pearinstaller'])) {
+            $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
+            $this->_packageInfo['dependencies']['required']['pearinstaller']),
+                'warning: PEAR Installer dependency already exists, overwriting');
+            unset($this->_packageInfo['dependencies']['required']['pearinstaller']);
+        }
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                'required' => array('optional', 'group'),
+                'pearinstaller' => array('package', 'subpackage', 'extension', 'os', 'arch')
+            ));
+    }
+
+    /**
+     * Mark a package as conflicting with this package
+     * @param string package name
+     * @param string package channel
+     * @param string extension this package provides, if any
+     * @param string|false minimum version required
+     * @param string|false maximum version allowed
+     * @param array|false versions to exclude from installation
+     */
+    function addConflictingPackageDepWithChannel($name, $channel,
+                $providesextension = false, $min = false, $max = false, $exclude = false)
+    {
+        $this->_isValid = 0;
+        $dep = $this->_constructDep($name, $channel, false, $min, $max, false,
+            $exclude, $providesextension, false, true);
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                'required' => array('optional', 'group'),
+                'package' => array('subpackage', 'extension', 'os', 'arch')
+            ));
+    }
+
+    /**
+     * Mark a package as conflicting with this package
+     * @param string package name
+     * @param string package channel
+     * @param string extension this package provides, if any
+     */
+    function addConflictingPackageDepWithUri($name, $uri, $providesextension = false)
+    {
+        $this->_isValid = 0;
+        $dep =
+            array(
+                'name' => $name,
+                'uri' => $uri,
+                'conflicts' => '',
+            );
+        if ($providesextension) {
+            $dep['providesextension'] = $providesextension;
+        }
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                'required' => array('optional', 'group'),
+                'package' => array('subpackage', 'extension', 'os', 'arch')
+            ));
+    }
+
+    function addDependencyGroup($name, $hint)
+    {
+        $this->_isValid = 0;
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo,
+            array('attribs' => array('name' => $name, 'hint' => $hint)),
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                'group' => array(),
+            ));
+    }
+
+    /**
+     * @param string package name
+     * @param string|false channel name, false if this is a uri
+     * @param string|false uri name, false if this is a channel
+     * @param string|false minimum version required
+     * @param string|false maximum version allowed
+     * @param string|false recommended installation version
+     * @param array|false versions to exclude from installation
+     * @param string extension this package provides, if any
+     * @param bool if true, tells the installer to ignore the default optional dependency group
+     *             when installing this package
+     * @param bool if true, tells the installer to negate this dependency (conflicts)
+     * @return array
+     * @access private
+     */
+    function _constructDep($name, $channel, $uri, $min, $max, $recommended, $exclude,
+                           $providesextension = false, $nodefault = false,
+                           $conflicts = false)
+    {
+        $dep =
+            array(
+                'name' => $name,
+            );
+        if ($channel) {
+            $dep['channel'] = $channel;
+        } elseif ($uri) {
+            $dep['uri'] = $uri;
+        }
+        if ($min) {
+            $dep['min'] = $min;
+        }
+        if ($max) {
+            $dep['max'] = $max;
+        }
+        if ($recommended) {
+            $dep['recommended'] = $recommended;
+        }
+        if ($exclude) {
+            if (is_array($exclude) && count($exclude) == 1) {
+                $exclude = $exclude[0];
+            }
+            $dep['exclude'] = $exclude;
+        }
+        if ($conflicts) {
+            $dep['conflicts'] = '';
+        }
+        if ($nodefault) {
+            $dep['nodefault'] = '';
+        }
+        if ($providesextension) {
+            $dep['providesextension'] = $providesextension;
+        }
+        return $dep;
+    }
+
+    /**
+     * @param package|subpackage
+     * @param string group name
+     * @param string package name
+     * @param string package channel
+     * @param string minimum version
+     * @param string maximum version
+     * @param string recommended version
+     * @param array|false optional excluded versions
+     * @param string extension this package provides, if any
+     * @param bool if true, tells the installer to ignore the default optional dependency group
+     *             when installing this package
+     * @return bool false if the dependency group has not been initialized with
+     *              {@link addDependencyGroup()}, or a subpackage is added with
+     *              a providesextension
+     */
+    function addGroupPackageDepWithChannel($type, $groupname, $name, $channel, $min = false,
+                                      $max = false, $recommended = false, $exclude = false,
+                                      $providesextension = false, $nodefault = false)
+    {
+        if ($type == 'subpackage' && $providesextension) {
+            return false; // subpackages must be php packages
+        }
+        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
+            $providesextension, $nodefault);
+        return $this->_addGroupDependency($type, $dep, $groupname);
+    }
+
+    /**
+     * @param package|subpackage
+     * @param string group name
+     * @param string package name
+     * @param string package uri
+     * @param string extension this package provides, if any
+     * @param bool if true, tells the installer to ignore the default optional dependency group
+     *             when installing this package
+     * @return bool false if the dependency group has not been initialized with
+     *              {@link addDependencyGroup()}
+     */
+    function addGroupPackageDepWithURI($type, $groupname, $name, $uri, $providesextension = false,
+                                       $nodefault = false)
+    {
+        if ($type == 'subpackage' && $providesextension) {
+            return false; // subpackages must be php packages
+        }
+        $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
+            $providesextension, $nodefault);
+        return $this->_addGroupDependency($type, $dep, $groupname);
+    }
+
+    /**
+     * @param string group name (must be pre-existing)
+     * @param string extension name
+     * @param string minimum version allowed
+     * @param string maximum version allowed
+     * @param string recommended version
+     * @param array incompatible versions
+     */
+    function addGroupExtensionDep($groupname, $name, $min = false, $max = false,
+                                         $recommended = false, $exclude = false)
+    {
+        $this->_isValid = 0;
+        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
+        return $this->_addGroupDependency('extension', $dep, $groupname);
+    }
+
+    /**
+     * @param package|subpackage|extension
+     * @param array dependency contents
+     * @param string name of the dependency group to add this to
+     * @return boolean
+     * @access private
+     */
+    function _addGroupDependency($type, $dep, $groupname)
+    {
+        $arr = array('subpackage', 'extension');
+        if ($type != 'package') {
+            array_shift($arr);
+        }
+        if ($type == 'extension') {
+            array_shift($arr);
+        }
+        if (!isset($this->_packageInfo['dependencies']['group'])) {
+            return false;
+        } else {
+            if (!isset($this->_packageInfo['dependencies']['group'][0])) {
+                if ($this->_packageInfo['dependencies']['group']['attribs']['name'] == $groupname) {
+                    $this->_packageInfo['dependencies']['group'] = $this->_mergeTag(
+                        $this->_packageInfo['dependencies']['group'], $dep,
+                        array(
+                            $type => $arr
+                        ));
+                    $this->_isValid = 0;
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                foreach ($this->_packageInfo['dependencies']['group'] as $i => $group) {
+                    if ($group['attribs']['name'] == $groupname) {
+                    $this->_packageInfo['dependencies']['group'][$i] = $this->_mergeTag(
+                        $this->_packageInfo['dependencies']['group'][$i], $dep,
+                        array(
+                            $type => $arr
+                        ));
+                        $this->_isValid = 0;
+                        return true;
+                    }
+                }
+                return false;
+            }
+        }
+    }
+
+    /**
+     * @param optional|required
+     * @param string package name
+     * @param string package channel
+     * @param string minimum version
+     * @param string maximum version
+     * @param string recommended version
+     * @param string extension this package provides, if any
+     * @param bool if true, tells the installer to ignore the default optional dependency group
+     *             when installing this package
+     * @param array|false optional excluded versions
+     */
+    function addPackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
+                                      $recommended = false, $exclude = false,
+                                      $providesextension = false, $nodefault = false)
+    {
+        if (!in_array($type, array('optional', 'required'), true)) {
+            $type = 'required';
+        }
+        $this->_isValid = 0;
+        $arr = array('optional', 'group');
+        if ($type != 'required') {
+            array_shift($arr);
+        }
+        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
+            $providesextension, $nodefault);
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                $type => $arr,
+                'package' => array('subpackage', 'extension', 'os', 'arch')
+            ));
+    }
+
+    /**
+     * @param optional|required
+     * @param string name of the package
+     * @param string uri of the package
+     * @param string extension this package provides, if any
+     * @param bool if true, tells the installer to ignore the default optional dependency group
+     *             when installing this package
+     */
+    function addPackageDepWithUri($type, $name, $uri, $providesextension = false,
+                                  $nodefault = false)
+    {
+        $this->_isValid = 0;
+        $arr = array('optional', 'group');
+        if ($type != 'required') {
+            array_shift($arr);
+        }
+        $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
+            $providesextension, $nodefault);
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                $type => $arr,
+                'package' => array('subpackage', 'extension', 'os', 'arch')
+            ));
+    }
+
+    /**
+     * @param optional|required optional, required
+     * @param string package name
+     * @param string package channel
+     * @param string minimum version
+     * @param string maximum version
+     * @param string recommended version
+     * @param array incompatible versions
+     * @param bool if true, tells the installer to ignore the default optional dependency group
+     *             when installing this package
+     */
+    function addSubpackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
+                                         $recommended = false, $exclude = false,
+                                         $nodefault = false)
+    {
+        $this->_isValid = 0;
+        $arr = array('optional', 'group');
+        if ($type != 'required') {
+            array_shift($arr);
+        }
+        $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
+            $nodefault);
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                $type => $arr,
+                'subpackage' => array('extension', 'os', 'arch')
+            ));
+    }
+
+    /**
+     * @param optional|required optional, required
+     * @param string package name
+     * @param string package uri for download
+     * @param bool if true, tells the installer to ignore the default optional dependency group
+     *             when installing this package
+     */
+    function addSubpackageDepWithUri($type, $name, $uri, $nodefault = false)
+    {
+        $this->_isValid = 0;
+        $arr = array('optional', 'group');
+        if ($type != 'required') {
+            array_shift($arr);
+        }
+        $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $nodefault);
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                $type => $arr,
+                'subpackage' => array('extension', 'os', 'arch')
+            ));
+    }
+
+    /**
+     * @param optional|required optional, required
+     * @param string extension name
+     * @param string minimum version
+     * @param string maximum version
+     * @param string recommended version
+     * @param array incompatible versions
+     */
+    function addExtensionDep($type, $name, $min = false, $max = false, $recommended = false,
+                             $exclude = false)
+    {
+        $this->_isValid = 0;
+        $arr = array('optional', 'group');
+        if ($type != 'required') {
+            array_shift($arr);
+        }
+        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                $type => $arr,
+                'extension' => array('os', 'arch')
+            ));
+    }
+
+    /**
+     * @param string Operating system name
+     * @param boolean true if this package cannot be installed on this OS
+     */
+    function addOsDep($name, $conflicts = false)
+    {
+        $this->_isValid = 0;
+        $dep = array('name' => $name);
+        if ($conflicts) {
+            $dep['conflicts'] = '';
+        }
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                'required' => array('optional', 'group'),
+                'os' => array('arch')
+            ));
+    }
+
+    /**
+     * @param string Architecture matching pattern
+     * @param boolean true if this package cannot be installed on this architecture
+     */
+    function addArchDep($pattern, $conflicts = false)
+    {
+        $this->_isValid = 0;
+        $dep = array('pattern' => $pattern);
+        if ($conflicts) {
+            $dep['conflicts'] = '';
+        }
+        $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
+            array(
+                'dependencies' => array('providesextension', 'usesrole', 'usestask',
+                    'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
+                    'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
+                'required' => array('optional', 'group'),
+                'arch' => array()
+            ));
+    }
+
+    /**
+     * Set the kind of package, and erase all release tags
+     *
+     * - a php package is a PEAR-style package
+     * - an extbin package is a PECL-style extension binary
+     * - an extsrc package is a PECL-style source for a binary
+     * - an zendextbin package is a PECL-style zend extension binary
+     * - an zendextsrc package is a PECL-style source for a zend extension binary
+     * - a bundle package is a collection of other pre-packaged packages
+     * @param php|extbin|extsrc|zendextsrc|zendextbin|bundle
+     * @return bool success
+     */
+    function setPackageType($type)
+    {
+        $this->_isValid = 0;
+        if (!in_array($type, array('php', 'extbin', 'extsrc', 'zendextsrc',
+                                   'zendextbin', 'bundle'))) {
+            return false;
+        }
+
+        if (in_array($type, array('zendextsrc', 'zendextbin'))) {
+            $this->_setPackageVersion2_1();
+        }
+
+        if ($type != 'bundle') {
+            $type .= 'release';
+        }
+
+        foreach (array('phprelease', 'extbinrelease', 'extsrcrelease',
+                       'zendextsrcrelease', 'zendextbinrelease', 'bundle') as $test) {
+            unset($this->_packageInfo[$test]);
+        }
+
+        if (!isset($this->_packageInfo[$type])) {
+            // ensure that the release tag is set up
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('changelog'),
+                array(), $type);
+        }
+
+        $this->_packageInfo[$type] = array();
+        return true;
+    }
+
+    /**
+     * @return bool true if package type is set up
+     */
+    function addRelease()
+    {
+        if ($type = $this->getPackageType()) {
+            if ($type != 'bundle') {
+                $type .= 'release';
+            }
+            $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
+                array($type => array('changelog')));
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Get the current release tag in order to add to it
+     * @param bool returns only releases that have installcondition if true
+     * @return array|null
+     */
+    function &_getCurrentRelease($strict = true)
+    {
+        if ($p = $this->getPackageType()) {
+            if ($strict) {
+                if ($p == 'extsrc' || $p == 'zendextsrc') {
+                    $a = null;
+                    return $a;
+                }
+            }
+            if ($p != 'bundle') {
+                $p .= 'release';
+            }
+            if (isset($this->_packageInfo[$p][0])) {
+                return $this->_packageInfo[$p][count($this->_packageInfo[$p]) - 1];
+            } else {
+                return $this->_packageInfo[$p];
+            }
+        } else {
+            $a = null;
+            return $a;
+        }
+    }
+
+    /**
+     * Add a file to the current release that should be installed under a different name
+     * @param string <contents> path to file
+     * @param string name the file should be installed as
+     */
+    function addInstallAs($path, $as)
+    {
+        $r = &$this->_getCurrentRelease();
+        if ($r === null) {
+            return false;
+        }
+        $this->_isValid = 0;
+        $r = $this->_mergeTag($r, array('attribs' => array('name' => $path, 'as' => $as)),
+            array(
+                'filelist' => array(),
+                'install' => array('ignore')
+            ));
+    }
+
+    /**
+     * Add a file to the current release that should be ignored
+     * @param string <contents> path to file
+     * @return bool success of operation
+     */
+    function addIgnore($path)
+    {
+        $r = &$this->_getCurrentRelease();
+        if ($r === null) {
+            return false;
+        }
+        $this->_isValid = 0;
+        $r = $this->_mergeTag($r, array('attribs' => array('name' => $path)),
+            array(
+                'filelist' => array(),
+                'ignore' => array()
+            ));
+    }
+
+    /**
+     * Add an extension binary package for this extension source code release
+     *
+     * Note that the package must be from the same channel as the extension source package
+     * @param string
+     */
+    function addBinarypackage($package)
+    {
+        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
+            return false;
+        }
+        $r = &$this->_getCurrentRelease(false);
+        if ($r === null) {
+            return false;
+        }
+        $this->_isValid = 0;
+        $r = $this->_mergeTag($r, $package,
+            array(
+                'binarypackage' => array('filelist'),
+            ));
+    }
+
+    /**
+     * Add a configureoption to an extension source package
+     * @param string
+     * @param string
+     * @param string
+     */
+    function addConfigureOption($name, $prompt, $default = null)
+    {
+        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
+            return false;
+        }
+
+        $r = &$this->_getCurrentRelease(false);
+        if ($r === null) {
+            return false;
+        }
+
+        $opt = array('attribs' => array('name' => $name, 'prompt' => $prompt));
+        if ($default !== null) {
+            $opt['attribs']['default'] = $default;
+        }
+
+        $this->_isValid = 0;
+        $r = $this->_mergeTag($r, $opt,
+            array(
+                'configureoption' => array('binarypackage', 'filelist'),
+            ));
+    }
+
+    /**
+     * Set an installation condition based on php version for the current release set
+     * @param string minimum version
+     * @param string maximum version
+     * @param false|array incompatible versions of PHP
+     */
+    function setPhpInstallCondition($min, $max, $exclude = false)
+    {
+        $r = &$this->_getCurrentRelease();
+        if ($r === null) {
+            return false;
+        }
+        $this->_isValid = 0;
+        if (isset($r['installconditions']['php'])) {
+            unset($r['installconditions']['php']);
+        }
+        $dep = array('min' => $min, 'max' => $max);
+        if ($exclude) {
+            if (is_array($exclude) && count($exclude) == 1) {
+                $exclude = $exclude[0];
+            }
+            $dep['exclude'] = $exclude;
+        }
+        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
+            $r = $this->_mergeTag($r, $dep,
+                array(
+                    'installconditions' => array('configureoption', 'binarypackage',
+                        'filelist'),
+                    'php' => array('extension', 'os', 'arch')
+                ));
+        } else {
+            $r = $this->_mergeTag($r, $dep,
+                array(
+                    'installconditions' => array('filelist'),
+                    'php' => array('extension', 'os', 'arch')
+                ));
+        }
+    }
+
+    /**
+     * @param optional|required optional, required
+     * @param string extension name
+     * @param string minimum version
+     * @param string maximum version
+     * @param string recommended version
+     * @param array incompatible versions
+     */
+    function addExtensionInstallCondition($name, $min = false, $max = false, $recommended = false,
+                                          $exclude = false)
+    {
+        $r = &$this->_getCurrentRelease();
+        if ($r === null) {
+            return false;
+        }
+        $this->_isValid = 0;
+        $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
+        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
+            $r = $this->_mergeTag($r, $dep,
+                array(
+                    'installconditions' => array('configureoption', 'binarypackage',
+                        'filelist'),
+                    'extension' => array('os', 'arch')
+                ));
+        } else {
+            $r = $this->_mergeTag($r, $dep,
+                array(
+                    'installconditions' => array('filelist'),
+                    'extension' => array('os', 'arch')
+                ));
+        }
+    }
+
+    /**
+     * Set an installation condition based on operating system for the current release set
+     * @param string OS name
+     * @param bool whether this OS is incompatible with the current release
+     */
+    function setOsInstallCondition($name, $conflicts = false)
+    {
+        $r = &$this->_getCurrentRelease();
+        if ($r === null) {
+            return false;
+        }
+        $this->_isValid = 0;
+        if (isset($r['installconditions']['os'])) {
+            unset($r['installconditions']['os']);
+        }
+        $dep = array('name' => $name);
+        if ($conflicts) {
+            $dep['conflicts'] = '';
+        }
+        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
+            $r = $this->_mergeTag($r, $dep,
+                array(
+                    'installconditions' => array('configureoption', 'binarypackage',
+                        'filelist'),
+                    'os' => array('arch')
+                ));
+        } else {
+            $r = $this->_mergeTag($r, $dep,
+                array(
+                    'installconditions' => array('filelist'),
+                    'os' => array('arch')
+                ));
+        }
+    }
+
+    /**
+     * Set an installation condition based on architecture for the current release set
+     * @param string architecture pattern
+     * @param bool whether this arch is incompatible with the current release
+     */
+    function setArchInstallCondition($pattern, $conflicts = false)
+    {
+        $r = &$this->_getCurrentRelease();
+        if ($r === null) {
+            return false;
+        }
+        $this->_isValid = 0;
+        if (isset($r['installconditions']['arch'])) {
+            unset($r['installconditions']['arch']);
+        }
+        $dep = array('pattern' => $pattern);
+        if ($conflicts) {
+            $dep['conflicts'] = '';
+        }
+        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
+            $r = $this->_mergeTag($r, $dep,
+                array(
+                    'installconditions' => array('configureoption', 'binarypackage',
+                        'filelist'),
+                    'arch' => array()
+                ));
+        } else {
+            $r = $this->_mergeTag($r, $dep,
+                array(
+                    'installconditions' => array('filelist'),
+                    'arch' => array()
+                ));
+        }
+    }
+
+    /**
+     * For extension binary releases, this is used to specify either the
+     * static URI to a source package, or the package name and channel of the extsrc/zendextsrc
+     * package it is based on.
+     * @param string Package name, or full URI to source package (extsrc/zendextsrc type)
+     */
+    function setSourcePackage($packageOrUri)
+    {
+        $this->_isValid = 0;
+        if (isset($this->_packageInfo['channel'])) {
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
+                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'bundle', 'changelog'),
+                $packageOrUri, 'srcpackage');
+        } else {
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
+                'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
+                'bundle', 'changelog'), $packageOrUri, 'srcuri');
+        }
+    }
+
+    /**
+     * Generate a valid change log entry from the current package.xml
+     * @param string|false
+     */
+    function generateChangeLogEntry($notes = false)
+    {
+        return array(
+            'version' =>
+                array(
+                    'release' => $this->getVersion('release'),
+                    'api' => $this->getVersion('api'),
+                    ),
+            'stability' =>
+                $this->getStability(),
+            'date' => $this->getDate(),
+            'license' => $this->getLicense(true),
+            'notes' => $notes ? $notes : $this->getNotes()
+            );
+    }
+
+    /**
+     * @param string release version to set change log notes for
+     * @param array output of {@link generateChangeLogEntry()}
+     */
+    function setChangelogEntry($releaseversion, $contents)
+    {
+        if (!isset($this->_packageInfo['changelog'])) {
+            $this->_packageInfo['changelog']['release'] = $contents;
+            return;
+        }
+        if (!isset($this->_packageInfo['changelog']['release'][0])) {
+            if ($this->_packageInfo['changelog']['release']['version']['release'] == $releaseversion) {
+                $this->_packageInfo['changelog']['release'] = array(
+                    $this->_packageInfo['changelog']['release']);
+            } else {
+                $this->_packageInfo['changelog']['release'] = array(
+                    $this->_packageInfo['changelog']['release']);
+                return $this->_packageInfo['changelog']['release'][] = $contents;
+            }
+        }
+        foreach($this->_packageInfo['changelog']['release'] as $index => $changelog) {
+            if (isset($changelog['version']) &&
+                  strnatcasecmp($changelog['version']['release'], $releaseversion) == 0) {
+                $curlog = $index;
+            }
+        }
+        if (isset($curlog)) {
+            $this->_packageInfo['changelog']['release'][$curlog] = $contents;
+        } else {
+            $this->_packageInfo['changelog']['release'][] = $contents;
+        }
+    }
+
+    /**
+     * Remove the changelog entirely
+     */
+    function clearChangeLog()
+    {
+        unset($this->_packageInfo['changelog']);
+    }
+}
Index: trunk/library/PEAR/PEAR/PackageFile/Parser/v1.php
===================================================================
--- trunk/library/PEAR/PEAR/PackageFile/Parser/v1.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/PackageFile/Parser/v1.php	(revision 5146)
@@ -0,0 +1,459 @@
+<?php
+/**
+ * package.xml parsing class, package.xml version 1.0
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * package.xml abstraction class
+ */
+require_once 'PEAR/PackageFile/v1.php';
+/**
+ * Parser for package.xml version 1.0
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: @PEAR-VER@
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_PackageFile_Parser_v1
+{
+    var $_registry;
+    var $_config;
+    var $_logger;
+    /**
+     * BC hack to allow PEAR_Common::infoFromString() to sort of
+     * work with the version 2.0 format - there's no filelist though
+     * @param PEAR_PackageFile_v2
+     */
+    function fromV2($packagefile)
+    {
+        $info = $packagefile->getArray(true);
+        $ret = new PEAR_PackageFile_v1;
+        $ret->fromArray($info['old']);
+    }
+
+    function setConfig(&$c)
+    {
+        $this->_config = &$c;
+        $this->_registry = &$c->getRegistry();
+    }
+
+    function setLogger(&$l)
+    {
+        $this->_logger = &$l;
+    }
+
+    /**
+     * @param string contents of package.xml file, version 1.0
+     * @return bool success of parsing
+     */
+    function &parse($data, $file, $archive = false)
+    {
+        if (!extension_loaded('xml')) {
+            return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension');
+        }
+        $xp = xml_parser_create();
+        if (!$xp) {
+            $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml');
+            return $a;
+        }
+        xml_set_object($xp, $this);
+        xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0');
+        xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0');
+        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
+
+        $this->element_stack = array();
+        $this->_packageInfo = array('provides' => array());
+        $this->current_element = false;
+        unset($this->dir_install);
+        $this->_packageInfo['filelist'] = array();
+        $this->filelist =& $this->_packageInfo['filelist'];
+        $this->dir_names = array();
+        $this->in_changelog = false;
+        $this->d_i = 0;
+        $this->cdata = '';
+        $this->_isValid = true;
+
+        if (!xml_parse($xp, $data, 1)) {
+            $code = xml_get_error_code($xp);
+            $line = xml_get_current_line_number($xp);
+            xml_parser_free($xp);
+            $a = &PEAR::raiseError(sprintf("XML error: %s at line %d",
+                           $str = xml_error_string($code), $line), 2);
+            return $a;
+        }
+
+        xml_parser_free($xp);
+
+        $pf = new PEAR_PackageFile_v1;
+        $pf->setConfig($this->_config);
+        if (isset($this->_logger)) {
+            $pf->setLogger($this->_logger);
+        }
+        $pf->setPackagefile($file, $archive);
+        $pf->fromArray($this->_packageInfo);
+        return $pf;
+    }
+    // {{{ _unIndent()
+
+    /**
+     * Unindent given string
+     *
+     * @param string $str The string that has to be unindented.
+     * @return string
+     * @access private
+     */
+    function _unIndent($str)
+    {
+        // remove leading newlines
+        $str = preg_replace('/^[\r\n]+/', '', $str);
+        // find whitespace at the beginning of the first line
+        $indent_len = strspn($str, " \t");
+        $indent = substr($str, 0, $indent_len);
+        $data = '';
+        // remove the same amount of whitespace from following lines
+        foreach (explode("\n", $str) as $line) {
+            if (substr($line, 0, $indent_len) == $indent) {
+                $data .= substr($line, $indent_len) . "\n";
+            } elseif (trim(substr($line, 0, $indent_len))) {
+                $data .= ltrim($line);
+            }
+        }
+        return $data;
+    }
+
+    // Support for package DTD v1.0:
+    // {{{ _element_start_1_0()
+
+    /**
+     * XML parser callback for ending elements.  Used for version 1.0
+     * packages.
+     *
+     * @param resource  $xp    XML parser resource
+     * @param string    $name  name of ending element
+     *
+     * @return void
+     *
+     * @access private
+     */
+    function _element_start_1_0($xp, $name, $attribs)
+    {
+        array_push($this->element_stack, $name);
+        $this->current_element = $name;
+        $spos = sizeof($this->element_stack) - 2;
+        $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
+        $this->current_attributes = $attribs;
+        $this->cdata = '';
+        switch ($name) {
+            case 'dir':
+                if ($this->in_changelog) {
+                    break;
+                }
+                if (array_key_exists('name', $attribs) && $attribs['name'] != '/') {
+                    $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
+                        $attribs['name']);
+                    if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) {
+                        $attribs['name'] = substr($attribs['name'], 0,
+                            strlen($attribs['name']) - 1);
+                    }
+                    if (strpos($attribs['name'], '/') === 0) {
+                        $attribs['name'] = substr($attribs['name'], 1);
+                    }
+                    $this->dir_names[] = $attribs['name'];
+                }
+                if (isset($attribs['baseinstalldir'])) {
+                    $this->dir_install = $attribs['baseinstalldir'];
+                }
+                if (isset($attribs['role'])) {
+                    $this->dir_role = $attribs['role'];
+                }
+                break;
+            case 'file':
+                if ($this->in_changelog) {
+                    break;
+                }
+                if (isset($attribs['name'])) {
+                    $path = '';
+                    if (count($this->dir_names)) {
+                        foreach ($this->dir_names as $dir) {
+                            $path .= $dir . '/';
+                        }
+                    }
+                    $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
+                        $attribs['name']);
+                    unset($attribs['name']);
+                    $this->current_path = $path;
+                    $this->filelist[$path] = $attribs;
+                    // Set the baseinstalldir only if the file don't have this attrib
+                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
+                        isset($this->dir_install))
+                    {
+                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
+                    }
+                    // Set the Role
+                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
+                        $this->filelist[$path]['role'] = $this->dir_role;
+                    }
+                }
+                break;
+            case 'replace':
+                if (!$this->in_changelog) {
+                    $this->filelist[$this->current_path]['replacements'][] = $attribs;
+                }
+                break;
+            case 'maintainers':
+                $this->_packageInfo['maintainers'] = array();
+                $this->m_i = 0; // maintainers array index
+                break;
+            case 'maintainer':
+                // compatibility check
+                if (!isset($this->_packageInfo['maintainers'])) {
+                    $this->_packageInfo['maintainers'] = array();
+                    $this->m_i = 0;
+                }
+                $this->_packageInfo['maintainers'][$this->m_i] = array();
+                $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i];
+                break;
+            case 'changelog':
+                $this->_packageInfo['changelog'] = array();
+                $this->c_i = 0; // changelog array index
+                $this->in_changelog = true;
+                break;
+            case 'release':
+                if ($this->in_changelog) {
+                    $this->_packageInfo['changelog'][$this->c_i] = array();
+                    $this->current_release = &$this->_packageInfo['changelog'][$this->c_i];
+                } else {
+                    $this->current_release = &$this->_packageInfo;
+                }
+                break;
+            case 'deps':
+                if (!$this->in_changelog) {
+                    $this->_packageInfo['release_deps'] = array();
+                }
+                break;
+            case 'dep':
+                // dependencies array index
+                if (!$this->in_changelog) {
+                    $this->d_i++;
+                    isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false;
+                    $this->_packageInfo['release_deps'][$this->d_i] = $attribs;
+                }
+                break;
+            case 'configureoptions':
+                if (!$this->in_changelog) {
+                    $this->_packageInfo['configure_options'] = array();
+                }
+                break;
+            case 'configureoption':
+                if (!$this->in_changelog) {
+                    $this->_packageInfo['configure_options'][] = $attribs;
+                }
+                break;
+            case 'provides':
+                if (empty($attribs['type']) || empty($attribs['name'])) {
+                    break;
+                }
+                $attribs['explicit'] = true;
+                $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
+                break;
+            case 'package' :
+                if (isset($attribs['version'])) {
+                    $this->_packageInfo['xsdversion'] = trim($attribs['version']);
+                } else {
+                    $this->_packageInfo['xsdversion'] = '1.0';
+                }
+                if (isset($attribs['packagerversion'])) {
+                    $this->_packageInfo['packagerversion'] = $attribs['packagerversion'];
+                }
+                break;
+        }
+    }
+
+    // }}}
+    // {{{ _element_end_1_0()
+
+    /**
+     * XML parser callback for ending elements.  Used for version 1.0
+     * packages.
+     *
+     * @param resource  $xp    XML parser resource
+     * @param string    $name  name of ending element
+     *
+     * @return void
+     *
+     * @access private
+     */
+    function _element_end_1_0($xp, $name)
+    {
+        $data = trim($this->cdata);
+        switch ($name) {
+            case 'name':
+                switch ($this->prev_element) {
+                    case 'package':
+                        $this->_packageInfo['package'] = $data;
+                        break;
+                    case 'maintainer':
+                        $this->current_maintainer['name'] = $data;
+                        break;
+                }
+                break;
+            case 'extends' :
+                $this->_packageInfo['extends'] = $data;
+                break;
+            case 'summary':
+                $this->_packageInfo['summary'] = $data;
+                break;
+            case 'description':
+                $data = $this->_unIndent($this->cdata);
+                $this->_packageInfo['description'] = $data;
+                break;
+            case 'user':
+                $this->current_maintainer['handle'] = $data;
+                break;
+            case 'email':
+                $this->current_maintainer['email'] = $data;
+                break;
+            case 'role':
+                $this->current_maintainer['role'] = $data;
+                break;
+            case 'version':
+                if ($this->in_changelog) {
+                    $this->current_release['version'] = $data;
+                } else {
+                    $this->_packageInfo['version'] = $data;
+                }
+                break;
+            case 'date':
+                if ($this->in_changelog) {
+                    $this->current_release['release_date'] = $data;
+                } else {
+                    $this->_packageInfo['release_date'] = $data;
+                }
+                break;
+            case 'notes':
+                // try to "de-indent" release notes in case someone
+                // has been over-indenting their xml ;-)
+                // Trim only on the right side
+                $data = rtrim($this->_unIndent($this->cdata));
+                if ($this->in_changelog) {
+                    $this->current_release['release_notes'] = $data;
+                } else {
+                    $this->_packageInfo['release_notes'] = $data;
+                }
+                break;
+            case 'warnings':
+                if ($this->in_changelog) {
+                    $this->current_release['release_warnings'] = $data;
+                } else {
+                    $this->_packageInfo['release_warnings'] = $data;
+                }
+                break;
+            case 'state':
+                if ($this->in_changelog) {
+                    $this->current_release['release_state'] = $data;
+                } else {
+                    $this->_packageInfo['release_state'] = $data;
+                }
+                break;
+            case 'license':
+                if ($this->in_changelog) {
+                    $this->current_release['release_license'] = $data;
+                } else {
+                    $this->_packageInfo['release_license'] = $data;
+                }
+                break;
+            case 'dep':
+                if ($data && !$this->in_changelog) {
+                    $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data;
+                }
+                break;
+            case 'dir':
+                if ($this->in_changelog) {
+                    break;
+                }
+                array_pop($this->dir_names);
+                break;
+            case 'file':
+                if ($this->in_changelog) {
+                    break;
+                }
+                if ($data) {
+                    $path = '';
+                    if (count($this->dir_names)) {
+                        foreach ($this->dir_names as $dir) {
+                            $path .= $dir . '/';
+                        }
+                    }
+                    $path .= $data;
+                    $this->filelist[$path] = $this->current_attributes;
+                    // Set the baseinstalldir only if the file don't have this attrib
+                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
+                        isset($this->dir_install))
+                    {
+                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
+                    }
+                    // Set the Role
+                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
+                        $this->filelist[$path]['role'] = $this->dir_role;
+                    }
+                }
+                break;
+            case 'maintainer':
+                if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) {
+                    $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead';
+                }
+                $this->m_i++;
+                break;
+            case 'release':
+                if ($this->in_changelog) {
+                    $this->c_i++;
+                }
+                break;
+            case 'changelog':
+                $this->in_changelog = false;
+                break;
+        }
+        array_pop($this->element_stack);
+        $spos = sizeof($this->element_stack) - 1;
+        $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
+        $this->cdata = '';
+    }
+
+    // }}}
+    // {{{ _pkginfo_cdata_1_0()
+
+    /**
+     * XML parser callback for character data.  Used for version 1.0
+     * packages.
+     *
+     * @param resource  $xp    XML parser resource
+     * @param string    $name  character data
+     *
+     * @return void
+     *
+     * @access private
+     */
+    function _pkginfo_cdata_1_0($xp, $data)
+    {
+        if (isset($this->cdata)) {
+            $this->cdata .= $data;
+        }
+    }
+
+    // }}}
+}
+?>
Index: trunk/library/PEAR/PEAR/PackageFile/Parser/v2.php
===================================================================
--- trunk/library/PEAR/PEAR/PackageFile/Parser/v2.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/PackageFile/Parser/v2.php	(revision 5146)
@@ -0,0 +1,113 @@
+<?php
+/**
+ * package.xml parsing class, package.xml version 2.0
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * base xml parser class
+ */
+require_once 'PEAR/XMLParser.php';
+require_once 'PEAR/PackageFile/v2.php';
+/**
+ * Parser for package.xml version 2.0
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: @PEAR-VER@
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_PackageFile_Parser_v2 extends PEAR_XMLParser
+{
+    var $_config;
+    var $_logger;
+    var $_registry;
+
+    function setConfig(&$c)
+    {
+        $this->_config = &$c;
+        $this->_registry = &$c->getRegistry();
+    }
+
+    function setLogger(&$l)
+    {
+        $this->_logger = &$l;
+    }
+    /**
+     * Unindent given string
+     *
+     * @param string $str The string that has to be unindented.
+     * @return string
+     * @access private
+     */
+    function _unIndent($str)
+    {
+        // remove leading newlines
+        $str = preg_replace('/^[\r\n]+/', '', $str);
+        // find whitespace at the beginning of the first line
+        $indent_len = strspn($str, " \t");
+        $indent = substr($str, 0, $indent_len);
+        $data = '';
+        // remove the same amount of whitespace from following lines
+        foreach (explode("\n", $str) as $line) {
+            if (substr($line, 0, $indent_len) == $indent) {
+                $data .= substr($line, $indent_len) . "\n";
+            } else {
+                $data .= $line . "\n";
+            }
+        }
+        return $data;
+    }
+
+    /**
+     * post-process data
+     *
+     * @param string $data
+     * @param string $element element name
+     */
+    function postProcess($data, $element)
+    {
+        if ($element == 'notes') {
+            return trim($this->_unIndent($data));
+        }
+        return trim($data);
+    }
+
+    /**
+     * @param string
+     * @param string file name of the package.xml
+     * @param string|false name of the archive this package.xml came from, if any
+     * @param string class name to instantiate and return.  This must be PEAR_PackageFile_v2 or
+     *               a subclass
+     * @return PEAR_PackageFile_v2
+     */
+    function &parse($data, $file, $archive = false, $class = 'PEAR_PackageFile_v2')
+    {
+        if (PEAR::isError($err = parent::parse($data, $file))) {
+            return $err;
+        }
+
+        $ret = new $class;
+        $ret->encoding = $this->encoding;
+        $ret->setConfig($this->_config);
+        if (isset($this->_logger)) {
+            $ret->setLogger($this->_logger);
+        }
+
+        $ret->fromArray($this->_unserializedData);
+        $ret->setPackagefile($file, $archive);
+        return $ret;
+    }
+}
Index: trunk/library/PEAR/PEAR/PackageFile/v1.php
===================================================================
--- trunk/library/PEAR/PEAR/PackageFile/v1.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/PackageFile/v1.php	(revision 5146)
@@ -0,0 +1,1612 @@
+<?php
+/**
+ * PEAR_PackageFile_v1, package.xml version 1.0
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * For error handling
+ */
+require_once 'PEAR/ErrorStack.php';
+
+/**
+ * Error code if parsing is attempted with no xml extension
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3);
+
+/**
+ * Error code if creating the xml parser resource fails
+ */
+define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4);
+
+/**
+ * Error code used for all sax xml parsing errors
+ */
+define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5);
+
+/**
+ * Error code used when there is no name
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6);
+
+/**
+ * Error code when a package name is not valid
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7);
+
+/**
+ * Error code used when no summary is parsed
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8);
+
+/**
+ * Error code for summaries that are more than 1 line
+ */
+define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9);
+
+/**
+ * Error code used when no description is present
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10);
+
+/**
+ * Error code used when no license is present
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11);
+
+/**
+ * Error code used when a <version> version number is not present
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12);
+
+/**
+ * Error code used when a <version> version number is invalid
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13);
+
+/**
+ * Error code when release state is missing
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14);
+
+/**
+ * Error code when release state is invalid
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15);
+
+/**
+ * Error code when release state is missing
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16);
+
+/**
+ * Error code when release state is invalid
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17);
+
+/**
+ * Error code when no release notes are found
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18);
+
+/**
+ * Error code when no maintainers are found
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19);
+
+/**
+ * Error code when a maintainer has no handle
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20);
+
+/**
+ * Error code when a maintainer has no handle
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21);
+
+/**
+ * Error code when a maintainer has no name
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22);
+
+/**
+ * Error code when a maintainer has no email
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23);
+
+/**
+ * Error code when a maintainer has no handle
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24);
+
+/**
+ * Error code when a dependency is not a PHP dependency, but has no name
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25);
+
+/**
+ * Error code when a dependency has no type (pkg, php, etc.)
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26);
+
+/**
+ * Error code when a dependency has no relation (lt, ge, has, etc.)
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27);
+
+/**
+ * Error code when a dependency is not a 'has' relation, but has no version
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28);
+
+/**
+ * Error code when a dependency has an invalid relation
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29);
+
+/**
+ * Error code when a dependency has an invalid type
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30);
+
+/**
+ * Error code when a dependency has an invalid optional option
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31);
+
+/**
+ * Error code when a dependency is a pkg dependency, and has an invalid package name
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32);
+
+/**
+ * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel
+ */
+define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33);
+
+/**
+ * Error code when rel="has" and version attribute is present.
+ */
+define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34);
+
+/**
+ * Error code when type="php" and dependency name is present
+ */
+define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35);
+
+/**
+ * Error code when a configure option has no name
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36);
+
+/**
+ * Error code when a configure option has no name
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37);
+
+/**
+ * Error code when a file in the filelist has an invalid role
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38);
+
+/**
+ * Error code when a file in the filelist has no role
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39);
+
+/**
+ * Error code when analyzing a php source file that has parse errors
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40);
+
+/**
+ * Error code when analyzing a php source file reveals a source element
+ * without a package name prefix
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41);
+
+/**
+ * Error code when an unknown channel is specified
+ */
+define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42);
+
+/**
+ * Error code when no files are found in the filelist
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43);
+
+/**
+ * Error code when a file is not valid php according to _analyzeSourceCode()
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44);
+
+/**
+ * Error code when the channel validator returns an error or warning
+ */
+define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45);
+
+/**
+ * Error code when a php5 package is packaged in php4 (analysis doesn't work)
+ */
+define('PEAR_PACKAGEFILE_ERROR_PHP5', 46);
+
+/**
+ * Error code when a file is listed in package.xml but does not exist
+ */
+define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47);
+
+/**
+ * Error code when a <dep type="php" rel="not"... is encountered (use rel="ne")
+ */
+define('PEAR_PACKAGEFILE_PHP_NO_NOT', 48);
+
+/**
+ * Error code when a package.xml contains non-ISO-8859-1 characters
+ */
+define('PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS', 49);
+
+/**
+ * Error code when a dependency is not a 'has' relation, but has no version
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION', 50);
+
+/**
+ * Error code when a package has no lead developer
+ */
+define('PEAR_PACKAGEFILE_ERROR_NO_LEAD', 51);
+
+/**
+ * Error code when a filename begins with "."
+ */
+define('PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME', 52);
+/**
+ * package.xml encapsulator
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_PackageFile_v1
+{
+    /**
+     * @access private
+     * @var PEAR_ErrorStack
+     * @access private
+     */
+    var $_stack;
+
+    /**
+     * A registry object, used to access the package name validation regex for non-standard channels
+     * @var PEAR_Registry
+     * @access private
+     */
+    var $_registry;
+
+    /**
+     * An object that contains a log method that matches PEAR_Common::log's signature
+     * @var object
+     * @access private
+     */
+    var $_logger;
+
+    /**
+     * Parsed package information
+     * @var array
+     * @access private
+     */
+    var $_packageInfo;
+
+    /**
+     * path to package.xml
+     * @var string
+     * @access private
+     */
+    var $_packageFile;
+
+    /**
+     * path to package .tgz or false if this is a local/extracted package.xml
+     * @var string
+     * @access private
+     */
+    var $_archiveFile;
+
+    /**
+     * @var int
+     * @access private
+     */
+    var $_isValid = 0;
+
+    /**
+     * Determines whether this packagefile was initialized only with partial package info
+     *
+     * If this package file was constructed via parsing REST, it will only contain
+     *
+     * - package name
+     * - channel name
+     * - dependencies 
+     * @var boolean
+     * @access private
+     */
+    var $_incomplete = true;
+
+    /**
+     * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack
+     * @param string Name of Error Stack class to use.
+     */
+    function PEAR_PackageFile_v1()
+    {
+        $this->_stack = &new PEAR_ErrorStack('PEAR_PackageFile_v1');
+        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
+        $this->_isValid = 0;
+    }
+
+    function installBinary($installer)
+    {
+        return false;
+    }
+
+    function isExtension($name)
+    {
+        return false;
+    }
+
+    function setConfig(&$config)
+    {
+        $this->_config = &$config;
+        $this->_registry = &$config->getRegistry();
+    }
+
+    function setRequestedGroup()
+    {
+        // placeholder
+    }
+
+    /**
+     * For saving in the registry.
+     *
+     * Set the last version that was installed
+     * @param string
+     */
+    function setLastInstalledVersion($version)
+    {
+        $this->_packageInfo['_lastversion'] = $version;
+    }
+
+    /**
+     * @return string|false
+     */
+    function getLastInstalledVersion()
+    {
+        if (isset($this->_packageInfo['_lastversion'])) {
+            return $this->_packageInfo['_lastversion'];
+        }
+        return false;
+    }
+
+    function getInstalledBinary()
+    {
+        return false;
+    }
+
+    function listPostinstallScripts()
+    {
+        return false;
+    }
+
+    function initPostinstallScripts()
+    {
+        return false;
+    }
+
+    function setLogger(&$logger)
+    {
+        if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) {
+            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
+        }
+        $this->_logger = &$logger;
+    }
+
+    function setPackagefile($file, $archive = false)
+    {
+        $this->_packageFile = $file;
+        $this->_archiveFile = $archive ? $archive : $file;
+    }
+
+    function getPackageFile()
+    {
+        return isset($this->_packageFile) ? $this->_packageFile : false;
+    }
+
+    function getPackageType()
+    {
+        return 'php';
+    }
+
+    function getArchiveFile()
+    {
+        return $this->_archiveFile;
+    }
+
+    function packageInfo($field)
+    {
+        if (!is_string($field) || empty($field) ||
+            !isset($this->_packageInfo[$field])) {
+            return false;
+        }
+        return $this->_packageInfo[$field];
+    }
+
+    function setDirtree($path)
+    {
+        if (!isset($this->_packageInfo['dirtree'])) {
+            $this->_packageInfo['dirtree'] = array();
+        }
+        $this->_packageInfo['dirtree'][$path] = true;
+    }
+
+    function getDirtree()
+    {
+        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
+            return $this->_packageInfo['dirtree'];
+        }
+        return false;
+    }
+
+    function resetDirtree()
+    {
+        unset($this->_packageInfo['dirtree']);
+    }
+
+    function fromArray($pinfo)
+    {
+        $this->_incomplete = false;
+        $this->_packageInfo = $pinfo;
+    }
+
+    function isIncomplete()
+    {
+        return $this->_incomplete;
+    }
+
+    function getChannel()
+    {
+        return 'pear.php.net';
+    }
+
+    function getUri()
+    {
+        return false;
+    }
+
+    function getTime()
+    {
+        return false;
+    }
+
+    function getExtends()
+    {
+        if (isset($this->_packageInfo['extends'])) {
+            return $this->_packageInfo['extends'];
+        }
+        return false;
+    }
+
+    /**
+     * @return array
+     */
+    function toArray()
+    {
+        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
+            return false;
+        }
+        return $this->getArray();
+    }
+
+    function getArray()
+    {
+        return $this->_packageInfo;
+    }
+
+    function getName()
+    {
+        return $this->getPackage();
+    }
+
+    function getPackage()
+    {
+        if (isset($this->_packageInfo['package'])) {
+            return $this->_packageInfo['package'];
+        }
+        return false;
+    }
+
+    /**
+     * WARNING - don't use this unless you know what you are doing
+     */
+    function setRawPackage($package)
+    {
+        $this->_packageInfo['package'] = $package;
+    }
+
+    function setPackage($package)
+    {
+        $this->_packageInfo['package'] = $package;
+        $this->_isValid = false;
+    }
+
+    function getVersion()
+    {
+        if (isset($this->_packageInfo['version'])) {
+            return $this->_packageInfo['version'];
+        }
+        return false;
+    }
+
+    function setVersion($version)
+    {
+        $this->_packageInfo['version'] = $version;
+        $this->_isValid = false;
+    }
+
+    function clearMaintainers()
+    {
+        unset($this->_packageInfo['maintainers']);
+    }
+
+    function getMaintainers()
+    {
+        if (isset($this->_packageInfo['maintainers'])) {
+            return $this->_packageInfo['maintainers'];
+        }
+        return false;
+    }
+
+    /**
+     * Adds a new maintainer - no checking of duplicates is performed, use
+     * updatemaintainer for that purpose.
+     */
+    function addMaintainer($role, $handle, $name, $email)
+    {
+        $this->_packageInfo['maintainers'][] =
+            array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name);
+        $this->_isValid = false;
+    }
+
+    function updateMaintainer($role, $handle, $name, $email)
+    {
+        $found = false;
+        if (!isset($this->_packageInfo['maintainers']) ||
+              !is_array($this->_packageInfo['maintainers'])) {
+            return $this->addMaintainer($role, $handle, $name, $email);
+        }
+        foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
+            if ($maintainer['handle'] == $handle) {
+                $found = $i;
+                break;
+            }
+        }
+        if ($found !== false) {
+            unset($this->_packageInfo['maintainers'][$found]);
+            $this->_packageInfo['maintainers'] =
+                array_values($this->_packageInfo['maintainers']);
+        }
+        $this->addMaintainer($role, $handle, $name, $email);
+    }
+
+    function deleteMaintainer($handle)
+    {
+        $found = false;
+        foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
+            if ($maintainer['handle'] == $handle) {
+                $found = $i;
+                break;
+            }
+        }
+        if ($found !== false) {
+            unset($this->_packageInfo['maintainers'][$found]);
+            $this->_packageInfo['maintainers'] =
+                array_values($this->_packageInfo['maintainers']);
+            return true;
+        }
+        return false;
+    }
+
+    function getState()
+    {
+        if (isset($this->_packageInfo['release_state'])) {
+            return $this->_packageInfo['release_state'];
+        }
+        return false;
+    }
+
+    function setRawState($state)
+    {
+        $this->_packageInfo['release_state'] = $state;
+    }
+
+    function setState($state)
+    {
+        $this->_packageInfo['release_state'] = $state;
+        $this->_isValid = false;
+    }
+
+    function getDate()
+    {
+        if (isset($this->_packageInfo['release_date'])) {
+            return $this->_packageInfo['release_date'];
+        }
+        return false;
+    }
+
+    function setDate($date)
+    {
+        $this->_packageInfo['release_date'] = $date;
+        $this->_isValid = false;
+    }
+
+    function getLicense()
+    {
+        if (isset($this->_packageInfo['release_license'])) {
+            return $this->_packageInfo['release_license'];
+        }
+        return false;
+    }
+
+    function setLicense($date)
+    {
+        $this->_packageInfo['release_license'] = $date;
+        $this->_isValid = false;
+    }
+
+    function getSummary()
+    {
+        if (isset($this->_packageInfo['summary'])) {
+            return $this->_packageInfo['summary'];
+        }
+        return false;
+    }
+
+    function setSummary($summary)
+    {
+        $this->_packageInfo['summary'] = $summary;
+        $this->_isValid = false;
+    }
+
+    function getDescription()
+    {
+        if (isset($this->_packageInfo['description'])) {
+            return $this->_packageInfo['description'];
+        }
+        return false;
+    }
+
+    function setDescription($desc)
+    {
+        $this->_packageInfo['description'] = $desc;
+        $this->_isValid = false;
+    }
+
+    function getNotes()
+    {
+        if (isset($this->_packageInfo['release_notes'])) {
+            return $this->_packageInfo['release_notes'];
+        }
+        return false;
+    }
+
+    function setNotes($notes)
+    {
+        $this->_packageInfo['release_notes'] = $notes;
+        $this->_isValid = false;
+    }
+
+    function getDeps()
+    {
+        if (isset($this->_packageInfo['release_deps'])) {
+            return $this->_packageInfo['release_deps'];
+        }
+        return false;
+    }
+
+    /**
+     * Reset dependencies prior to adding new ones
+     */
+    function clearDeps()
+    {
+        unset($this->_packageInfo['release_deps']);
+    }
+
+    function addPhpDep($version, $rel)
+    {
+        $this->_isValid = false;
+        $this->_packageInfo['release_deps'][] =
+            array('type' => 'php',
+                  'rel' => $rel,
+                  'version' => $version);
+    }
+
+    function addPackageDep($name, $version, $rel, $optional = 'no')
+    {
+        $this->_isValid = false;
+        $dep =
+            array('type' => 'pkg',
+                  'name' => $name,
+                  'rel' => $rel,
+                  'optional' => $optional);
+        if ($rel != 'has' && $rel != 'not') {
+            $dep['version'] = $version;
+        }
+        $this->_packageInfo['release_deps'][] = $dep;
+    }
+
+    function addExtensionDep($name, $version, $rel, $optional = 'no')
+    {
+        $this->_isValid = false;
+        $this->_packageInfo['release_deps'][] =
+            array('type' => 'ext',
+                  'name' => $name,
+                  'rel' => $rel,
+                  'version' => $version,
+                  'optional' => $optional);
+    }
+
+    /**
+     * WARNING - do not use this function directly unless you know what you're doing
+     */
+    function setDeps($deps)
+    {
+        $this->_packageInfo['release_deps'] = $deps;
+    }
+
+    function hasDeps()
+    {
+        return isset($this->_packageInfo['release_deps']) &&
+            count($this->_packageInfo['release_deps']);
+    }
+
+    function getDependencyGroup($group)
+    {
+        return false;
+    }
+
+    function isCompatible($pf)
+    {
+        return false;
+    }
+
+    function isSubpackageOf($p)
+    {
+        return $p->isSubpackage($this);
+    }
+
+    function isSubpackage($p)
+    {
+        return false;
+    }
+
+    function dependsOn($package, $channel)
+    {
+        if (strtolower($channel) != 'pear.php.net') {
+            return false;
+        }
+        if (!($deps = $this->getDeps())) {
+            return false;
+        }
+        foreach ($deps as $dep) {
+            if ($dep['type'] != 'pkg') {
+                continue;
+            }
+            if (strtolower($dep['name']) == strtolower($package)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    function getConfigureOptions()
+    {
+        if (isset($this->_packageInfo['configure_options'])) {
+            return $this->_packageInfo['configure_options'];
+        }
+        return false;
+    }
+
+    function hasConfigureOptions()
+    {
+        return isset($this->_packageInfo['configure_options']) &&
+            count($this->_packageInfo['configure_options']);
+    }
+
+    function addConfigureOption($name, $prompt, $default = false)
+    {
+        $o = array('name' => $name, 'prompt' => $prompt);
+        if ($default !== false) {
+            $o['default'] = $default;
+        }
+        if (!isset($this->_packageInfo['configure_options'])) {
+            $this->_packageInfo['configure_options'] = array();
+        }
+        $this->_packageInfo['configure_options'][] = $o;
+    }
+
+    function clearConfigureOptions()
+    {
+        unset($this->_packageInfo['configure_options']);
+    }
+
+    function getProvides()
+    {
+        if (isset($this->_packageInfo['provides'])) {
+            return $this->_packageInfo['provides'];
+        }
+        return false;
+    }
+
+    function getProvidesExtension()
+    {
+        return false;
+    }
+
+    function addFile($dir, $file, $attrs)
+    {
+        $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
+        if ($dir == '/' || $dir == '') {
+            $dir = '';
+        } else {
+            $dir .= '/';
+        }
+        $file = $dir . $file;
+        $file = preg_replace('![\\/]+!', '/', $file);
+        $this->_packageInfo['filelist'][$file] = $attrs;
+    }
+
+    function getInstallationFilelist()
+    {
+        return $this->getFilelist();
+    }
+
+    function getFilelist()
+    {
+        if (isset($this->_packageInfo['filelist'])) {
+            return $this->_packageInfo['filelist'];
+        }
+        return false;
+    }
+
+    function setFileAttribute($file, $attr, $value)
+    {
+        $this->_packageInfo['filelist'][$file][$attr] = $value;
+    }
+
+    function resetFilelist()
+    {
+        $this->_packageInfo['filelist'] = array();
+    }
+
+    function setInstalledAs($file, $path)
+    {
+        if ($path) {
+            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
+        }
+        unset($this->_packageInfo['filelist'][$file]['installed_as']);
+    }
+
+    function installedFile($file, $atts)
+    {
+        if (isset($this->_packageInfo['filelist'][$file])) {
+            $this->_packageInfo['filelist'][$file] =
+                array_merge($this->_packageInfo['filelist'][$file], $atts);
+        } else {
+            $this->_packageInfo['filelist'][$file] = $atts;
+        }
+    }
+
+    function getChangelog()
+    {
+        if (isset($this->_packageInfo['changelog'])) {
+            return $this->_packageInfo['changelog'];
+        }
+        return false;
+    }
+
+    function getPackagexmlVersion()
+    {
+        return '1.0';
+    }
+
+    /**
+     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
+     * @param boolean determines whether to purge the error stack after retrieving
+     * @return array
+     */
+    function getValidationWarnings($purge = true)
+    {
+        return $this->_stack->getErrors($purge);
+    }
+
+    // }}}
+    /**
+     * Validation error.  Also marks the object contents as invalid
+     * @param error code
+     * @param array error information
+     * @access private
+     */
+    function _validateError($code, $params = array())
+    {
+        $this->_stack->push($code, 'error', $params, false, false, debug_backtrace());
+        $this->_isValid = false;
+    }
+
+    /**
+     * Validation warning.  Does not mark the object contents invalid.
+     * @param error code
+     * @param array error information
+     * @access private
+     */
+    function _validateWarning($code, $params = array())
+    {
+        $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace());
+    }
+
+    /**
+     * @param integer error code
+     * @access protected
+     */
+    function _getErrorMessage()
+    {
+        return array(
+                PEAR_PACKAGEFILE_ERROR_NO_NAME =>
+                    'Missing Package Name',
+                PEAR_PACKAGEFILE_ERROR_NO_SUMMARY =>
+                    'No summary found',
+                PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY =>
+                    'Summary should be on one line',
+                PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION =>
+                    'Missing description',
+                PEAR_PACKAGEFILE_ERROR_NO_LICENSE =>
+                    'Missing license',
+                PEAR_PACKAGEFILE_ERROR_NO_VERSION =>
+                    'No release version found',
+                PEAR_PACKAGEFILE_ERROR_NO_STATE =>
+                    'No release state found',
+                PEAR_PACKAGEFILE_ERROR_NO_DATE =>
+                    'No release date found',
+                PEAR_PACKAGEFILE_ERROR_NO_NOTES =>
+                    'No release notes found',
+                PEAR_PACKAGEFILE_ERROR_NO_LEAD =>
+                    'Package must have at least one lead maintainer',
+                PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS =>
+                    'No maintainers found, at least one must be defined',
+                PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE =>
+                    'Maintainer %index% has no handle (user ID at channel server)',
+                PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE =>
+                    'Maintainer %index% has no role',
+                PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME =>
+                    'Maintainer %index% has no name',
+                PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL =>
+                    'Maintainer %index% has no email',
+                PEAR_PACKAGEFILE_ERROR_NO_DEPNAME =>
+                    'Dependency %index% is not a php dependency, and has no name',
+                PEAR_PACKAGEFILE_ERROR_NO_DEPREL =>
+                    'Dependency %index% has no relation (rel)',
+                PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE =>
+                    'Dependency %index% has no type',
+                PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED =>
+                    'PHP Dependency %index% has a name attribute of "%name%" which will be' .
+                        ' ignored!',
+                PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION =>
+                    'Dependency %index% is not a rel="has" or rel="not" dependency, ' .
+                        'and has no version',
+                PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION =>
+                    'Dependency %index% is a type="php" dependency, ' .
+                        'and has no version',
+                PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED =>
+                    'Dependency %index% is a rel="%rel%" dependency, versioning is ignored',
+                PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL =>
+                    'Dependency %index% has invalid optional value "%opt%", should be yes or no',
+                PEAR_PACKAGEFILE_PHP_NO_NOT =>
+                    'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' .
+                        ' to exclude specific versions',
+                PEAR_PACKAGEFILE_ERROR_NO_CONFNAME =>
+                    'Configure Option %index% has no name',
+                PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT =>
+                    'Configure Option %index% has no prompt',
+                PEAR_PACKAGEFILE_ERROR_NO_FILES =>
+                    'No files in <filelist> section of package.xml',
+                PEAR_PACKAGEFILE_ERROR_NO_FILEROLE =>
+                    'File "%file%" has no role, expecting one of "%roles%"',
+                PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE =>
+                    'File "%file%" has invalid role "%role%", expecting one of "%roles%"',
+                PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME =>
+                    'File "%file%" cannot start with ".", cannot package or install',
+                PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE =>
+                    'Parser error: invalid PHP found in file "%file%"',
+                PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX =>
+                    'in %file%: %type% "%name%" not prefixed with package name "%package%"',
+                PEAR_PACKAGEFILE_ERROR_INVALID_FILE =>
+                    'Parser error: invalid PHP file "%file%"',
+                PEAR_PACKAGEFILE_ERROR_CHANNELVAL =>
+                    'Channel validator error: field "%field%" - %reason%',
+                PEAR_PACKAGEFILE_ERROR_PHP5 =>
+                    'Error, PHP5 token encountered in %file%, analysis should be in PHP5',
+                PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND =>
+                    'File "%file%" in package.xml does not exist',
+                PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS =>
+                    'Package.xml contains non-ISO-8859-1 characters, and may not validate',
+            );
+    }
+
+    /**
+     * Validate XML package definition file.
+     *
+     * @access public
+     * @return boolean
+     */
+    function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false)
+    {
+        if (($this->_isValid & $state) == $state) {
+            return true;
+        }
+        $this->_isValid = true;
+        $info = $this->_packageInfo;
+        if (empty($info['package'])) {
+            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME);
+            $this->_packageName = $pn = 'unknown';
+        } else {
+            $this->_packageName = $pn = $info['package'];
+        }
+
+        if (empty($info['summary'])) {
+            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY);
+        } elseif (strpos(trim($info['summary']), "\n") !== false) {
+            $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY,
+                array('summary' => $info['summary']));
+        }
+        if (empty($info['description'])) {
+            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION);
+        }
+        if (empty($info['release_license'])) {
+            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE);
+        }
+        if (empty($info['version'])) {
+            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION);
+        }
+        if (empty($info['release_state'])) {
+            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE);
+        }
+        if (empty($info['release_date'])) {
+            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE);
+        }
+        if (empty($info['release_notes'])) {
+            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES);
+        }
+        if (empty($info['maintainers'])) {
+            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS);
+        } else {
+            $haslead = false;
+            $i = 1;
+            foreach ($info['maintainers'] as $m) {
+                if (empty($m['handle'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE,
+                        array('index' => $i));
+                }
+                if (empty($m['role'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE,
+                        array('index' => $i, 'roles' => PEAR_Common::getUserRoles()));
+                } elseif ($m['role'] == 'lead') {
+                    $haslead = true;
+                }
+                if (empty($m['name'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME,
+                        array('index' => $i));
+                }
+                if (empty($m['email'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL,
+                        array('index' => $i));
+                }
+                $i++;
+            }
+            if (!$haslead) {
+                $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD);
+            }
+        }
+        if (!empty($info['release_deps'])) {
+            $i = 1;
+            foreach ($info['release_deps'] as $d) {
+                if (!isset($d['type']) || empty($d['type'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE,
+                        array('index' => $i, 'types' => PEAR_Common::getDependencyTypes()));
+                    continue;
+                }
+                if (!isset($d['rel']) || empty($d['rel'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL,
+                        array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations()));
+                    continue;
+                }
+                if (!empty($d['optional'])) {
+                    if (!in_array($d['optional'], array('yes', 'no'))) {
+                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL,
+                            array('index' => $i, 'opt' => $d['optional']));
+                    }
+                }
+                if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION,
+                        array('index' => $i));
+                } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) {
+                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED,
+                        array('index' => $i, 'rel' => $d['rel']));
+                }
+                if ($d['type'] == 'php' && !empty($d['name'])) {
+                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED,
+                        array('index' => $i, 'name' => $d['name']));
+                } elseif ($d['type'] != 'php' && empty($d['name'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME,
+                        array('index' => $i));
+                }
+                if ($d['type'] == 'php' && empty($d['version'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION,
+                        array('index' => $i));
+                }
+                if (($d['rel'] == 'not') && ($d['type'] == 'php')) {
+                    $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT,
+                        array('index' => $i));
+                }
+                $i++;
+            }
+        }
+        if (!empty($info['configure_options'])) {
+            $i = 1;
+            foreach ($info['configure_options'] as $c) {
+                if (empty($c['name'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME,
+                        array('index' => $i));
+                }
+                if (empty($c['prompt'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT,
+                        array('index' => $i));
+                }
+                $i++;
+            }
+        }
+        if (empty($info['filelist'])) {
+            $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES);
+            $errors[] = 'no files';
+        } else {
+            foreach ($info['filelist'] as $file => $fa) {
+                if (empty($fa['role'])) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE,
+                        array('file' => $file, 'roles' => PEAR_Common::getFileRoles()));
+                    continue;
+                } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE,
+                        array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles()));
+                }
+                if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) {
+                    // file contains .. parent directory or . cur directory references
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
+                        array('file' => $file));
+                }
+                if (isset($fa['install-as']) &&
+                      preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
+                                 str_replace('\\', '/', $fa['install-as']))) {
+                    // install-as contains .. parent directory or . cur directory references
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
+                        array('file' => $file . ' [installed as ' . $fa['install-as'] . ']'));
+                }
+                if (isset($fa['baseinstalldir']) &&
+                      preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
+                                 str_replace('\\', '/', $fa['baseinstalldir']))) {
+                    // install-as contains .. parent directory or . cur directory references
+                    $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
+                        array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']'));
+                }
+            }
+        }
+        if (isset($this->_registry) && $this->_isValid) {
+            $chan = $this->_registry->getChannel('pear.php.net');
+            if (PEAR::isError($chan)) {
+                $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage());
+                return $this->_isValid = 0;
+            }
+            $validator = $chan->getValidationObject();
+            $validator->setPackageFile($this);
+            $validator->validate($state);
+            $failures = $validator->getFailures();
+            foreach ($failures['errors'] as $error) {
+                $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error);
+            }
+            foreach ($failures['warnings'] as $warning) {
+                $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning);
+            }
+        }
+        if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) {
+            if ($this->_analyzePhpFiles()) {
+                $this->_isValid = true;
+            }
+        }
+        if ($this->_isValid) {
+            return $this->_isValid = $state;
+        }
+        return $this->_isValid = 0;
+    }
+
+    function _analyzePhpFiles()
+    {
+        if (!$this->_isValid) {
+            return false;
+        }
+        if (!isset($this->_packageFile)) {
+            return false;
+        }
+        $dir_prefix = dirname($this->_packageFile);
+        $common = new PEAR_Common;
+        $log = isset($this->_logger) ? array(&$this->_logger, 'log') :
+            array($common, 'log');
+        $info = $this->getFilelist();
+        foreach ($info as $file => $fa) {
+            if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
+                $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND,
+                    array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file));
+                continue;
+            }
+            if ($fa['role'] == 'php' && $dir_prefix) {
+                call_user_func_array($log, array(1, "Analyzing $file"));
+                $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
+                if ($srcinfo) {
+                    $this->_buildProvidesArray($srcinfo);
+                }
+            }
+        }
+        $this->_packageName = $pn = $this->getPackage();
+        $pnl = strlen($pn);
+        if (isset($this->_packageInfo['provides'])) {
+            foreach ((array) $this->_packageInfo['provides'] as $key => $what) {
+                if (isset($what['explicit'])) {
+                    // skip conformance checks if the provides entry is
+                    // specified in the package.xml file
+                    continue;
+                }
+                extract($what);
+                if ($type == 'class') {
+                    if (!strncasecmp($name, $pn, $pnl)) {
+                        continue;
+                    }
+                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
+                        array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
+                } elseif ($type == 'function') {
+                    if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
+                        continue;
+                    }
+                    $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
+                        array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
+                }
+            }
+        }
+        return $this->_isValid;
+    }
+
+    /**
+     * Get the default xml generator object
+     *
+     * @return PEAR_PackageFile_Generator_v1
+     */
+    function &getDefaultGenerator()
+    {
+        if (!class_exists('PEAR_PackageFile_Generator_v1')) {
+            require_once 'PEAR/PackageFile/Generator/v1.php';
+        }
+        $a = &new PEAR_PackageFile_Generator_v1($this);
+        return $a;
+    }
+
+    /**
+     * Get the contents of a file listed within the package.xml
+     * @param string
+     * @return string
+     */
+    function getFileContents($file)
+    {
+        if ($this->_archiveFile == $this->_packageFile) { // unpacked
+            $dir = dirname($this->_packageFile);
+            $file = $dir . DIRECTORY_SEPARATOR . $file;
+            $file = str_replace(array('/', '\\'),
+                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
+            if (file_exists($file) && is_readable($file)) {
+                return implode('', file($file));
+            }
+        } else { // tgz
+            if (!class_exists('Archive_Tar')) {
+                require_once 'Archive/Tar.php';
+            }
+            $tar = &new Archive_Tar($this->_archiveFile);
+            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
+            if ($file != 'package.xml' && $file != 'package2.xml') {
+                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
+            }
+            $file = $tar->extractInString($file);
+            $tar->popErrorHandling();
+            if (PEAR::isError($file)) {
+                return PEAR::raiseError("Cannot locate file '$file' in archive");
+            }
+            return $file;
+        }
+    }
+
+    // {{{ analyzeSourceCode()
+    /**
+     * Analyze the source code of the given PHP file
+     *
+     * @param  string Filename of the PHP file
+     * @return mixed
+     * @access private
+     */
+    function _analyzeSourceCode($file)
+    {
+        if (!function_exists("token_get_all")) {
+            return false;
+        }
+        if (!defined('T_DOC_COMMENT')) {
+            define('T_DOC_COMMENT', T_COMMENT);
+        }
+        if (!defined('T_INTERFACE')) {
+            define('T_INTERFACE', -1);
+        }
+        if (!defined('T_IMPLEMENTS')) {
+            define('T_IMPLEMENTS', -1);
+        }
+        if (!$fp = @fopen($file, "r")) {
+            return false;
+        }
+        fclose($fp);
+        $contents = file_get_contents($file);
+        $tokens = token_get_all($contents);
+/*
+        for ($i = 0; $i < sizeof($tokens); $i++) {
+            @list($token, $data) = $tokens[$i];
+            if (is_string($token)) {
+                var_dump($token);
+            } else {
+                print token_name($token) . ' ';
+                var_dump(rtrim($data));
+            }
+        }
+*/
+        $look_for = 0;
+        $paren_level = 0;
+        $bracket_level = 0;
+        $brace_level = 0;
+        $lastphpdoc = '';
+        $current_class = '';
+        $current_interface = '';
+        $current_class_level = -1;
+        $current_function = '';
+        $current_function_level = -1;
+        $declared_classes = array();
+        $declared_interfaces = array();
+        $declared_functions = array();
+        $declared_methods = array();
+        $used_classes = array();
+        $used_functions = array();
+        $extends = array();
+        $implements = array();
+        $nodeps = array();
+        $inquote = false;
+        $interface = false;
+        for ($i = 0; $i < sizeof($tokens); $i++) {
+            if (is_array($tokens[$i])) {
+                list($token, $data) = $tokens[$i];
+            } else {
+                $token = $tokens[$i];
+                $data = '';
+            }
+            if ($inquote) {
+                if ($token != '"' && $token != T_END_HEREDOC) {
+                    continue;
+                } else {
+                    $inquote = false;
+                    continue;
+                }
+            }
+            switch ($token) {
+                case T_WHITESPACE :
+                    continue;
+                case ';':
+                    if ($interface) {
+                        $current_function = '';
+                        $current_function_level = -1;
+                    }
+                    break;
+                case '"':
+                case T_START_HEREDOC:
+                    $inquote = true;
+                    break;
+                case T_CURLY_OPEN:
+                case T_DOLLAR_OPEN_CURLY_BRACES:
+                case '{': $brace_level++; continue 2;
+                case '}':
+                    $brace_level--;
+                    if ($current_class_level == $brace_level) {
+                        $current_class = '';
+                        $current_class_level = -1;
+                    }
+                    if ($current_function_level == $brace_level) {
+                        $current_function = '';
+                        $current_function_level = -1;
+                    }
+                    continue 2;
+                case '[': $bracket_level++; continue 2;
+                case ']': $bracket_level--; continue 2;
+                case '(': $paren_level++;   continue 2;
+                case ')': $paren_level--;   continue 2;
+                case T_INTERFACE:
+                    $interface = true;
+                case T_CLASS:
+                    if (($current_class_level != -1) || ($current_function_level != -1)) {
+                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
+                            array('file' => $file));
+                        return false;
+                    }
+                case T_FUNCTION:
+                case T_NEW:
+                case T_EXTENDS:
+                case T_IMPLEMENTS:
+                    $look_for = $token;
+                    continue 2;
+                case T_STRING:
+                    if (version_compare(zend_version(), '2.0', '<')) {
+                        if (in_array(strtolower($data),
+                            array('public', 'private', 'protected', 'abstract',
+                                  'interface', 'implements', 'throw') 
+                                 )) {
+                            $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_PHP5,
+                                array($file));
+                        }
+                    }
+                    if ($look_for == T_CLASS) {
+                        $current_class = $data;
+                        $current_class_level = $brace_level;
+                        $declared_classes[] = $current_class;
+                    } elseif ($look_for == T_INTERFACE) {
+                        $current_interface = $data;
+                        $current_class_level = $brace_level;
+                        $declared_interfaces[] = $current_interface;
+                    } elseif ($look_for == T_IMPLEMENTS) {
+                        $implements[$current_class] = $data;
+                    } elseif ($look_for == T_EXTENDS) {
+                        $extends[$current_class] = $data;
+                    } elseif ($look_for == T_FUNCTION) {
+                        if ($current_class) {
+                            $current_function = "$current_class::$data";
+                            $declared_methods[$current_class][] = $data;
+                        } elseif ($current_interface) {
+                            $current_function = "$current_interface::$data";
+                            $declared_methods[$current_interface][] = $data;
+                        } else {
+                            $current_function = $data;
+                            $declared_functions[] = $current_function;
+                        }
+                        $current_function_level = $brace_level;
+                        $m = array();
+                    } elseif ($look_for == T_NEW) {
+                        $used_classes[$data] = true;
+                    }
+                    $look_for = 0;
+                    continue 2;
+                case T_VARIABLE:
+                    $look_for = 0;
+                    continue 2;
+                case T_DOC_COMMENT:
+                case T_COMMENT:
+                    if (preg_match('!^/\*\*\s!', $data)) {
+                        $lastphpdoc = $data;
+                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
+                            $nodeps = array_merge($nodeps, $m[1]);
+                        }
+                    }
+                    continue 2;
+                case T_DOUBLE_COLON:
+                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
+                        $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
+                            array('file' => $file));
+                        return false;
+                    }
+                    $class = $tokens[$i - 1][1];
+                    if (strtolower($class) != 'parent') {
+                        $used_classes[$class] = true;
+                    }
+                    continue 2;
+            }
+        }
+        return array(
+            "source_file" => $file,
+            "declared_classes" => $declared_classes,
+            "declared_interfaces" => $declared_interfaces,
+            "declared_methods" => $declared_methods,
+            "declared_functions" => $declared_functions,
+            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
+            "inheritance" => $extends,
+            "implements" => $implements,
+            );
+    }
+
+    /**
+     * Build a "provides" array from data returned by
+     * analyzeSourceCode().  The format of the built array is like
+     * this:
+     *
+     *  array(
+     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
+     *    ...
+     *  )
+     *
+     *
+     * @param array $srcinfo array with information about a source file
+     * as returned by the analyzeSourceCode() method.
+     *
+     * @return void
+     *
+     * @access private
+     *
+     */
+    function _buildProvidesArray($srcinfo)
+    {
+        if (!$this->_isValid) {
+            return false;
+        }
+        $file = basename($srcinfo['source_file']);
+        $pn = $this->getPackage();
+        $pnl = strlen($pn);
+        foreach ($srcinfo['declared_classes'] as $class) {
+            $key = "class;$class";
+            if (isset($this->_packageInfo['provides'][$key])) {
+                continue;
+            }
+            $this->_packageInfo['provides'][$key] =
+                array('file'=> $file, 'type' => 'class', 'name' => $class);
+            if (isset($srcinfo['inheritance'][$class])) {
+                $this->_packageInfo['provides'][$key]['extends'] =
+                    $srcinfo['inheritance'][$class];
+            }
+        }
+        foreach ($srcinfo['declared_methods'] as $class => $methods) {
+            foreach ($methods as $method) {
+                $function = "$class::$method";
+                $key = "function;$function";
+                if ($method{0} == '_' || !strcasecmp($method, $class) ||
+                    isset($this->_packageInfo['provides'][$key])) {
+                    continue;
+                }
+                $this->_packageInfo['provides'][$key] =
+                    array('file'=> $file, 'type' => 'function', 'name' => $function);
+            }
+        }
+
+        foreach ($srcinfo['declared_functions'] as $function) {
+            $key = "function;$function";
+            if ($function{0} == '_' || isset($this->_packageInfo['provides'][$key])) {
+                continue;
+            }
+            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
+                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
+            }
+            $this->_packageInfo['provides'][$key] =
+                array('file'=> $file, 'type' => 'function', 'name' => $function);
+        }
+    }
+
+    // }}}
+}
+?>
Index: trunk/library/PEAR/PEAR/PackageFile/v2.php
===================================================================
--- trunk/library/PEAR/PEAR/PackageFile/v2.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/PackageFile/v2.php	(revision 5146)
@@ -0,0 +1,2049 @@
+<?php
+/**
+ * PEAR_PackageFile_v2, package.xml version 2.0
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * For error handling
+ */
+require_once 'PEAR/ErrorStack.php';
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_PackageFile_v2
+{
+
+    /**
+     * Parsed package information
+     * @var array
+     * @access private
+     */
+    var $_packageInfo = array();
+
+    /**
+     * path to package .tgz or false if this is a local/extracted package.xml
+     * @var string|false
+     * @access private
+     */
+    var $_archiveFile;
+
+    /**
+     * path to package .xml or false if this is an abstract parsed-from-string xml
+     * @var string|false
+     * @access private
+     */
+    var $_packageFile;
+
+    /**
+     * This is used by file analysis routines to log progress information
+     * @var PEAR_Common
+     * @access protected
+     */
+    var $_logger;
+
+    /**
+     * This is set to the highest validation level that has been validated
+     *
+     * If the package.xml is invalid or unknown, this is set to 0.  If
+     * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL.  If
+     * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING
+     * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING.  This allows validation
+     * "caching" to occur, which is particularly important for package validation, so
+     * that PHP files are not validated twice
+     * @var int
+     * @access private
+     */
+    var $_isValid = 0;
+
+    /**
+     * True if the filelist has been validated
+     * @param bool
+     */
+    var $_filesValid = false;
+
+    /**
+     * @var PEAR_Registry
+     * @access protected
+     */
+    var $_registry;
+
+    /**
+     * @var PEAR_Config
+     * @access protected
+     */
+    var $_config;
+
+    /**
+     * Optional Dependency group requested for installation
+     * @var string
+     * @access private
+     */
+    var $_requestedGroup = false;
+
+    /**
+     * @var PEAR_ErrorStack
+     * @access protected
+     */
+    var $_stack;
+
+    /**
+     * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
+     */
+    var $_tasksNs;
+
+    /**
+     * Determines whether this packagefile was initialized only with partial package info
+     *
+     * If this package file was constructed via parsing REST, it will only contain
+     *
+     * - package name
+     * - channel name
+     * - dependencies
+     * @var boolean
+     * @access private
+     */
+    var $_incomplete = true;
+
+    /**
+     * @var PEAR_PackageFile_v2_Validator
+     */
+    var $_v2Validator;
+
+    /**
+     * The constructor merely sets up the private error stack
+     */
+    function PEAR_PackageFile_v2()
+    {
+        $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
+        $this->_isValid = false;
+    }
+
+    /**
+     * To make unit-testing easier
+     * @param PEAR_Frontend_*
+     * @param array options
+     * @param PEAR_Config
+     * @return PEAR_Downloader
+     * @access protected
+     */
+    function &getPEARDownloader(&$i, $o, &$c)
+    {
+        $z = &new PEAR_Downloader($i, $o, $c);
+        return $z;
+    }
+
+    /**
+     * To make unit-testing easier
+     * @param PEAR_Config
+     * @param array options
+     * @param array package name as returned from {@link PEAR_Registry::parsePackageName()}
+     * @param int PEAR_VALIDATE_* constant
+     * @return PEAR_Dependency2
+     * @access protected
+     */
+    function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
+    {
+        if (!class_exists('PEAR_Dependency2')) {
+            require_once 'PEAR/Dependency2.php';
+        }
+        $z = &new PEAR_Dependency2($c, $o, $p, $s);
+        return $z;
+    }
+
+    function getInstalledBinary()
+    {
+        return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
+            false;
+    }
+
+    /**
+     * Installation of source package has failed, attempt to download and install the
+     * binary version of this package.
+     * @param PEAR_Installer
+     * @return array|false
+     */
+    function installBinary(&$installer)
+    {
+        if (!OS_WINDOWS) {
+            $a = false;
+            return $a;
+        }
+        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
+            $releasetype = $this->getPackageType() . 'release';
+            if (!is_array($installer->getInstallPackages())) {
+                $a = false;
+                return $a;
+            }
+            foreach ($installer->getInstallPackages() as $p) {
+                if ($p->isExtension($this->_packageInfo['providesextension'])) {
+                    if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
+                        $a = false;
+                        return $a; // the user probably downloaded it separately
+                    }
+                }
+            }
+            if (isset($this->_packageInfo[$releasetype]['binarypackage'])) {
+                $installer->log(0, 'Attempting to download binary version of extension "' .
+                    $this->_packageInfo['providesextension'] . '"');
+                $params = $this->_packageInfo[$releasetype]['binarypackage'];
+                if (!is_array($params) || !isset($params[0])) {
+                    $params = array($params);
+                }
+                if (isset($this->_packageInfo['channel'])) {
+                    foreach ($params as $i => $param) {
+                        $params[$i] = array('channel' => $this->_packageInfo['channel'],
+                            'package' => $param, 'version' => $this->getVersion());
+                    }
+                }
+                $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
+                    $installer->config);
+                $verbose = $dl->config->get('verbose');
+                $dl->config->set('verbose', -1);
+                foreach ($params as $param) {
+                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                    $ret = $dl->download(array($param));
+                    PEAR::popErrorHandling();
+                    if (is_array($ret) && count($ret)) {
+                        break;
+                    }
+                }
+                $dl->config->set('verbose', $verbose);
+                if (is_array($ret)) {
+                    if (count($ret) == 1) {
+                        $pf = $ret[0]->getPackageFile();
+                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                        $err = $installer->install($ret[0]);
+                        PEAR::popErrorHandling();
+                        if (is_array($err)) {
+                            $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage();
+                            // "install" self, so all dependencies will work transparently
+                            $this->_registry->addPackage2($this);
+                            $installer->log(0, 'Download and install of binary extension "' .
+                                $this->_registry->parsedPackageNameToString(
+                                    array('channel' => $pf->getChannel(),
+                                          'package' => $pf->getPackage()), true) . '" successful');
+                            $a = array($ret[0], $err);
+                            return $a;
+                        }
+                        $installer->log(0, 'Download and install of binary extension "' .
+                            $this->_registry->parsedPackageNameToString(
+                                    array('channel' => $pf->getChannel(),
+                                          'package' => $pf->getPackage()), true) . '" failed');
+                    }
+                }
+            }
+        }
+        $a = false;
+        return $a;
+    }
+
+    /**
+     * @return string|false Extension name
+     */
+    function getProvidesExtension()
+    {
+        if (in_array($this->getPackageType(),
+              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
+            if (isset($this->_packageInfo['providesextension'])) {
+                return $this->_packageInfo['providesextension'];
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @param string Extension name
+     * @return bool
+     */
+    function isExtension($extension)
+    {
+        if (in_array($this->getPackageType(),
+              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
+            return $this->_packageInfo['providesextension'] == $extension;
+        }
+        return false;
+    }
+
+    /**
+     * Tests whether every part of the package.xml 1.0 is represented in
+     * this package.xml 2.0
+     * @param PEAR_PackageFile_v1
+     * @return bool
+     */
+    function isEquivalent($pf1)
+    {
+        if (!$pf1) {
+            return true;
+        }
+        if ($this->getPackageType() == 'bundle') {
+            return false;
+        }
+        $this->_stack->getErrors(true);
+        if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
+            return false;
+        }
+        $pass = true;
+        if ($pf1->getPackage() != $this->getPackage()) {
+            $this->_differentPackage($pf1->getPackage());
+            $pass = false;
+        }
+        if ($pf1->getVersion() != $this->getVersion()) {
+            $this->_differentVersion($pf1->getVersion());
+            $pass = false;
+        }
+        if (trim($pf1->getSummary()) != $this->getSummary()) {
+            $this->_differentSummary($pf1->getSummary());
+            $pass = false;
+        }
+        if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
+              preg_replace('/\s+/', '', $this->getDescription())) {
+            $this->_differentDescription($pf1->getDescription());
+            $pass = false;
+        }
+        if ($pf1->getState() != $this->getState()) {
+            $this->_differentState($pf1->getState());
+            $pass = false;
+        }
+        if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
+              preg_replace('/\s+/', '', $pf1->getNotes()))) {
+            $this->_differentNotes($pf1->getNotes());
+            $pass = false;
+        }
+        $mymaintainers = $this->getMaintainers();
+        $yourmaintainers = $pf1->getMaintainers();
+        for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) {
+            $reset = false;
+            for ($i2 = 0; $i2 < count($mymaintainers); $i2++) {
+                if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
+                    if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
+                        $this->_differentRole($mymaintainers[$i2]['handle'],
+                            $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
+                        $pass = false;
+                    }
+                    if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
+                        $this->_differentEmail($mymaintainers[$i2]['handle'],
+                            $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
+                        $pass = false;
+                    }
+                    if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
+                        $this->_differentName($mymaintainers[$i2]['handle'],
+                            $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
+                        $pass = false;
+                    }
+                    unset($mymaintainers[$i2]);
+                    $mymaintainers = array_values($mymaintainers);
+                    unset($yourmaintainers[$i1]);
+                    $yourmaintainers = array_values($yourmaintainers);
+                    $reset = true;
+                    break;
+                }
+            }
+            if ($reset) {
+                $i1 = -1;
+            }
+        }
+        $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
+        $filelist = $this->getFilelist();
+        foreach ($pf1->getFilelist() as $file => $atts) {
+            if (!isset($filelist[$file])) {
+                $this->_missingFile($file);
+                $pass = false;
+            }
+        }
+        return $pass;
+    }
+
+    function _differentPackage($package)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
+            'self' => $this->getPackage()),
+            'package.xml 1.0 package "%package%" does not match "%self%"');
+    }
+
+    function _differentVersion($version)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
+            'self' => $this->getVersion()),
+            'package.xml 1.0 version "%version%" does not match "%self%"');
+    }
+
+    function _differentState($state)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
+            'self' => $this->getState()),
+            'package.xml 1.0 state "%state%" does not match "%self%"');
+    }
+
+    function _differentRole($handle, $role, $selfrole)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
+            'role' => $role, 'self' => $selfrole),
+            'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
+    }
+
+    function _differentEmail($handle, $email, $selfemail)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
+            'email' => $email, 'self' => $selfemail),
+            'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
+    }
+
+    function _differentName($handle, $name, $selfname)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
+            'name' => $name, 'self' => $selfname),
+            'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
+    }
+
+    function _unmatchedMaintainers($my, $yours)
+    {
+        if ($my) {
+            array_walk($my, create_function('&$i, $k', '$i = $i["handle"];'));
+            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
+                'package.xml 2.0 has unmatched extra maintainers "%handles%"');
+        }
+        if ($yours) {
+            array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];'));
+            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
+                'package.xml 1.0 has unmatched extra maintainers "%handles%"');
+        }
+    }
+
+    function _differentNotes($notes)
+    {
+        $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
+        $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
+            substr($this->getNotes(), 0, 24) . '...';
+        $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
+            'self' => $truncmynotes),
+            'package.xml 1.0 release notes "%notes%" do not match "%self%"');
+    }
+
+    function _differentSummary($summary)
+    {
+        $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
+        $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
+            substr($this->getsummary(), 0, 24) . '...';
+        $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
+            'self' => $truncmysummary),
+            'package.xml 1.0 summary "%summary%" does not match "%self%"');
+    }
+
+    function _differentDescription($description)
+    {
+        $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
+        $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
+            substr($this->getdescription(), 0, 24) . '...');
+        $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
+            'self' => $truncmydescription),
+            'package.xml 1.0 description "%description%" does not match "%self%"');
+    }
+
+    function _missingFile($file)
+    {
+        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
+            'package.xml 1.0 file "%file%" is not present in <contents>');
+    }
+
+    /**
+     * WARNING - do not use this function unless you know what you're doing
+     */
+    function setRawState($state)
+    {
+        if (!isset($this->_packageInfo['stability'])) {
+            $this->_packageInfo['stability'] = array();
+        }
+        $this->_packageInfo['stability']['release'] = $state;
+    }
+
+    /**
+     * WARNING - do not use this function unless you know what you're doing
+     */
+    function setRawCompatible($compatible)
+    {
+        $this->_packageInfo['compatible'] = $compatible;
+    }
+
+    /**
+     * WARNING - do not use this function unless you know what you're doing
+     */
+    function setRawPackage($package)
+    {
+        $this->_packageInfo['name'] = $package;
+    }
+
+    /**
+     * WARNING - do not use this function unless you know what you're doing
+     */
+    function setRawChannel($channel)
+    {
+        $this->_packageInfo['channel'] = $channel;
+    }
+
+    function setRequestedGroup($group)
+    {
+        $this->_requestedGroup = $group;
+    }
+
+    function getRequestedGroup()
+    {
+        if (isset($this->_requestedGroup)) {
+            return $this->_requestedGroup;
+        }
+        return false;
+    }
+
+    /**
+     * For saving in the registry.
+     *
+     * Set the last version that was installed
+     * @param string
+     */
+    function setLastInstalledVersion($version)
+    {
+        $this->_packageInfo['_lastversion'] = $version;
+    }
+
+    /**
+     * @return string|false
+     */
+    function getLastInstalledVersion()
+    {
+        if (isset($this->_packageInfo['_lastversion'])) {
+            return $this->_packageInfo['_lastversion'];
+        }
+        return false;
+    }
+
+    /**
+     * Determines whether this package.xml has post-install scripts or not
+     * @return array|false
+     */
+    function listPostinstallScripts()
+    {
+        $filelist = $this->getFilelist();
+        $contents = $this->getContents();
+        $contents = $contents['dir']['file'];
+        if (!is_array($contents) || !isset($contents[0])) {
+            $contents = array($contents);
+        }
+        $taskfiles = array();
+        foreach ($contents as $file) {
+            $atts = $file['attribs'];
+            unset($file['attribs']);
+            if (count($file)) {
+                $taskfiles[$atts['name']] = $file;
+            }
+        }
+        $common = new PEAR_Common;
+        $common->debug = $this->_config->get('verbose');
+        $this->_scripts = array();
+        $ret = array();
+        foreach ($taskfiles as $name => $tasks) {
+            if (!isset($filelist[$name])) {
+                // ignored files will not be in the filelist
+                continue;
+            }
+            $atts = $filelist[$name];
+            foreach ($tasks as $tag => $raw) {
+                $task = $this->getTask($tag);
+                $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL);
+                if ($task->isScript()) {
+                    $ret[] = $filelist[$name]['installed_as'];
+                }
+            }
+        }
+        if (count($ret)) {
+            return $ret;
+        }
+        return false;
+    }
+
+    /**
+     * Initialize post-install scripts for running
+     *
+     * This method can be used to detect post-install scripts, as the return value
+     * indicates whether any exist
+     * @return bool
+     */
+    function initPostinstallScripts()
+    {
+        $filelist = $this->getFilelist();
+        $contents = $this->getContents();
+        $contents = $contents['dir']['file'];
+        if (!is_array($contents) || !isset($contents[0])) {
+            $contents = array($contents);
+        }
+        $taskfiles = array();
+        foreach ($contents as $file) {
+            $atts = $file['attribs'];
+            unset($file['attribs']);
+            if (count($file)) {
+                $taskfiles[$atts['name']] = $file;
+            }
+        }
+        $common = new PEAR_Common;
+        $common->debug = $this->_config->get('verbose');
+        $this->_scripts = array();
+        foreach ($taskfiles as $name => $tasks) {
+            if (!isset($filelist[$name])) {
+                // file was not installed due to installconditions
+                continue;
+            }
+            $atts = $filelist[$name];
+            foreach ($tasks as $tag => $raw) {
+                $taskname = $this->getTask($tag);
+                $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
+                if (!$task->isScript()) {
+                    continue; // scripts are only handled after installation
+                }
+                $lastversion = isset($this->_packageInfo['_lastversion']) ?
+                    $this->_packageInfo['_lastversion'] : null;
+                $task->init($raw, $atts, $lastversion);
+                $res = $task->startSession($this, $atts['installed_as']);
+                if (!$res) {
+                    continue; // skip this file
+                }
+                if (PEAR::isError($res)) {
+                    return $res;
+                }
+                $assign = &$task;
+                $this->_scripts[] = &$assign;
+            }
+        }
+        if (count($this->_scripts)) {
+            return true;
+        }
+        return false;
+    }
+
+    function runPostinstallScripts()
+    {
+        if ($this->initPostinstallScripts()) {
+            $ui = &PEAR_Frontend::singleton();
+            if ($ui) {
+                $ui->runPostinstallScripts($this->_scripts, $this);
+            }
+        }
+    }
+
+
+    /**
+     * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
+     * <file> tags.
+     */
+    function flattenFilelist()
+    {
+        if (isset($this->_packageInfo['bundle'])) {
+            return;
+        }
+        $filelist = array();
+        if (isset($this->_packageInfo['contents']['dir']['dir'])) {
+            $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
+            if (!isset($filelist[1])) {
+                $filelist = $filelist[0];
+            }
+            $this->_packageInfo['contents']['dir']['file'] = $filelist;
+            unset($this->_packageInfo['contents']['dir']['dir']);
+        } else {
+            // else already flattened but check for baseinstalldir propagation
+            if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
+                if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
+                    foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
+                        if (isset($file['attribs']['baseinstalldir'])) {
+                            continue;
+                        }
+                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
+                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
+                    }
+                } else {
+                    if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
+                       $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
+                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @param array the final flattened file list
+     * @param array the current directory being processed
+     * @param string|false any recursively inherited baeinstalldir attribute
+     * @param string private recursion variable
+     * @return array
+     * @access protected
+     */
+    function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
+    {
+        if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
+            $baseinstall = $dir['attribs']['baseinstalldir'];
+        }
+        if (isset($dir['dir'])) {
+            if (!isset($dir['dir'][0])) {
+                $dir['dir'] = array($dir['dir']);
+            }
+            foreach ($dir['dir'] as $subdir) {
+                if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
+                    $name = '*unknown*';
+                } else {
+                    $name = $subdir['attribs']['name'];
+                }
+                $newpath = empty($path) ? $name :
+                    $path . '/' . $name;
+                $this->_getFlattenedFilelist($files, $subdir,
+                    $baseinstall, $newpath);
+            }
+        }
+        if (isset($dir['file'])) {
+            if (!isset($dir['file'][0])) {
+                $dir['file'] = array($dir['file']);
+            }
+            foreach ($dir['file'] as $file) {
+                $attrs = $file['attribs'];
+                $name = $attrs['name'];
+                if ($baseinstall && !isset($attrs['baseinstalldir'])) {
+                    $attrs['baseinstalldir'] = $baseinstall;
+                }
+                $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
+                $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
+                    $attrs['name']);
+                $file['attribs'] = $attrs;
+                $files[] = $file;
+            }
+        }
+    }
+
+    function setConfig(&$config)
+    {
+        $this->_config = &$config;
+        $this->_registry = &$config->getRegistry();
+    }
+
+    function setLogger(&$logger)
+    {
+        if (!is_object($logger) || !method_exists($logger, 'log')) {
+            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
+        }
+        $this->_logger = &$logger;
+    }
+
+    /**
+     * WARNING - do not use this function directly unless you know what you're doing
+     */
+    function setDeps($deps)
+    {
+        $this->_packageInfo['dependencies'] = $deps;
+    }
+
+    /**
+     * WARNING - do not use this function directly unless you know what you're doing
+     */
+    function setCompatible($compat)
+    {
+        $this->_packageInfo['compatible'] = $compat;
+    }
+
+    function setPackagefile($file, $archive = false)
+    {
+        $this->_packageFile = $file;
+        $this->_archiveFile = $archive ? $archive : $file;
+    }
+
+    /**
+     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
+     * @param boolean determines whether to purge the error stack after retrieving
+     * @return array
+     */
+    function getValidationWarnings($purge = true)
+    {
+        return $this->_stack->getErrors($purge);
+    }
+
+    function getPackageFile()
+    {
+        return $this->_packageFile;
+    }
+
+    function getArchiveFile()
+    {
+        return $this->_archiveFile;
+    }
+
+
+    /**
+     * Directly set the array that defines this packagefile
+     *
+     * WARNING: no validation.  This should only be performed by internal methods
+     * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
+     * @param array
+     */
+    function fromArray($pinfo)
+    {
+        unset($pinfo['old']);
+        unset($pinfo['xsdversion']);
+        // If the changelog isn't an array then it was passed in as an empty tag
+        if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) {
+          unset($pinfo['changelog']);
+        }
+        $this->_incomplete = false;
+        $this->_packageInfo = $pinfo;
+    }
+
+    function isIncomplete()
+    {
+        return $this->_incomplete;
+    }
+
+    /**
+     * @return array
+     */
+    function toArray($forreg = false)
+    {
+        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
+            return false;
+        }
+        return $this->getArray($forreg);
+    }
+
+    function getArray($forReg = false)
+    {
+        if ($forReg) {
+            $arr = $this->_packageInfo;
+            $arr['old'] = array();
+            $arr['old']['version'] = $this->getVersion();
+            $arr['old']['release_date'] = $this->getDate();
+            $arr['old']['release_state'] = $this->getState();
+            $arr['old']['release_license'] = $this->getLicense();
+            $arr['old']['release_notes'] = $this->getNotes();
+            $arr['old']['release_deps'] = $this->getDeps();
+            $arr['old']['maintainers'] = $this->getMaintainers();
+            $arr['xsdversion'] = '2.0';
+            return $arr;
+        } else {
+            $info = $this->_packageInfo;
+            unset($info['dirtree']);
+            if (isset($info['_lastversion'])) {
+                unset($info['_lastversion']);
+            }
+            if (isset($info['#binarypackage'])) {
+                unset($info['#binarypackage']);
+            }
+            return $info;
+        }
+    }
+
+    function packageInfo($field)
+    {
+        $arr = $this->getArray(true);
+        if ($field == 'state') {
+            return $arr['stability']['release'];
+        }
+        if ($field == 'api-version') {
+            return $arr['version']['api'];
+        }
+        if ($field == 'api-state') {
+            return $arr['stability']['api'];
+        }
+        if (isset($arr['old'][$field])) {
+            if (!is_string($arr['old'][$field])) {
+                return null;
+            }
+            return $arr['old'][$field];
+        }
+        if (isset($arr[$field])) {
+            if (!is_string($arr[$field])) {
+                return null;
+            }
+            return $arr[$field];
+        }
+        return null;
+    }
+
+    function getName()
+    {
+        return $this->getPackage();
+    }
+
+    function getPackage()
+    {
+        if (isset($this->_packageInfo['name'])) {
+            return $this->_packageInfo['name'];
+        }
+        return false;
+    }
+
+    function getChannel()
+    {
+        if (isset($this->_packageInfo['uri'])) {
+            return '__uri';
+        }
+        if (isset($this->_packageInfo['channel'])) {
+            return strtolower($this->_packageInfo['channel']);
+        }
+        return false;
+    }
+
+    function getUri()
+    {
+        if (isset($this->_packageInfo['uri'])) {
+            return $this->_packageInfo['uri'];
+        }
+        return false;
+    }
+
+    function getExtends()
+    {
+        if (isset($this->_packageInfo['extends'])) {
+            return $this->_packageInfo['extends'];
+        }
+        return false;
+    }
+
+    function getSummary()
+    {
+        if (isset($this->_packageInfo['summary'])) {
+            return $this->_packageInfo['summary'];
+        }
+        return false;
+    }
+
+    function getDescription()
+    {
+        if (isset($this->_packageInfo['description'])) {
+            return $this->_packageInfo['description'];
+        }
+        return false;
+    }
+
+    function getMaintainers($raw = false)
+    {
+        if (!isset($this->_packageInfo['lead'])) {
+            return false;
+        }
+        if ($raw) {
+            $ret = array('lead' => $this->_packageInfo['lead']);
+            (isset($this->_packageInfo['developer'])) ?
+                $ret['developer'] = $this->_packageInfo['developer'] :null;
+            (isset($this->_packageInfo['contributor'])) ?
+                $ret['contributor'] = $this->_packageInfo['contributor'] :null;
+            (isset($this->_packageInfo['helper'])) ?
+                $ret['helper'] = $this->_packageInfo['helper'] :null;
+            return $ret;
+        } else {
+            $ret = array();
+            $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
+                array($this->_packageInfo['lead']);
+            foreach ($leads as $lead) {
+                $s = $lead;
+                $s['handle'] = $s['user'];
+                unset($s['user']);
+                $s['role'] = 'lead';
+                $ret[] = $s;
+            }
+            if (isset($this->_packageInfo['developer'])) {
+                $leads = isset($this->_packageInfo['developer'][0]) ?
+                    $this->_packageInfo['developer'] :
+                    array($this->_packageInfo['developer']);
+                foreach ($leads as $maintainer) {
+                    $s = $maintainer;
+                    $s['handle'] = $s['user'];
+                    unset($s['user']);
+                    $s['role'] = 'developer';
+                    $ret[] = $s;
+                }
+            }
+            if (isset($this->_packageInfo['contributor'])) {
+                $leads = isset($this->_packageInfo['contributor'][0]) ?
+                    $this->_packageInfo['contributor'] :
+                    array($this->_packageInfo['contributor']);
+                foreach ($leads as $maintainer) {
+                    $s = $maintainer;
+                    $s['handle'] = $s['user'];
+                    unset($s['user']);
+                    $s['role'] = 'contributor';
+                    $ret[] = $s;
+                }
+            }
+            if (isset($this->_packageInfo['helper'])) {
+                $leads = isset($this->_packageInfo['helper'][0]) ?
+                    $this->_packageInfo['helper'] :
+                    array($this->_packageInfo['helper']);
+                foreach ($leads as $maintainer) {
+                    $s = $maintainer;
+                    $s['handle'] = $s['user'];
+                    unset($s['user']);
+                    $s['role'] = 'helper';
+                    $ret[] = $s;
+                }
+            }
+            return $ret;
+        }
+        return false;
+    }
+
+    function getLeads()
+    {
+        if (isset($this->_packageInfo['lead'])) {
+            return $this->_packageInfo['lead'];
+        }
+        return false;
+    }
+
+    function getDevelopers()
+    {
+        if (isset($this->_packageInfo['developer'])) {
+            return $this->_packageInfo['developer'];
+        }
+        return false;
+    }
+
+    function getContributors()
+    {
+        if (isset($this->_packageInfo['contributor'])) {
+            return $this->_packageInfo['contributor'];
+        }
+        return false;
+    }
+
+    function getHelpers()
+    {
+        if (isset($this->_packageInfo['helper'])) {
+            return $this->_packageInfo['helper'];
+        }
+        return false;
+    }
+
+    function setDate($date)
+    {
+        if (!isset($this->_packageInfo['date'])) {
+            // ensure that the extends tag is set up in the right location
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                array('time', 'version',
+                    'stability', 'license', 'notes', 'contents', 'compatible',
+                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
+                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
+                    'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
+        }
+        $this->_packageInfo['date'] = $date;
+        $this->_isValid = 0;
+    }
+
+    function setTime($time)
+    {
+        $this->_isValid = 0;
+        if (!isset($this->_packageInfo['time'])) {
+            // ensure that the time tag is set up in the right location
+            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
+                    array('version',
+                    'stability', 'license', 'notes', 'contents', 'compatible',
+                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
+                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
+                    'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
+        }
+        $this->_packageInfo['time'] = $time;
+    }
+
+    function getDate()
+    {
+        if (isset($this->_packageInfo['date'])) {
+            return $this->_packageInfo['date'];
+        }
+        return false;
+    }
+
+    function getTime()
+    {
+        if (isset($this->_packageInfo['time'])) {
+            return $this->_packageInfo['time'];
+        }
+        return false;
+    }
+
+    /**
+     * @param package|api version category to return
+     */
+    function getVersion($key = 'release')
+    {
+        if (isset($this->_packageInfo['version'][$key])) {
+            return $this->_packageInfo['version'][$key];
+        }
+        return false;
+    }
+
+    function getStability()
+    {
+        if (isset($this->_packageInfo['stability'])) {
+            return $this->_packageInfo['stability'];
+        }
+        return false;
+    }
+
+    function getState($key = 'release')
+    {
+        if (isset($this->_packageInfo['stability'][$key])) {
+            return $this->_packageInfo['stability'][$key];
+        }
+        return false;
+    }
+
+    function getLicense($raw = false)
+    {
+        if (isset($this->_packageInfo['license'])) {
+            if ($raw) {
+                return $this->_packageInfo['license'];
+            }
+            if (is_array($this->_packageInfo['license'])) {
+                return $this->_packageInfo['license']['_content'];
+            } else {
+                return $this->_packageInfo['license'];
+            }
+        }
+        return false;
+    }
+
+    function getLicenseLocation()
+    {
+        if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
+            return false;
+        }
+        return $this->_packageInfo['license']['attribs'];
+    }
+
+    function getNotes()
+    {
+        if (isset($this->_packageInfo['notes'])) {
+            return $this->_packageInfo['notes'];
+        }
+        return false;
+    }
+
+    /**
+     * Return the <usesrole> tag contents, if any
+     * @return array|false
+     */
+    function getUsesrole()
+    {
+        if (isset($this->_packageInfo['usesrole'])) {
+            return $this->_packageInfo['usesrole'];
+        }
+        return false;
+    }
+
+    /**
+     * Return the <usestask> tag contents, if any
+     * @return array|false
+     */
+    function getUsestask()
+    {
+        if (isset($this->_packageInfo['usestask'])) {
+            return $this->_packageInfo['usestask'];
+        }
+        return false;
+    }
+
+    /**
+     * This should only be used to retrieve filenames and install attributes
+     */
+    function getFilelist($preserve = false)
+    {
+        if (isset($this->_packageInfo['filelist']) && !$preserve) {
+            return $this->_packageInfo['filelist'];
+        }
+        $this->flattenFilelist();
+        if ($contents = $this->getContents()) {
+            $ret = array();
+            if (!isset($contents['dir'])) {
+                return false;
+            }
+            if (!isset($contents['dir']['file'][0])) {
+                $contents['dir']['file'] = array($contents['dir']['file']);
+            }
+            foreach ($contents['dir']['file'] as $file) {
+                $name = $file['attribs']['name'];
+                if (!$preserve) {
+                    $file = $file['attribs'];
+                }
+                $ret[$name] = $file;
+            }
+            if (!$preserve) {
+                $this->_packageInfo['filelist'] = $ret;
+            }
+            return $ret;
+        }
+        return false;
+    }
+
+    /**
+     * Return configure options array, if any
+     *
+     * @return array|false
+     */
+    function getConfigureOptions()
+    {
+        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
+            return false;
+        }
+
+        $releases = $this->getReleases();
+        if (isset($releases[0])) {
+            $releases = $releases[0];
+        }
+
+        if (isset($releases['configureoption'])) {
+            if (!isset($releases['configureoption'][0])) {
+                $releases['configureoption'] = array($releases['configureoption']);
+            }
+
+            for ($i = 0; $i < count($releases['configureoption']); $i++) {
+                $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
+            }
+
+            return $releases['configureoption'];
+        }
+
+        return false;
+    }
+
+    /**
+     * This is only used at install-time, after all serialization
+     * is over.
+     */
+    function resetFilelist()
+    {
+        $this->_packageInfo['filelist'] = array();
+    }
+
+    /**
+     * Retrieve a list of files that should be installed on this computer
+     * @return array
+     */
+    function getInstallationFilelist($forfilecheck = false)
+    {
+        $contents = $this->getFilelist(true);
+        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
+            $base = $contents['dir']['attribs']['baseinstalldir'];
+        }
+        if (isset($this->_packageInfo['bundle'])) {
+            return PEAR::raiseError(
+                'Exception: bundles should be handled in download code only');
+        }
+        $release = $this->getReleases();
+        if ($release) {
+            if (!isset($release[0])) {
+                if (!isset($release['installconditions']) && !isset($release['filelist'])) {
+                    if ($forfilecheck) {
+                        return $this->getFilelist();
+                    }
+                    return $contents;
+                }
+                $release = array($release);
+            }
+            $depchecker = &$this->getPEARDependency2($this->_config, array(),
+                array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
+                PEAR_VALIDATE_INSTALLING);
+            foreach ($release as $instance) {
+                if (isset($instance['installconditions'])) {
+                    $installconditions = $instance['installconditions'];
+                    if (is_array($installconditions)) {
+                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                        foreach ($installconditions as $type => $conditions) {
+                            if (!isset($conditions[0])) {
+                                $conditions = array($conditions);
+                            }
+                            foreach ($conditions as $condition) {
+                                $ret = $depchecker->{"validate{$type}Dependency"}($condition);
+                                if (PEAR::isError($ret)) {
+                                    PEAR::popErrorHandling();
+                                    continue 3; // skip this release
+                                }
+                            }
+                        }
+                        PEAR::popErrorHandling();
+                    }
+                }
+                // this is the release to use
+                if (isset($instance['filelist'])) {
+                    // ignore files
+                    if (isset($instance['filelist']['ignore'])) {
+                        $ignore = isset($instance['filelist']['ignore'][0]) ?
+                            $instance['filelist']['ignore'] :
+                            array($instance['filelist']['ignore']);
+                        foreach ($ignore as $ig) {
+                            unset ($contents[$ig['attribs']['name']]);
+                        }
+                    }
+                    // install files as this name
+                    if (isset($instance['filelist']['install'])) {
+                        $installas = isset($instance['filelist']['install'][0]) ?
+                            $instance['filelist']['install'] :
+                            array($instance['filelist']['install']);
+                        foreach ($installas as $as) {
+                            $contents[$as['attribs']['name']]['attribs']['install-as'] =
+                                $as['attribs']['as'];
+                        }
+                    }
+                }
+                if ($forfilecheck) {
+                    foreach ($contents as $file => $attrs) {
+                        $contents[$file] = $attrs['attribs'];
+                    }
+                }
+                return $contents;
+            }
+        } else { // simple release - no installconditions or install-as
+            if ($forfilecheck) {
+                return $this->getFilelist();
+            }
+            return $contents;
+        }
+        // no releases matched
+        return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
+            'system, extensions installed, or architecture, cannot install');
+    }
+
+    /**
+     * This is only used at install-time, after all serialization
+     * is over.
+     * @param string file name
+     * @param string installed path
+     */
+    function setInstalledAs($file, $path)
+    {
+        if ($path) {
+            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
+        }
+        unset($this->_packageInfo['filelist'][$file]['installed_as']);
+    }
+
+    function getInstalledLocation($file)
+    {
+        if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
+            return $this->_packageInfo['filelist'][$file]['installed_as'];
+        }
+        return false;
+    }
+
+    /**
+     * This is only used at install-time, after all serialization
+     * is over.
+     */
+    function installedFile($file, $atts)
+    {
+        if (isset($this->_packageInfo['filelist'][$file])) {
+            $this->_packageInfo['filelist'][$file] =
+                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
+        } else {
+            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
+        }
+    }
+
+    /**
+     * Retrieve the contents tag
+     */
+    function getContents()
+    {
+        if (isset($this->_packageInfo['contents'])) {
+            return $this->_packageInfo['contents'];
+        }
+        return false;
+    }
+
+    /**
+     * @param string full path to file
+     * @param string attribute name
+     * @param string attribute value
+     * @param int risky but fast - use this to choose a file based on its position in the list
+     *            of files.  Index is zero-based like PHP arrays.
+     * @return bool success of operation
+     */
+    function setFileAttribute($filename, $attr, $value, $index = false)
+    {
+        $this->_isValid = 0;
+        if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
+            $this->_filesValid = false;
+        }
+        if ($index !== false &&
+              isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
+            $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
+            return true;
+        }
+        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
+            return false;
+        }
+        $files = $this->_packageInfo['contents']['dir']['file'];
+        if (!isset($files[0])) {
+            $files = array($files);
+            $ind = false;
+        } else {
+            $ind = true;
+        }
+        foreach ($files as $i => $file) {
+            if (isset($file['attribs'])) {
+                if ($file['attribs']['name'] == $filename) {
+                    if ($ind) {
+                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
+                    } else {
+                        $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
+                    }
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    function setDirtree($path)
+    {
+        if (!isset($this->_packageInfo['dirtree'])) {
+            $this->_packageInfo['dirtree'] = array();
+        }
+        $this->_packageInfo['dirtree'][$path] = true;
+    }
+
+    function getDirtree()
+    {
+        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
+            return $this->_packageInfo['dirtree'];
+        }
+        return false;
+    }
+
+    function resetDirtree()
+    {
+        unset($this->_packageInfo['dirtree']);
+    }
+
+    /**
+     * Determines whether this package claims it is compatible with the version of
+     * the package that has a recommended version dependency
+     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
+     * @return boolean
+     */
+    function isCompatible($pf)
+    {
+        if (!isset($this->_packageInfo['compatible'])) {
+            return false;
+        }
+        if (!isset($this->_packageInfo['channel'])) {
+            return false;
+        }
+        $me = $pf->getVersion();
+        $compatible = $this->_packageInfo['compatible'];
+        if (!isset($compatible[0])) {
+            $compatible = array($compatible);
+        }
+        $found = false;
+        foreach ($compatible as $info) {
+            if (strtolower($info['name']) == strtolower($pf->getPackage())) {
+                if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
+                    $found = true;
+                    break;
+                }
+            }
+        }
+        if (!$found) {
+            return false;
+        }
+        if (isset($info['exclude'])) {
+            if (!isset($info['exclude'][0])) {
+                $info['exclude'] = array($info['exclude']);
+            }
+            foreach ($info['exclude'] as $exclude) {
+                if (version_compare($me, $exclude, '==')) {
+                    return false;
+                }
+            }
+        }
+        if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return array|false
+     */
+    function getCompatible()
+    {
+        if (isset($this->_packageInfo['compatible'])) {
+            return $this->_packageInfo['compatible'];
+        }
+        return false;
+    }
+
+    function getDependencies()
+    {
+        if (isset($this->_packageInfo['dependencies'])) {
+            return $this->_packageInfo['dependencies'];
+        }
+        return false;
+    }
+
+    function isSubpackageOf($p)
+    {
+        return $p->isSubpackage($this);
+    }
+
+    /**
+     * Determines whether the passed in package is a subpackage of this package.
+     *
+     * No version checking is done, only name verification.
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @return bool
+     */
+    function isSubpackage($p)
+    {
+        $sub = array();
+        if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
+            $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
+            if (!isset($sub[0])) {
+                $sub = array($sub);
+            }
+        }
+        if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
+            $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
+            if (!isset($sub1[0])) {
+                $sub1 = array($sub1);
+            }
+            $sub = array_merge($sub, $sub1);
+        }
+        if (isset($this->_packageInfo['dependencies']['group'])) {
+            $group = $this->_packageInfo['dependencies']['group'];
+            if (!isset($group[0])) {
+                $group = array($group);
+            }
+            foreach ($group as $deps) {
+                if (isset($deps['subpackage'])) {
+                    $sub2 = $deps['subpackage'];
+                    if (!isset($sub2[0])) {
+                        $sub2 = array($sub2);
+                    }
+                    $sub = array_merge($sub, $sub2);
+                }
+            }
+        }
+        foreach ($sub as $dep) {
+            if (strtolower($dep['name']) == strtolower($p->getPackage())) {
+                if (isset($dep['channel'])) {
+                    if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
+                        return true;
+                    }
+                } else {
+                    if ($dep['uri'] == $p->getURI()) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    function dependsOn($package, $channel)
+    {
+        if (!($deps = $this->getDependencies())) {
+            return false;
+        }
+        foreach (array('package', 'subpackage') as $type) {
+            foreach (array('required', 'optional') as $needed) {
+                if (isset($deps[$needed][$type])) {
+                    if (!isset($deps[$needed][$type][0])) {
+                        $deps[$needed][$type] = array($deps[$needed][$type]);
+                    }
+                    foreach ($deps[$needed][$type] as $dep) {
+                        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
+                        if (strtolower($dep['name']) == strtolower($package) &&
+                              $depchannel == $channel) {
+                            return true;
+                        }
+                    }
+                }
+            }
+            if (isset($deps['group'])) {
+                if (!isset($deps['group'][0])) {
+                    $dep['group'] = array($deps['group']);
+                }
+                foreach ($deps['group'] as $group) {
+                    if (isset($group[$type])) {
+                        if (!is_array($group[$type])) {
+                            $group[$type] = array($group[$type]);
+                        }
+                        foreach ($group[$type] as $dep) {
+                            $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
+                            if (strtolower($dep['name']) == strtolower($package) &&
+                                  $depchannel == $channel) {
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get the contents of a dependency group
+     * @param string
+     * @return array|false
+     */
+    function getDependencyGroup($name)
+    {
+        $name = strtolower($name);
+        if (!isset($this->_packageInfo['dependencies']['group'])) {
+            return false;
+        }
+        $groups = $this->_packageInfo['dependencies']['group'];
+        if (!isset($groups[0])) {
+            $groups = array($groups);
+        }
+        foreach ($groups as $group) {
+            if (strtolower($group['attribs']['name']) == $name) {
+                return $group;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Retrieve a partial package.xml 1.0 representation of dependencies
+     *
+     * a very limited representation of dependencies is returned by this method.
+     * The <exclude> tag for excluding certain versions of a dependency is
+     * completely ignored.  In addition, dependency groups are ignored, with the
+     * assumption that all dependencies in dependency groups are also listed in
+     * the optional group that work with all dependency groups
+     * @param boolean return package.xml 2.0 <dependencies> tag
+     * @return array|false
+     */
+    function getDeps($raw = false, $nopearinstaller = false)
+    {
+        if (isset($this->_packageInfo['dependencies'])) {
+            if ($raw) {
+                return $this->_packageInfo['dependencies'];
+            }
+            $ret = array();
+            $map = array(
+                'php' => 'php',
+                'package' => 'pkg',
+                'subpackage' => 'pkg',
+                'extension' => 'ext',
+                'os' => 'os',
+                'pearinstaller' => 'pkg',
+                );
+            foreach (array('required', 'optional') as $type) {
+                $optional = ($type == 'optional') ? 'yes' : 'no';
+                if (!isset($this->_packageInfo['dependencies'][$type])
+                    || empty($this->_packageInfo['dependencies'][$type])) {
+                    continue;
+                }
+                foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
+                    if ($dtype == 'pearinstaller' && $nopearinstaller) {
+                        continue;
+                    }
+                    if (!isset($deps[0])) {
+                        $deps = array($deps);
+                    }
+                    foreach ($deps as $dep) {
+                        if (!isset($map[$dtype])) {
+                            // no support for arch type
+                            continue;
+                        }
+                        if ($dtype == 'pearinstaller') {
+                            $dep['name'] = 'PEAR';
+                            $dep['channel'] = 'pear.php.net';
+                        }
+                        $s = array('type' => $map[$dtype]);
+                        if (isset($dep['channel'])) {
+                            $s['channel'] = $dep['channel'];
+                        }
+                        if (isset($dep['uri'])) {
+                            $s['uri'] = $dep['uri'];
+                        }
+                        if (isset($dep['name'])) {
+                            $s['name'] = $dep['name'];
+                        }
+                        if (isset($dep['conflicts'])) {
+                            $s['rel'] = 'not';
+                        } else {
+                            if (!isset($dep['min']) &&
+                                  !isset($dep['max'])) {
+                                $s['rel'] = 'has';
+                                $s['optional'] = $optional;
+                            } elseif (isset($dep['min']) &&
+                                  isset($dep['max'])) {
+                                $s['rel'] = 'ge';
+                                $s1 = $s;
+                                $s1['rel'] = 'le';
+                                $s['version'] = $dep['min'];
+                                $s1['version'] = $dep['max'];
+                                if (isset($dep['channel'])) {
+                                    $s1['channel'] = $dep['channel'];
+                                }
+                                if ($dtype != 'php') {
+                                    $s['name'] = $dep['name'];
+                                    $s1['name'] = $dep['name'];
+                                }
+                                $s['optional'] = $optional;
+                                $s1['optional'] = $optional;
+                                $ret[] = $s1;
+                            } elseif (isset($dep['min'])) {
+                                if (isset($dep['exclude']) &&
+                                      $dep['exclude'] == $dep['min']) {
+                                    $s['rel'] = 'gt';
+                                } else {
+                                    $s['rel'] = 'ge';
+                                }
+                                $s['version'] = $dep['min'];
+                                $s['optional'] = $optional;
+                                if ($dtype != 'php') {
+                                    $s['name'] = $dep['name'];
+                                }
+                            } elseif (isset($dep['max'])) {
+                                if (isset($dep['exclude']) &&
+                                      $dep['exclude'] == $dep['max']) {
+                                    $s['rel'] = 'lt';
+                                } else {
+                                    $s['rel'] = 'le';
+                                }
+                                $s['version'] = $dep['max'];
+                                $s['optional'] = $optional;
+                                if ($dtype != 'php') {
+                                    $s['name'] = $dep['name'];
+                                }
+                            }
+                        }
+                        $ret[] = $s;
+                    }
+                }
+            }
+            if (count($ret)) {
+                return $ret;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
+     */
+    function getPackageType()
+    {
+        if (isset($this->_packageInfo['phprelease'])) {
+            return 'php';
+        }
+        if (isset($this->_packageInfo['extsrcrelease'])) {
+            return 'extsrc';
+        }
+        if (isset($this->_packageInfo['extbinrelease'])) {
+            return 'extbin';
+        }
+        if (isset($this->_packageInfo['zendextsrcrelease'])) {
+            return 'zendextsrc';
+        }
+        if (isset($this->_packageInfo['zendextbinrelease'])) {
+            return 'zendextbin';
+        }
+        if (isset($this->_packageInfo['bundle'])) {
+            return 'bundle';
+        }
+        return false;
+    }
+
+    /**
+     * @return array|false
+     */
+    function getReleases()
+    {
+        $type = $this->getPackageType();
+        if ($type != 'bundle') {
+            $type .= 'release';
+        }
+        if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
+            return $this->_packageInfo[$type];
+        }
+        return false;
+    }
+
+    /**
+     * @return array
+     */
+    function getChangelog()
+    {
+        if (isset($this->_packageInfo['changelog'])) {
+            return $this->_packageInfo['changelog'];
+        }
+        return false;
+    }
+
+    function hasDeps()
+    {
+        return isset($this->_packageInfo['dependencies']);
+    }
+
+    function getPackagexmlVersion()
+    {
+        if (isset($this->_packageInfo['zendextsrcrelease'])) {
+            return '2.1';
+        }
+        if (isset($this->_packageInfo['zendextbinrelease'])) {
+            return '2.1';
+        }
+        return '2.0';
+    }
+
+    /**
+     * @return array|false
+     */
+    function getSourcePackage()
+    {
+        if (isset($this->_packageInfo['extbinrelease']) ||
+              isset($this->_packageInfo['zendextbinrelease'])) {
+            return array('channel' => $this->_packageInfo['srcchannel'],
+                         'package' => $this->_packageInfo['srcpackage']);
+        }
+        return false;
+    }
+
+    function getBundledPackages()
+    {
+        if (isset($this->_packageInfo['bundle'])) {
+            return $this->_packageInfo['contents']['bundledpackage'];
+        }
+        return false;
+    }
+
+    function getLastModified()
+    {
+        if (isset($this->_packageInfo['_lastmodified'])) {
+            return $this->_packageInfo['_lastmodified'];
+        }
+        return false;
+    }
+
+    /**
+     * Get the contents of a file listed within the package.xml
+     * @param string
+     * @return string
+     */
+    function getFileContents($file)
+    {
+        if ($this->_archiveFile == $this->_packageFile) { // unpacked
+            $dir = dirname($this->_packageFile);
+            $file = $dir . DIRECTORY_SEPARATOR . $file;
+            $file = str_replace(array('/', '\\'),
+                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
+            if (file_exists($file) && is_readable($file)) {
+                return implode('', file($file));
+            }
+        } else { // tgz
+            $tar = &new Archive_Tar($this->_archiveFile);
+            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
+            if ($file != 'package.xml' && $file != 'package2.xml') {
+                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
+            }
+            $file = $tar->extractInString($file);
+            $tar->popErrorHandling();
+            if (PEAR::isError($file)) {
+                return PEAR::raiseError("Cannot locate file '$file' in archive");
+            }
+            return $file;
+        }
+    }
+
+    function &getRW()
+    {
+        if (!class_exists('PEAR_PackageFile_v2_rw')) {
+            require_once 'PEAR/PackageFile/v2/rw.php';
+        }
+        $a = new PEAR_PackageFile_v2_rw;
+        foreach (get_object_vars($this) as $name => $unused) {
+            if (!isset($this->$name)) {
+                continue;
+            }
+            if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
+                  $name == '_stack') {
+                $a->$name = &$this->$name;
+            } else {
+                $a->$name = $this->$name;
+            }
+        }
+        return $a;
+    }
+
+    function &getDefaultGenerator()
+    {
+        if (!class_exists('PEAR_PackageFile_Generator_v2')) {
+            require_once 'PEAR/PackageFile/Generator/v2.php';
+        }
+        $a = &new PEAR_PackageFile_Generator_v2($this);
+        return $a;
+    }
+
+    function analyzeSourceCode($file, $string = false)
+    {
+        if (!isset($this->_v2Validator) ||
+              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
+            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
+                require_once 'PEAR/PackageFile/v2/Validator.php';
+            }
+            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
+        }
+        return $this->_v2Validator->analyzeSourceCode($file, $string);
+    }
+
+    function validate($state = PEAR_VALIDATE_NORMAL)
+    {
+        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
+            return false;
+        }
+        if (!isset($this->_v2Validator) ||
+              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
+            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
+                require_once 'PEAR/PackageFile/v2/Validator.php';
+            }
+            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
+        }
+        if (isset($this->_packageInfo['xsdversion'])) {
+            unset($this->_packageInfo['xsdversion']);
+        }
+        return $this->_v2Validator->validate($this, $state);
+    }
+
+    function getTasksNs()
+    {
+        if (!isset($this->_tasksNs)) {
+            if (isset($this->_packageInfo['attribs'])) {
+                foreach ($this->_packageInfo['attribs'] as $name => $value) {
+                    if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
+                        $this->_tasksNs = str_replace('xmlns:', '', $name);
+                        break;
+                    }
+                }
+            }
+        }
+        return $this->_tasksNs;
+    }
+
+    /**
+     * Determine whether a task name is a valid task.  Custom tasks may be defined
+     * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
+     *
+     * Note that this method will auto-load the task class file and test for the existence
+     * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
+     * PEAR_Task_mycustom_task
+     * @param string
+     * @return boolean
+     */
+    function getTask($task)
+    {
+        $this->getTasksNs();
+        // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
+        $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
+        $taskfile = str_replace(' ', '/', ucwords($task));
+        $task = str_replace(array(' ', '/'), '_', ucwords($task));
+        if (class_exists("PEAR_Task_$task")) {
+            return "PEAR_Task_$task";
+        }
+        $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true);
+        if ($fp) {
+            fclose($fp);
+            require_once "PEAR/Task/$taskfile.php";
+            return "PEAR_Task_$task";
+        }
+        return false;
+    }
+
+    /**
+     * Key-friendly array_splice
+     * @param tagname to splice a value in before
+     * @param mixed the value to splice in
+     * @param string the new tag name
+     */
+    function _ksplice($array, $key, $value, $newkey)
+    {
+        $offset = array_search($key, array_keys($array));
+        $after = array_slice($array, $offset);
+        $before = array_slice($array, 0, $offset);
+        $before[$newkey] = $value;
+        return array_merge($before, $after);
+    }
+
+    /**
+     * @param array a list of possible keys, in the order they may occur
+     * @param mixed contents of the new package.xml tag
+     * @param string tag name
+     * @access private
+     */
+    function _insertBefore($array, $keys, $contents, $newkey)
+    {
+        foreach ($keys as $key) {
+            if (isset($array[$key])) {
+                return $array = $this->_ksplice($array, $key, $contents, $newkey);
+            }
+        }
+        $array[$newkey] = $contents;
+        return $array;
+    }
+
+    /**
+     * @param subsection of {@link $_packageInfo}
+     * @param array|string tag contents
+     * @param array format:
+     * <pre>
+     * array(
+     *   tagname => array(list of tag names that follow this one),
+     *   childtagname => array(list of child tag names that follow this one),
+     * )
+     * </pre>
+     *
+     * This allows construction of nested tags
+     * @access private
+     */
+    function _mergeTag($manip, $contents, $order)
+    {
+        if (count($order)) {
+            foreach ($order as $tag => $curorder) {
+                if (!isset($manip[$tag])) {
+                    // ensure that the tag is set up
+                    $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
+                }
+                if (count($order) > 1) {
+                    $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
+                    return $manip;
+                }
+            }
+        } else {
+            return $manip;
+        }
+        if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
+            $manip[$tag][] = $contents;
+        } else {
+            if (!count($manip[$tag])) {
+                $manip[$tag] = $contents;
+            } else {
+                $manip[$tag] = array($manip[$tag]);
+                $manip[$tag][] = $contents;
+            }
+        }
+        return $manip;
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Config.php
===================================================================
--- trunk/library/PEAR/PEAR/Config.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Config.php	(revision 5146)
@@ -0,0 +1,2097 @@
+<?php
+/**
+ * PEAR_Config, customized configuration handling for the PEAR Installer
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Config.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * Required for error handling
+ */
+require_once 'PEAR.php';
+require_once 'PEAR/Registry.php';
+require_once 'PEAR/Installer/Role.php';
+require_once 'System.php';
+
+/**
+ * Last created PEAR_Config instance.
+ * @var object
+ */
+$GLOBALS['_PEAR_Config_instance'] = null;
+if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
+    $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
+} else {
+    $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
+}
+
+// Below we define constants with default values for all configuration
+// parameters except username/password.  All of them can have their
+// defaults set through environment variables.  The reason we use the
+// PHP_ prefix is for some security, PHP protects environment
+// variables starting with PHP_*.
+
+// default channel and preferred mirror is based on whether we are invoked through
+// the "pear" or the "pecl" command
+if (!defined('PEAR_RUNTYPE')) {
+    define('PEAR_RUNTYPE', 'pear');
+}
+
+if (PEAR_RUNTYPE == 'pear') {
+    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net');
+} else {
+    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net');
+}
+
+if (getenv('PHP_PEAR_SYSCONF_DIR')) {
+    define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
+} elseif (getenv('SystemRoot')) {
+    define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
+} else {
+    define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
+}
+
+// Default for master_server
+if (getenv('PHP_PEAR_MASTER_SERVER')) {
+    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
+}
+
+// Default for http_proxy
+if (getenv('PHP_PEAR_HTTP_PROXY')) {
+    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
+} elseif (getenv('http_proxy')) {
+    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
+}
+
+// Default for php_dir
+if (getenv('PHP_PEAR_INSTALL_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
+} else {
+    if (@file_exists($PEAR_INSTALL_DIR) && is_dir($PEAR_INSTALL_DIR)) {
+        define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
+    } else {
+        define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
+    }
+}
+
+// Default for ext_dir
+if (getenv('PHP_PEAR_EXTENSION_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
+} else {
+    if (ini_get('extension_dir')) {
+        define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
+    } elseif (defined('PEAR_EXTENSION_DIR') &&
+              file_exists(PEAR_EXTENSION_DIR) && is_dir(PEAR_EXTENSION_DIR)) {
+        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
+    } elseif (defined('PHP_EXTENSION_DIR')) {
+        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
+    } else {
+        define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
+    }
+}
+
+// Default for doc_dir
+if (getenv('PHP_PEAR_DOC_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_DOC_DIR',
+           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
+}
+
+// Default for bin_dir
+if (getenv('PHP_PEAR_BIN_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
+}
+
+// Default for data_dir
+if (getenv('PHP_PEAR_DATA_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_DATA_DIR',
+           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
+}
+
+// Default for cfg_dir
+if (getenv('PHP_PEAR_CFG_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_CFG_DIR',
+           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg');
+}
+
+// Default for www_dir
+if (getenv('PHP_PEAR_WWW_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_WWW_DIR',
+           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www');
+}
+
+// Default for test_dir
+if (getenv('PHP_PEAR_TEST_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_TEST_DIR',
+           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
+}
+
+// Default for temp_dir
+if (getenv('PHP_PEAR_TEMP_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_TEMP_DIR',
+           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
+           DIRECTORY_SEPARATOR . 'temp');
+}
+
+// Default for cache_dir
+if (getenv('PHP_PEAR_CACHE_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
+           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
+           DIRECTORY_SEPARATOR . 'cache');
+}
+
+// Default for download_dir
+if (getenv('PHP_PEAR_DOWNLOAD_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR',
+           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
+           DIRECTORY_SEPARATOR . 'download');
+}
+
+// Default for php_bin
+if (getenv('PHP_PEAR_PHP_BIN')) {
+    define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
+           DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
+}
+
+// Default for verbose
+if (getenv('PHP_PEAR_VERBOSE')) {
+    define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
+}
+
+// Default for preferred_state
+if (getenv('PHP_PEAR_PREFERRED_STATE')) {
+    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
+}
+
+// Default for umask
+if (getenv('PHP_PEAR_UMASK')) {
+    define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
+}
+
+// Default for cache_ttl
+if (getenv('PHP_PEAR_CACHE_TTL')) {
+    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
+}
+
+// Default for sig_type
+if (getenv('PHP_PEAR_SIG_TYPE')) {
+    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
+}
+
+// Default for sig_bin
+if (getenv('PHP_PEAR_SIG_BIN')) {
+    define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_SIG_BIN',
+           System::which(
+               'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
+}
+
+// Default for sig_keydir
+if (getenv('PHP_PEAR_SIG_KEYDIR')) {
+    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
+           PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
+}
+
+/**
+ * This is a class for storing configuration data, keeping track of
+ * which are system-defined, user-defined or defaulted.
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Config extends PEAR
+{
+    /**
+     * Array of config files used.
+     *
+     * @var array layer => config file
+     */
+    var $files = array(
+        'system' => '',
+        'user' => '',
+        );
+
+    var $layers = array();
+
+    /**
+     * Configuration data, two-dimensional array where the first
+     * dimension is the config layer ('user', 'system' and 'default'),
+     * and the second dimension is keyname => value.
+     *
+     * The order in the first dimension is important!  Earlier
+     * layers will shadow later ones when a config value is
+     * requested (if a 'user' value exists, it will be returned first,
+     * then 'system' and finally 'default').
+     *
+     * @var array layer => array(keyname => value, ...)
+     */
+    var $configuration = array(
+        'user' => array(),
+        'system' => array(),
+        'default' => array(),
+        );
+
+    /**
+     * Configuration values that can be set for a channel
+     *
+     * All other configuration values can only have a global value
+     * @var array
+     * @access private
+     */
+    var $_channelConfigInfo = array(
+        'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir',
+        'test_dir', 'www_dir', 'php_bin', 'php_prefix', 'php_suffix', 'username',
+        'password', 'verbose', 'preferred_state', 'umask', 'preferred_mirror', 'php_ini'
+        );
+
+    /**
+     * Channels that can be accessed
+     * @see setChannels()
+     * @var array
+     * @access private
+     */
+    var $_channels = array('pear.php.net', 'pecl.php.net', '__uri');
+
+    /**
+     * This variable is used to control the directory values returned
+     * @see setInstallRoot();
+     * @var string|false
+     * @access private
+     */
+    var $_installRoot = false;
+
+    /**
+     * If requested, this will always refer to the registry
+     * contained in php_dir
+     * @var PEAR_Registry
+     */
+    var $_registry = array();
+
+    /**
+     * @var array
+     * @access private
+     */
+    var $_regInitialized = array();
+
+    /**
+     * @var bool
+     * @access private
+     */
+    var $_noRegistry = false;
+
+    /**
+     * amount of errors found while parsing config
+     * @var integer
+     * @access private
+     */
+    var $_errorsFound = 0;
+    var $_lastError = null;
+
+    /**
+     * Information about the configuration data.  Stores the type,
+     * default value and a documentation string for each configuration
+     * value.
+     *
+     * @var array layer => array(infotype => value, ...)
+     */
+    var $configuration_info = array(
+        // Channels/Internet Access
+        'default_channel' => array(
+            'type' => 'string',
+            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
+            'doc' => 'the default channel to use for all non explicit commands',
+            'prompt' => 'Default Channel',
+            'group' => 'Internet Access',
+            ),
+        'preferred_mirror' => array(
+            'type' => 'string',
+            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
+            'doc' => 'the default server or mirror to use for channel actions',
+            'prompt' => 'Default Channel Mirror',
+            'group' => 'Internet Access',
+            ),
+        'remote_config' => array(
+            'type' => 'password',
+            'default' => '',
+            'doc' => 'ftp url of remote configuration file to use for synchronized install',
+            'prompt' => 'Remote Configuration File',
+            'group' => 'Internet Access',
+            ),
+        'auto_discover' => array(
+            'type' => 'integer',
+            'default' => 0,
+            'doc' => 'whether to automatically discover new channels',
+            'prompt' => 'Auto-discover new Channels',
+            'group' => 'Internet Access',
+            ),
+        // Internet Access
+        'master_server' => array(
+            'type' => 'string',
+            'default' => 'pear.php.net',
+            'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]',
+            'prompt' => 'PEAR server [DEPRECATED]',
+            'group' => 'Internet Access',
+            ),
+        'http_proxy' => array(
+            'type' => 'string',
+            'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
+            'doc' => 'HTTP proxy (host:port) to use when downloading packages',
+            'prompt' => 'HTTP Proxy Server Address',
+            'group' => 'Internet Access',
+            ),
+        // File Locations
+        'php_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
+            'doc' => 'directory where .php files are installed',
+            'prompt' => 'PEAR directory',
+            'group' => 'File Locations',
+            ),
+        'ext_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
+            'doc' => 'directory where loadable extensions are installed',
+            'prompt' => 'PHP extension directory',
+            'group' => 'File Locations',
+            ),
+        'doc_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
+            'doc' => 'directory where documentation is installed',
+            'prompt' => 'PEAR documentation directory',
+            'group' => 'File Locations',
+            ),
+        'bin_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
+            'doc' => 'directory where executables are installed',
+            'prompt' => 'PEAR executables directory',
+            'group' => 'File Locations',
+            ),
+        'data_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
+            'doc' => 'directory where data files are installed',
+            'prompt' => 'PEAR data directory',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'cfg_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_CFG_DIR,
+            'doc' => 'directory where modifiable configuration files are installed',
+            'prompt' => 'PEAR configuration file directory',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'www_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_WWW_DIR,
+            'doc' => 'directory where www frontend files (html/js) are installed',
+            'prompt' => 'PEAR www files directory',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'test_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
+            'doc' => 'directory where regression tests are installed',
+            'prompt' => 'PEAR test directory',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'cache_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
+            'doc' => 'directory which is used for web service cache',
+            'prompt' => 'PEAR Installer cache directory',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'temp_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR,
+            'doc' => 'directory which is used for all temp files',
+            'prompt' => 'PEAR Installer temp directory',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'download_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR,
+            'doc' => 'directory which is used for all downloaded files',
+            'prompt' => 'PEAR Installer download directory',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'php_bin' => array(
+            'type' => 'file',
+            'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
+            'doc' => 'PHP CLI/CGI binary for executing scripts',
+            'prompt' => 'PHP CLI/CGI binary',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'php_prefix' => array(
+            'type' => 'string',
+            'default' => '',
+            'doc' => '--program-prefix for php_bin\'s ./configure, used for pecl installs',
+            'prompt' => '--program-prefix passed to PHP\'s ./configure',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'php_suffix' => array(
+            'type' => 'string',
+            'default' => '',
+            'doc' => '--program-suffix for php_bin\'s ./configure, used for pecl installs',
+            'prompt' => '--program-suffix passed to PHP\'s ./configure',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'php_ini' => array(
+            'type' => 'file',
+            'default' => '',
+            'doc' => 'location of php.ini in which to enable PECL extensions on install',
+            'prompt' => 'php.ini location',
+            'group' => 'File Locations (Advanced)',
+            ),
+        // Maintainers
+        'username' => array(
+            'type' => 'string',
+            'default' => '',
+            'doc' => '(maintainers) your PEAR account name',
+            'prompt' => 'PEAR username (for maintainers)',
+            'group' => 'Maintainers',
+            ),
+        'password' => array(
+            'type' => 'password',
+            'default' => '',
+            'doc' => '(maintainers) your PEAR account password',
+            'prompt' => 'PEAR password (for maintainers)',
+            'group' => 'Maintainers',
+            ),
+        // Advanced
+        'verbose' => array(
+            'type' => 'integer',
+            'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
+            'doc' => 'verbosity level
+0: really quiet
+1: somewhat quiet
+2: verbose
+3: debug',
+            'prompt' => 'Debug Log Level',
+            'group' => 'Advanced',
+            ),
+        'preferred_state' => array(
+            'type' => 'set',
+            'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
+            'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
+            'valid_set' => array(
+                'stable', 'beta', 'alpha', 'devel', 'snapshot'),
+            'prompt' => 'Preferred Package State',
+            'group' => 'Advanced',
+            ),
+        'umask' => array(
+            'type' => 'mask',
+            'default' => PEAR_CONFIG_DEFAULT_UMASK,
+            'doc' => 'umask used when creating files (Unix-like systems only)',
+            'prompt' => 'Unix file mask',
+            'group' => 'Advanced',
+            ),
+        'cache_ttl' => array(
+            'type' => 'integer',
+            'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
+            'doc' => 'amount of secs where the local cache is used and not updated',
+            'prompt' => 'Cache TimeToLive',
+            'group' => 'Advanced',
+            ),
+        'sig_type' => array(
+            'type' => 'set',
+            'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
+            'doc' => 'which package signature mechanism to use',
+            'valid_set' => array('gpg'),
+            'prompt' => 'Package Signature Type',
+            'group' => 'Maintainers',
+            ),
+        'sig_bin' => array(
+            'type' => 'string',
+            'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
+            'doc' => 'which package signature mechanism to use',
+            'prompt' => 'Signature Handling Program',
+            'group' => 'Maintainers',
+            ),
+        'sig_keyid' => array(
+            'type' => 'string',
+            'default' => '',
+            'doc' => 'which key to use for signing with',
+            'prompt' => 'Signature Key Id',
+            'group' => 'Maintainers',
+            ),
+        'sig_keydir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
+            'doc' => 'directory where signature keys are located',
+            'prompt' => 'Signature Key Directory',
+            'group' => 'Maintainers',
+            ),
+        // __channels is reserved - used for channel-specific configuration
+        );
+
+    /**
+     * Constructor.
+     *
+     * @param string file to read user-defined options from
+     * @param string file to read system-wide defaults from
+     * @param bool   determines whether a registry object "follows"
+     *               the value of php_dir (is automatically created
+     *               and moved when php_dir is changed)
+     * @param bool   if true, fails if configuration files cannot be loaded
+     *
+     * @access public
+     *
+     * @see PEAR_Config::singleton
+     */
+    function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false,
+                         $strict = true)
+    {
+        $this->PEAR();
+        PEAR_Installer_Role::initializeConfig($this);
+        $sl = DIRECTORY_SEPARATOR;
+        if (empty($user_file)) {
+            if (OS_WINDOWS) {
+                $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
+            } else {
+                $user_file = getenv('HOME') . $sl . '.pearrc';
+            }
+        }
+
+        if (empty($system_file)) {
+            $system_file = PEAR_CONFIG_SYSCONFDIR . $sl;
+            if (OS_WINDOWS) {
+                $system_file .= 'pearsys.ini';
+            } else {
+                $system_file .= 'pear.conf';
+            }
+        }
+
+        $this->layers = array_keys($this->configuration);
+        $this->files['user']   = $user_file;
+        $this->files['system'] = $system_file;
+        if ($user_file && file_exists($user_file)) {
+            $this->pushErrorHandling(PEAR_ERROR_RETURN);
+            $this->readConfigFile($user_file, 'user', $strict);
+            $this->popErrorHandling();
+            if ($this->_errorsFound > 0) {
+                return;
+            }
+        }
+
+        if ($system_file && @file_exists($system_file)) {
+            $this->mergeConfigFile($system_file, false, 'system', $strict);
+            if ($this->_errorsFound > 0) {
+                return;
+            }
+
+        }
+
+        if (!$ftp_file) {
+            $ftp_file = $this->get('remote_config');
+        }
+
+        if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) {
+            $this->readFTPConfigFile($ftp_file);
+        }
+
+        foreach ($this->configuration_info as $key => $info) {
+            $this->configuration['default'][$key] = $info['default'];
+        }
+
+        $this->_registry['default'] = &new PEAR_Registry($this->configuration['default']['php_dir']);
+        $this->_registry['default']->setConfig($this, false);
+        $this->_regInitialized['default'] = false;
+        //$GLOBALS['_PEAR_Config_instance'] = &$this;
+    }
+
+    /**
+     * Return the default locations of user and system configuration files
+     * @static
+     */
+    function getDefaultConfigFiles()
+    {
+        $sl = DIRECTORY_SEPARATOR;
+        if (OS_WINDOWS) {
+            return array(
+                'user'   => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini',
+                'system' =>  PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini'
+            );
+        }
+
+        return array(
+            'user'   => getenv('HOME') . $sl . '.pearrc',
+            'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf'
+        );
+    }
+
+    /**
+     * Static singleton method.  If you want to keep only one instance
+     * of this class in use, this method will give you a reference to
+     * the last created PEAR_Config object if one exists, or create a
+     * new object.
+     *
+     * @param string (optional) file to read user-defined options from
+     * @param string (optional) file to read system-wide defaults from
+     *
+     * @return object an existing or new PEAR_Config instance
+     *
+     * @access public
+     *
+     * @see PEAR_Config::PEAR_Config
+     */
+    function &singleton($user_file = '', $system_file = '', $strict = true)
+    {
+        if (is_object($GLOBALS['_PEAR_Config_instance'])) {
+            return $GLOBALS['_PEAR_Config_instance'];
+        }
+
+        $t_conf = &new PEAR_Config($user_file, $system_file, false, $strict);
+        if ($t_conf->_errorsFound > 0) {
+             return $t_conf->lastError;
+        }
+
+        $GLOBALS['_PEAR_Config_instance'] = &$t_conf;
+        return $GLOBALS['_PEAR_Config_instance'];
+    }
+
+    /**
+     * Determine whether any configuration files have been detected, and whether a
+     * registry object can be retrieved from this configuration.
+     * @return bool
+     * @since PEAR 1.4.0a1
+     */
+    function validConfiguration()
+    {
+        if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Reads configuration data from a file.  All existing values in
+     * the config layer are discarded and replaced with data from the
+     * file.
+     * @param string file to read from, if NULL or not specified, the
+     *               last-used file for the same layer (second param) is used
+     * @param string config layer to insert data into ('user' or 'system')
+     * @return bool TRUE on success or a PEAR error on failure
+     */
+    function readConfigFile($file = null, $layer = 'user', $strict = true)
+    {
+        if (empty($this->files[$layer])) {
+            return $this->raiseError("unknown config layer `$layer'");
+        }
+
+        if ($file === null) {
+            $file = $this->files[$layer];
+        }
+
+        $data = $this->_readConfigDataFrom($file);
+        if (PEAR::isError($data)) {
+            if (!$strict) {
+                return true;
+            }
+
+            $this->_errorsFound++;
+            $this->lastError = $data;
+
+            return $data;
+        }
+
+        $this->files[$layer] = $file;
+        $this->_decodeInput($data);
+        $this->configuration[$layer] = $data;
+        $this->_setupChannels();
+        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
+            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
+            $this->_registry[$layer]->setConfig($this, false);
+            $this->_regInitialized[$layer] = false;
+        } else {
+            unset($this->_registry[$layer]);
+        }
+        return true;
+    }
+
+    /**
+     * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini
+     * @return true|PEAR_Error
+     */
+    function readFTPConfigFile($path)
+    {
+        do { // poor man's try
+            if (!class_exists('PEAR_FTP')) {
+                if (!class_exists('PEAR_Common')) {
+                    require_once 'PEAR/Common.php';
+                }
+                if (PEAR_Common::isIncludeable('PEAR/FTP.php')) {
+                    require_once 'PEAR/FTP.php';
+                }
+            }
+
+            if (!class_exists('PEAR_FTP')) {
+                return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config');
+            }
+
+            $this->_ftp = &new PEAR_FTP;
+            $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN);
+            $e = $this->_ftp->init($path);
+            if (PEAR::isError($e)) {
+                $this->_ftp->popErrorHandling();
+                return $e;
+            }
+
+            $tmp = System::mktemp('-d');
+            PEAR_Common::addTempFile($tmp);
+            $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR .
+                'pear.ini', false, FTP_BINARY);
+            if (PEAR::isError($e)) {
+                $this->_ftp->popErrorHandling();
+                return $e;
+            }
+
+            PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini');
+            $this->_ftp->disconnect();
+            $this->_ftp->popErrorHandling();
+            $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini';
+            $e = $this->readConfigFile(null, 'ftp');
+            if (PEAR::isError($e)) {
+                return $e;
+            }
+
+            $fail = array();
+            foreach ($this->configuration_info as $key => $val) {
+                if (in_array($this->getGroup($key),
+                      array('File Locations', 'File Locations (Advanced)')) &&
+                      $this->getType($key) == 'directory') {
+                    // any directory configs must be set for this to work
+                    if (!isset($this->configuration['ftp'][$key])) {
+                        $fail[] = $key;
+                    }
+                }
+            }
+
+            if (!count($fail)) {
+                return true;
+            }
+
+            $fail = '"' . implode('", "', $fail) . '"';
+            unset($this->files['ftp']);
+            unset($this->configuration['ftp']);
+            return PEAR::raiseError('ERROR: Ftp configuration file must set all ' .
+                'directory configuration variables.  These variables were not set: ' .
+                $fail);
+        } while (false); // poor man's catch
+        unset($this->files['ftp']);
+        return PEAR::raiseError('no remote host specified');
+    }
+
+    /**
+     * Reads the existing configurations and creates the _channels array from it
+     */
+    function _setupChannels()
+    {
+        $set = array_flip(array_values($this->_channels));
+        foreach ($this->configuration as $layer => $data) {
+            $i = 1000;
+            if (isset($data['__channels']) && is_array($data['__channels'])) {
+                foreach ($data['__channels'] as $channel => $info) {
+                    $set[$channel] = $i++;
+                }
+            }
+        }
+        $this->_channels = array_values(array_flip($set));
+        $this->setChannels($this->_channels);
+    }
+
+    function deleteChannel($channel)
+    {
+        $ch = strtolower($channel);
+        foreach ($this->configuration as $layer => $data) {
+            if (isset($data['__channels']) && isset($data['__channels'][$ch])) {
+                unset($this->configuration[$layer]['__channels'][$ch]);
+            }
+        }
+
+        $this->_channels = array_flip($this->_channels);
+        unset($this->_channels[$ch]);
+        $this->_channels = array_flip($this->_channels);
+    }
+
+    /**
+     * Merges data into a config layer from a file.  Does the same
+     * thing as readConfigFile, except it does not replace all
+     * existing values in the config layer.
+     * @param string file to read from
+     * @param bool whether to overwrite existing data (default TRUE)
+     * @param string config layer to insert data into ('user' or 'system')
+     * @param string if true, errors are returned if file opening fails
+     * @return bool TRUE on success or a PEAR error on failure
+     */
+    function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true)
+    {
+        if (empty($this->files[$layer])) {
+            return $this->raiseError("unknown config layer `$layer'");
+        }
+
+        if ($file === null) {
+            $file = $this->files[$layer];
+        }
+
+        $data = $this->_readConfigDataFrom($file);
+        if (PEAR::isError($data)) {
+            if (!$strict) {
+                return true;
+            }
+
+            $this->_errorsFound++;
+            $this->lastError = $data;
+
+            return $data;
+        }
+
+        $this->_decodeInput($data);
+        if ($override) {
+            $this->configuration[$layer] =
+                PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data);
+        } else {
+            $this->configuration[$layer] =
+                PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]);
+        }
+
+        $this->_setupChannels();
+        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
+            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
+            $this->_registry[$layer]->setConfig($this, false);
+            $this->_regInitialized[$layer] = false;
+        } else {
+            unset($this->_registry[$layer]);
+        }
+        return true;
+    }
+
+    /**
+     * @param array
+     * @param array
+     * @return array
+     * @static
+     */
+    function arrayMergeRecursive($arr2, $arr1)
+    {
+        $ret = array();
+        foreach ($arr2 as $key => $data) {
+            if (!isset($arr1[$key])) {
+                $ret[$key] = $data;
+                unset($arr1[$key]);
+                continue;
+            }
+            if (is_array($data)) {
+                if (!is_array($arr1[$key])) {
+                    $ret[$key] = $arr1[$key];
+                    unset($arr1[$key]);
+                    continue;
+                }
+                $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]);
+                unset($arr1[$key]);
+            }
+        }
+
+        return array_merge($ret, $arr1);
+    }
+
+    /**
+     * Writes data into a config layer from a file.
+     *
+     * @param string|null file to read from, or null for default
+     * @param string config layer to insert data into ('user' or
+     *               'system')
+     * @param string|null data to write to config file or null for internal data [DEPRECATED]
+     * @return bool TRUE on success or a PEAR error on failure
+     */
+    function writeConfigFile($file = null, $layer = 'user', $data = null)
+    {
+        $this->_lazyChannelSetup($layer);
+        if ($layer == 'both' || $layer == 'all') {
+            foreach ($this->files as $type => $file) {
+                $err = $this->writeConfigFile($file, $type, $data);
+                if (PEAR::isError($err)) {
+                    return $err;
+                }
+            }
+            return true;
+        }
+
+        if (empty($this->files[$layer])) {
+            return $this->raiseError("unknown config file type `$layer'");
+        }
+
+        if ($file === null) {
+            $file = $this->files[$layer];
+        }
+
+        $data = ($data === null) ? $this->configuration[$layer] : $data;
+        $this->_encodeOutput($data);
+        $opt = array('-p', dirname($file));
+        if (!@System::mkDir($opt)) {
+            return $this->raiseError("could not create directory: " . dirname($file));
+        }
+
+        if (file_exists($file) && is_file($file) && !is_writeable($file)) {
+            return $this->raiseError("no write access to $file!");
+        }
+
+        $fp = @fopen($file, "w");
+        if (!$fp) {
+            return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)");
+        }
+
+        $contents = "#PEAR_Config 0.9\n" . serialize($data);
+        if (!@fwrite($fp, $contents)) {
+            return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)");
+        }
+        return true;
+    }
+
+    /**
+     * Reads configuration data from a file and returns the parsed data
+     * in an array.
+     *
+     * @param string file to read from
+     * @return array configuration data or a PEAR error on failure
+     * @access private
+     */
+    function _readConfigDataFrom($file)
+    {
+        $fp = false;
+        if (file_exists($file)) {
+            $fp = @fopen($file, "r");
+        }
+
+        if (!$fp) {
+            return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
+        }
+
+        $size = filesize($file);
+        $rt = get_magic_quotes_runtime();
+        set_magic_quotes_runtime(0);
+        fclose($fp);
+        $contents = file_get_contents($file);
+        if (empty($contents)) {
+            return $this->raiseError('Configuration file "' . $file . '" is empty');
+        }
+
+        set_magic_quotes_runtime($rt);
+
+        $version = false;
+        if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
+            $version = $matches[1];
+            $contents = substr($contents, strlen($matches[0]));
+        } else {
+            // Museum config file
+            if (substr($contents,0,2) == 'a:') {
+                $version = '0.1';
+            }
+        }
+
+        if ($version && version_compare("$version", '1', '<')) {
+            // no '@', it is possible that unserialize
+            // raises a notice but it seems to block IO to
+            // STDOUT if a '@' is used and a notice is raise
+            $data = unserialize($contents);
+
+            if (!is_array($data) && !$data) {
+                if ($contents == serialize(false)) {
+                    $data = array();
+                } else {
+                    $err = $this->raiseError("PEAR_Config: bad data in $file");
+                    return $err;
+                }
+            }
+            if (!is_array($data)) {
+                if (strlen(trim($contents)) > 0) {
+                    $error = "PEAR_Config: bad data in $file";
+                    $err = $this->raiseError($error);
+                    return $err;
+                }
+
+                $data = array();
+            }
+        // add parsing of newer formats here...
+        } else {
+            $err = $this->raiseError("$file: unknown version `$version'");
+            return $err;
+        }
+
+        return $data;
+    }
+
+    /**
+    * Gets the file used for storing the config for a layer
+    *
+    * @param string $layer 'user' or 'system'
+    */
+    function getConfFile($layer)
+    {
+        return $this->files[$layer];
+    }
+
+    /**
+     * @param string Configuration class name, used for detecting duplicate calls
+     * @param array information on a role as parsed from its xml file
+     * @return true|PEAR_Error
+     * @access private
+     */
+    function _addConfigVars($class, $vars)
+    {
+        static $called = array();
+        if (isset($called[$class])) {
+            return;
+        }
+
+        $called[$class] = 1;
+        if (count($vars) > 3) {
+            return $this->raiseError('Roles can only define 3 new config variables or less');
+        }
+
+        foreach ($vars as $name => $var) {
+            if (!is_array($var)) {
+                return $this->raiseError('Configuration information must be an array');
+            }
+
+            if (!isset($var['type'])) {
+                return $this->raiseError('Configuration information must contain a type');
+            } elseif (!in_array($var['type'],
+                    array('string', 'mask', 'password', 'directory', 'file', 'set'))) {
+                  return $this->raiseError(
+                      'Configuration type must be one of directory, file, string, ' .
+                      'mask, set, or password');
+            }
+            if (!isset($var['default'])) {
+                return $this->raiseError(
+                    'Configuration information must contain a default value ("default" index)');
+            }
+
+            if (is_array($var['default'])) {
+                $real_default = '';
+                foreach ($var['default'] as $config_var => $val) {
+                    if (strpos($config_var, 'text') === 0) {
+                        $real_default .= $val;
+                    } elseif (strpos($config_var, 'constant') === 0) {
+                        if (!defined($val)) {
+                            return $this->raiseError(
+                                'Unknown constant "' . $val . '" requested in ' .
+                                'default value for configuration variable "' .
+                                $name . '"');
+                        }
+
+                        $real_default .= constant($val);
+                    } elseif (isset($this->configuration_info[$config_var])) {
+                        $real_default .=
+                            $this->configuration_info[$config_var]['default'];
+                    } else {
+                        return $this->raiseError(
+                            'Unknown request for "' . $config_var . '" value in ' .
+                            'default value for configuration variable "' .
+                            $name . '"');
+                    }
+                }
+                $var['default'] = $real_default;
+            }
+
+            if ($var['type'] == 'integer') {
+                $var['default'] = (integer) $var['default'];
+            }
+
+            if (!isset($var['doc'])) {
+                return $this->raiseError(
+                    'Configuration information must contain a summary ("doc" index)');
+            }
+
+            if (!isset($var['prompt'])) {
+                return $this->raiseError(
+                    'Configuration information must contain a simple prompt ("prompt" index)');
+            }
+
+            if (!isset($var['group'])) {
+                return $this->raiseError(
+                    'Configuration information must contain a simple group ("group" index)');
+            }
+
+            if (isset($this->configuration_info[$name])) {
+                return $this->raiseError('Configuration variable "' . $name .
+                    '" already exists');
+            }
+
+            $this->configuration_info[$name] = $var;
+            // fix bug #7351: setting custom config variable in a channel fails
+            $this->_channelConfigInfo[] = $name;
+        }
+
+        return true;
+    }
+
+    /**
+     * Encodes/scrambles configuration data before writing to files.
+     * Currently, 'password' values will be base64-encoded as to avoid
+     * that people spot cleartext passwords by accident.
+     *
+     * @param array (reference) array to encode values in
+     * @return bool TRUE on success
+     * @access private
+     */
+    function _encodeOutput(&$data)
+    {
+        foreach ($data as $key => $value) {
+            if ($key == '__channels') {
+                foreach ($data['__channels'] as $channel => $blah) {
+                    $this->_encodeOutput($data['__channels'][$channel]);
+                }
+            }
+
+            if (!isset($this->configuration_info[$key])) {
+                continue;
+            }
+
+            $type = $this->configuration_info[$key]['type'];
+            switch ($type) {
+                // we base64-encode passwords so they are at least
+                // not shown in plain by accident
+                case 'password': {
+                    $data[$key] = base64_encode($data[$key]);
+                    break;
+                }
+                case 'mask': {
+                    $data[$key] = octdec($data[$key]);
+                    break;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Decodes/unscrambles configuration data after reading from files.
+     *
+     * @param array (reference) array to encode values in
+     * @return bool TRUE on success
+     * @access private
+     *
+     * @see PEAR_Config::_encodeOutput
+     */
+    function _decodeInput(&$data)
+    {
+        if (!is_array($data)) {
+            return true;
+        }
+
+        foreach ($data as $key => $value) {
+            if ($key == '__channels') {
+                foreach ($data['__channels'] as $channel => $blah) {
+                    $this->_decodeInput($data['__channels'][$channel]);
+                }
+            }
+
+            if (!isset($this->configuration_info[$key])) {
+                continue;
+            }
+
+            $type = $this->configuration_info[$key]['type'];
+            switch ($type) {
+                case 'password': {
+                    $data[$key] = base64_decode($data[$key]);
+                    break;
+                }
+                case 'mask': {
+                    $data[$key] = decoct($data[$key]);
+                    break;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Retrieve the default channel.
+     *
+     * On startup, channels are not initialized, so if the default channel is not
+     * pear.php.net, then initialize the config.
+     * @param string registry layer
+     * @return string|false
+     */
+    function getDefaultChannel($layer = null)
+    {
+        $ret = false;
+        if ($layer === null) {
+            foreach ($this->layers as $layer) {
+                if (isset($this->configuration[$layer]['default_channel'])) {
+                    $ret = $this->configuration[$layer]['default_channel'];
+                    break;
+                }
+            }
+        } elseif (isset($this->configuration[$layer]['default_channel'])) {
+            $ret = $this->configuration[$layer]['default_channel'];
+        }
+
+        if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') {
+            $ret = 'pecl.php.net';
+        }
+
+        if ($ret) {
+            if ($ret != 'pear.php.net') {
+                $this->_lazyChannelSetup();
+            }
+
+            return $ret;
+        }
+
+        return PEAR_CONFIG_DEFAULT_CHANNEL;
+    }
+
+    /**
+     * Returns a configuration value, prioritizing layers as per the
+     * layers property.
+     *
+     * @param string config key
+     * @return mixed the config value, or NULL if not found
+     * @access public
+     */
+    function get($key, $layer = null, $channel = false)
+    {
+        if (!isset($this->configuration_info[$key])) {
+            return null;
+        }
+
+        if ($key == '__channels') {
+            return null;
+        }
+
+        if ($key == 'default_channel') {
+            return $this->getDefaultChannel($layer);
+        }
+
+        if (!$channel) {
+            $channel = $this->getDefaultChannel();
+        } elseif ($channel != 'pear.php.net') {
+            $this->_lazyChannelSetup();
+        }
+        $channel = strtolower($channel);
+
+        $test = (in_array($key, $this->_channelConfigInfo)) ?
+            $this->_getChannelValue($key, $layer, $channel) :
+            null;
+        if ($test !== null) {
+            if ($this->_installRoot) {
+                if (in_array($this->getGroup($key),
+                      array('File Locations', 'File Locations (Advanced)')) &&
+                      $this->getType($key) == 'directory') {
+                    return $this->_prependPath($test, $this->_installRoot);
+                }
+            }
+            return $test;
+        }
+
+        if ($layer === null) {
+            foreach ($this->layers as $layer) {
+                if (isset($this->configuration[$layer][$key])) {
+                    $test = $this->configuration[$layer][$key];
+                    if ($this->_installRoot) {
+                        if (in_array($this->getGroup($key),
+                              array('File Locations', 'File Locations (Advanced)')) &&
+                              $this->getType($key) == 'directory') {
+                            return $this->_prependPath($test, $this->_installRoot);
+                        }
+                    }
+
+                    if ($key == 'preferred_mirror') {
+                        $reg = &$this->getRegistry();
+                        if (is_object($reg)) {
+                            $chan = &$reg->getChannel($channel);
+                            if (PEAR::isError($chan)) {
+                                return $channel;
+                            }
+
+                            if (!$chan->getMirror($test) && $chan->getName() != $test) {
+                                return $channel; // mirror does not exist
+                            }
+                        }
+                    }
+                    return $test;
+                }
+            }
+        } elseif (isset($this->configuration[$layer][$key])) {
+            $test = $this->configuration[$layer][$key];
+            if ($this->_installRoot) {
+                if (in_array($this->getGroup($key),
+                      array('File Locations', 'File Locations (Advanced)')) &&
+                      $this->getType($key) == 'directory') {
+                    return $this->_prependPath($test, $this->_installRoot);
+                }
+            }
+
+            if ($key == 'preferred_mirror') {
+                $reg = &$this->getRegistry();
+                if (is_object($reg)) {
+                    $chan = &$reg->getChannel($channel);
+                    if (PEAR::isError($chan)) {
+                        return $channel;
+                    }
+
+                    if (!$chan->getMirror($test) && $chan->getName() != $test) {
+                        return $channel; // mirror does not exist
+                    }
+                }
+            }
+
+            return $test;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a channel-specific configuration value, prioritizing layers as per the
+     * layers property.
+     *
+     * @param string config key
+     * @return mixed the config value, or NULL if not found
+     * @access private
+     */
+    function _getChannelValue($key, $layer, $channel)
+    {
+        if ($key == '__channels' || $channel == 'pear.php.net') {
+            return null;
+        }
+
+        $ret = null;
+        if ($layer === null) {
+            foreach ($this->layers as $ilayer) {
+                if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) {
+                    $ret = $this->configuration[$ilayer]['__channels'][$channel][$key];
+                    break;
+                }
+            }
+        } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
+            $ret = $this->configuration[$layer]['__channels'][$channel][$key];
+        }
+
+        if ($key != 'preferred_mirror') {
+            return $ret;
+        }
+
+
+        if ($ret !== null) {
+            $reg = &$this->getRegistry($layer);
+            if (is_object($reg)) {
+                $chan = &$reg->getChannel($channel);
+                if (PEAR::isError($chan)) {
+                    return $channel;
+                }
+
+                if (!$chan->getMirror($ret) && $chan->getName() != $ret) {
+                    return $channel; // mirror does not exist
+                }
+            }
+
+            return $ret;
+        }
+
+        if ($channel != $this->getDefaultChannel($layer)) {
+            return $channel; // we must use the channel name as the preferred mirror
+                             // if the user has not chosen an alternate
+        }
+
+        return $this->getDefaultChannel($layer);
+    }
+
+    /**
+     * Set a config value in a specific layer (defaults to 'user').
+     * Enforces the types defined in the configuration_info array.  An
+     * integer config variable will be cast to int, and a set config
+     * variable will be validated against its legal values.
+     *
+     * @param string config key
+     * @param string config value
+     * @param string (optional) config layer
+     * @param string channel to set this value for, or null for global value
+     * @return bool TRUE on success, FALSE on failure
+     */
+    function set($key, $value, $layer = 'user', $channel = false)
+    {
+        if ($key == '__channels') {
+            return false;
+        }
+
+        if (!isset($this->configuration[$layer])) {
+            return false;
+        }
+
+        if ($key == 'default_channel') {
+            // can only set this value globally
+            $channel = 'pear.php.net';
+            if ($value != 'pear.php.net') {
+                $this->_lazyChannelSetup($layer);
+            }
+        }
+
+        if ($key == 'preferred_mirror') {
+            if ($channel == '__uri') {
+                return false; // can't set the __uri pseudo-channel's mirror
+            }
+
+            $reg = &$this->getRegistry($layer);
+            if (is_object($reg)) {
+                $chan = &$reg->getChannel($channel ? $channel : 'pear.php.net');
+                if (PEAR::isError($chan)) {
+                    return false;
+                }
+
+                if (!$chan->getMirror($value) && $chan->getName() != $value) {
+                    return false; // mirror does not exist
+                }
+            }
+        }
+
+        if (!isset($this->configuration_info[$key])) {
+            return false;
+        }
+
+        extract($this->configuration_info[$key]);
+        switch ($type) {
+            case 'integer':
+                $value = (int)$value;
+                break;
+            case 'set': {
+                // If a valid_set is specified, require the value to
+                // be in the set.  If there is no valid_set, accept
+                // any value.
+                if ($valid_set) {
+                    reset($valid_set);
+                    if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
+                        (key($valid_set) !== 0 && empty($valid_set[$value])))
+                    {
+                        return false;
+                    }
+                }
+                break;
+            }
+        }
+
+        if (!$channel) {
+            $channel = $this->get('default_channel', null, 'pear.php.net');
+        }
+
+        if (!in_array($channel, $this->_channels)) {
+            $this->_lazyChannelSetup($layer);
+            $reg = &$this->getRegistry($layer);
+            if ($reg) {
+                $channel = $reg->channelName($channel);
+            }
+
+            if (!in_array($channel, $this->_channels)) {
+                return false;
+            }
+        }
+
+        if ($channel != 'pear.php.net') {
+            if (in_array($key, $this->_channelConfigInfo)) {
+                $this->configuration[$layer]['__channels'][$channel][$key] = $value;
+                return true;
+            }
+
+            return false;
+        }
+
+        if ($key == 'default_channel') {
+            if (!isset($reg)) {
+                $reg = &$this->getRegistry($layer);
+                if (!$reg) {
+                    $reg = &$this->getRegistry();
+                }
+            }
+
+            if ($reg) {
+                $value = $reg->channelName($value);
+            }
+
+            if (!$value) {
+                return false;
+            }
+        }
+
+        $this->configuration[$layer][$key] = $value;
+        if ($key == 'php_dir' && !$this->_noRegistry) {
+            if (!isset($this->_registry[$layer]) ||
+                  $value != $this->_registry[$layer]->install_dir) {
+                $this->_registry[$layer] = &new PEAR_Registry($value);
+                $this->_regInitialized[$layer] = false;
+                $this->_registry[$layer]->setConfig($this, false);
+            }
+        }
+
+        return true;
+    }
+
+    function _lazyChannelSetup($uselayer = false)
+    {
+        if ($this->_noRegistry) {
+            return;
+        }
+
+        $merge = false;
+        foreach ($this->_registry as $layer => $p) {
+            if ($uselayer && $uselayer != $layer) {
+                continue;
+            }
+
+            if (!$this->_regInitialized[$layer]) {
+                if ($layer == 'default' && isset($this->_registry['user']) ||
+                      isset($this->_registry['system'])) {
+                    // only use the default registry if there are no alternatives
+                    continue;
+                }
+
+                if (!is_object($this->_registry[$layer])) {
+                    if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) {
+                        $this->_registry[$layer] = &new PEAR_Registry($phpdir);
+                        $this->_registry[$layer]->setConfig($this, false);
+                        $this->_regInitialized[$layer] = false;
+                    } else {
+                        unset($this->_registry[$layer]);
+                        return;
+                    }
+                }
+
+                $this->setChannels($this->_registry[$layer]->listChannels(), $merge);
+                $this->_regInitialized[$layer] = true;
+                $merge = true;
+            }
+        }
+    }
+
+    /**
+     * Set the list of channels.
+     *
+     * This should be set via a call to {@link PEAR_Registry::listChannels()}
+     * @param array
+     * @param bool
+     * @return bool success of operation
+     */
+    function setChannels($channels, $merge = false)
+    {
+        if (!is_array($channels)) {
+            return false;
+        }
+
+        if ($merge) {
+            $this->_channels = array_merge($this->_channels, $channels);
+        } else {
+            $this->_channels = $channels;
+        }
+
+        foreach ($channels as $channel) {
+            $channel = strtolower($channel);
+            if ($channel == 'pear.php.net') {
+                continue;
+            }
+
+            foreach ($this->layers as $layer) {
+                if (!isset($this->configuration[$layer]['__channels'])) {
+                    $this->configuration[$layer]['__channels'] = array();
+                }
+                if (!isset($this->configuration[$layer]['__channels'][$channel])
+                      || !is_array($this->configuration[$layer]['__channels'][$channel])) {
+                    $this->configuration[$layer]['__channels'][$channel] = array();
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Get the type of a config value.
+     *
+     * @param string  config key
+     *
+     * @return string type, one of "string", "integer", "file",
+     * "directory", "set" or "password".
+     *
+     * @access public
+     *
+     */
+    function getType($key)
+    {
+        if (isset($this->configuration_info[$key])) {
+            return $this->configuration_info[$key]['type'];
+        }
+        return false;
+    }
+
+    /**
+     * Get the documentation for a config value.
+     *
+     * @param string  config key
+     * @return string documentation string
+     *
+     * @access public
+     *
+     */
+    function getDocs($key)
+    {
+        if (isset($this->configuration_info[$key])) {
+            return $this->configuration_info[$key]['doc'];
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the short documentation for a config value.
+     *
+     * @param string  config key
+     * @return string short documentation string
+     *
+     * @access public
+     *
+     */
+    function getPrompt($key)
+    {
+        if (isset($this->configuration_info[$key])) {
+            return $this->configuration_info[$key]['prompt'];
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the parameter group for a config key.
+     *
+     * @param string  config key
+     * @return string parameter group
+     *
+     * @access public
+     *
+     */
+    function getGroup($key)
+    {
+        if (isset($this->configuration_info[$key])) {
+            return $this->configuration_info[$key]['group'];
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the list of parameter groups.
+     *
+     * @return array list of parameter groups
+     *
+     * @access public
+     *
+     */
+    function getGroups()
+    {
+        $tmp = array();
+        foreach ($this->configuration_info as $key => $info) {
+            $tmp[$info['group']] = 1;
+        }
+
+        return array_keys($tmp);
+    }
+
+    /**
+     * Get the list of the parameters in a group.
+     *
+     * @param string $group parameter group
+     * @return array list of parameters in $group
+     *
+     * @access public
+     *
+     */
+    function getGroupKeys($group)
+    {
+        $keys = array();
+        foreach ($this->configuration_info as $key => $info) {
+            if ($info['group'] == $group) {
+                $keys[] = $key;
+            }
+        }
+
+        return $keys;
+    }
+
+    /**
+     * Get the list of allowed set values for a config value.  Returns
+     * NULL for config values that are not sets.
+     *
+     * @param string  config key
+     * @return array enumerated array of set values, or NULL if the
+     *               config key is unknown or not a set
+     *
+     * @access public
+     *
+     */
+    function getSetValues($key)
+    {
+        if (isset($this->configuration_info[$key]) &&
+            isset($this->configuration_info[$key]['type']) &&
+            $this->configuration_info[$key]['type'] == 'set')
+        {
+            $valid_set = $this->configuration_info[$key]['valid_set'];
+            reset($valid_set);
+            if (key($valid_set) === 0) {
+                return $valid_set;
+            }
+
+            return array_keys($valid_set);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get all the current config keys.
+     *
+     * @return array simple array of config keys
+     *
+     * @access public
+     */
+    function getKeys()
+    {
+        $keys = array();
+        foreach ($this->layers as $layer) {
+            $test = $this->configuration[$layer];
+            if (isset($test['__channels'])) {
+                foreach ($test['__channels'] as $channel => $configs) {
+                    $keys = array_merge($keys, $configs);
+                }
+            }
+
+            unset($test['__channels']);
+            $keys = array_merge($keys, $test);
+
+        }
+        return array_keys($keys);
+    }
+
+    /**
+     * Remove the a config key from a specific config layer.
+     *
+     * @param string config key
+     * @param string (optional) config layer
+     * @param string (optional) channel (defaults to default channel)
+     * @return bool TRUE on success, FALSE on failure
+     *
+     * @access public
+     */
+    function remove($key, $layer = 'user', $channel = null)
+    {
+        if ($channel === null) {
+            $channel = $this->getDefaultChannel();
+        }
+
+        if ($channel !== 'pear.php.net') {
+            if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
+                unset($this->configuration[$layer]['__channels'][$channel][$key]);
+                return true;
+            }
+        }
+
+        if (isset($this->configuration[$layer][$key])) {
+            unset($this->configuration[$layer][$key]);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Temporarily remove an entire config layer.  USE WITH CARE!
+     *
+     * @param string config key
+     * @param string (optional) config layer
+     * @return bool TRUE on success, FALSE on failure
+     *
+     * @access public
+     */
+    function removeLayer($layer)
+    {
+        if (isset($this->configuration[$layer])) {
+            $this->configuration[$layer] = array();
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Stores configuration data in a layer.
+     *
+     * @param string config layer to store
+     * @return bool TRUE on success, or PEAR error on failure
+     *
+     * @access public
+     */
+    function store($layer = 'user', $data = null)
+    {
+        return $this->writeConfigFile(null, $layer, $data);
+    }
+
+    /**
+     * Tells what config layer that gets to define a key.
+     *
+     * @param string config key
+     * @param boolean return the defining channel
+     *
+     * @return string|array the config layer, or an empty string if not found.
+     *
+     *         if $returnchannel, the return is an array array('layer' => layername,
+     *         'channel' => channelname), or an empty string if not found
+     *
+     * @access public
+     */
+    function definedBy($key, $returnchannel = false)
+    {
+        foreach ($this->layers as $layer) {
+            $channel = $this->getDefaultChannel();
+            if ($channel !== 'pear.php.net') {
+                if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
+                    if ($returnchannel) {
+                        return array('layer' => $layer, 'channel' => $channel);
+                    }
+                    return $layer;
+                }
+            }
+
+            if (isset($this->configuration[$layer][$key])) {
+                if ($returnchannel) {
+                    return array('layer' => $layer, 'channel' => 'pear.php.net');
+                }
+                return $layer;
+            }
+        }
+
+        return '';
+    }
+
+    /**
+     * Tells whether a given key exists as a config value.
+     *
+     * @param string config key
+     * @return bool whether <config key> exists in this object
+     *
+     * @access public
+     */
+    function isDefined($key)
+    {
+        foreach ($this->layers as $layer) {
+            if (isset($this->configuration[$layer][$key])) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Tells whether a given config layer exists.
+     *
+     * @param string config layer
+     * @return bool whether <config layer> exists in this object
+     *
+     * @access public
+     */
+    function isDefinedLayer($layer)
+    {
+        return isset($this->configuration[$layer]);
+    }
+
+    /**
+     * Returns the layers defined (except the 'default' one)
+     *
+     * @return array of the defined layers
+     */
+    function getLayers()
+    {
+        $cf = $this->configuration;
+        unset($cf['default']);
+        return array_keys($cf);
+    }
+
+    function apiVersion()
+    {
+        return '1.1';
+    }
+
+    /**
+     * @return PEAR_Registry
+     */
+    function &getRegistry($use = null)
+    {
+        $layer = $use === null ? 'user' : $use;
+        if (isset($this->_registry[$layer])) {
+            return $this->_registry[$layer];
+        } elseif ($use === null && isset($this->_registry['system'])) {
+            return $this->_registry['system'];
+        } elseif ($use === null && isset($this->_registry['default'])) {
+            return $this->_registry['default'];
+        } elseif ($use) {
+            $a = false;
+            return $a;
+        }
+
+        // only go here if null was passed in
+        echo "CRITICAL ERROR: Registry could not be initialized from any value";
+        exit(1);
+    }
+
+    /**
+     * This is to allow customization like the use of installroot
+     * @param PEAR_Registry
+     * @return bool
+     */
+    function setRegistry(&$reg, $layer = 'user')
+    {
+        if ($this->_noRegistry) {
+            return false;
+        }
+
+        if (!in_array($layer, array('user', 'system'))) {
+            return false;
+        }
+
+        $this->_registry[$layer] = &$reg;
+        if (is_object($reg)) {
+            $this->_registry[$layer]->setConfig($this, false);
+        }
+
+        return true;
+    }
+
+    function noRegistry()
+    {
+        $this->_noRegistry = true;
+    }
+
+    /**
+     * @return PEAR_REST
+     */
+    function &getREST($version, $options = array())
+    {
+        $version = str_replace('.', '', $version);
+        if (!class_exists($class = 'PEAR_REST_' . $version)) {
+            require_once 'PEAR/REST/' . $version . '.php';
+        }
+
+        $remote = &new $class($this, $options);
+        return $remote;
+    }
+
+    /**
+     * The ftp server is set in {@link readFTPConfigFile()}.  It exists only if a
+     * remote configuration file has been specified
+     * @return PEAR_FTP|false
+     */
+    function &getFTP()
+    {
+        if (isset($this->_ftp)) {
+            return $this->_ftp;
+        }
+
+        $a = false;
+        return $a;
+    }
+
+    function _prependPath($path, $prepend)
+    {
+        if (strlen($prepend) > 0) {
+            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
+                if (preg_match('/^[a-z]:/i', $prepend)) {
+                    $prepend = substr($prepend, 2);
+                } elseif ($prepend{0} != '\\') {
+                    $prepend = "\\$prepend";
+                }
+                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
+            } else {
+                $path = $prepend . $path;
+            }
+        }
+        return $path;
+    }
+
+    /**
+     * @param string|false installation directory to prepend to all _dir variables, or false to
+     *                     disable
+     */
+    function setInstallRoot($root)
+    {
+        if (substr($root, -1) == DIRECTORY_SEPARATOR) {
+            $root = substr($root, 0, -1);
+        }
+        $old = $this->_installRoot;
+        $this->_installRoot = $root;
+        if (($old != $root) && !$this->_noRegistry) {
+            foreach (array_keys($this->_registry) as $layer) {
+                if ($layer == 'ftp' || !isset($this->_registry[$layer])) {
+                    continue;
+                }
+                $this->_registry[$layer] =
+                    &new PEAR_Registry($this->get('php_dir', $layer, 'pear.php.net'));
+                $this->_registry[$layer]->setConfig($this, false);
+                $this->_regInitialized[$layer] = false;
+            }
+        }
+    }
+}
Index: trunk/library/PEAR/PEAR/Installer/Role/Www.xml
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Www.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Www.xml	(revision 5146)
@@ -0,0 +1,15 @@
+<role version="1.0">
+ <releasetypes>php</releasetypes>
+ <releasetypes>extsrc</releasetypes>
+ <releasetypes>extbin</releasetypes>
+ <releasetypes>zendextsrc</releasetypes>
+ <releasetypes>zendextbin</releasetypes>
+ <installable>1</installable>
+ <locationconfig>www_dir</locationconfig>
+ <honorsbaseinstall>1</honorsbaseinstall>
+ <unusualbaseinstall />
+ <phpfile />
+ <executable />
+ <phpextension />
+ <config_vars />
+</role>
Index: trunk/library/PEAR/PEAR/Installer/Role/Cfg.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Cfg.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Cfg.php	(revision 5146)
@@ -0,0 +1,106 @@
+<?php
+/**
+ * PEAR_Installer_Role_Cfg
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  2007-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Cfg.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.7.0
+ */
+
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  2007-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.7.0
+ */
+class PEAR_Installer_Role_Cfg extends PEAR_Installer_Role_Common
+{
+    /**
+     * @var PEAR_Installer
+     */
+    var $installer;
+
+    /**
+     * the md5 of the original file
+     *
+     * @var unknown_type
+     */
+    var $md5 = null;
+
+    /**
+     * Do any unusual setup here
+     * @param PEAR_Installer
+     * @param PEAR_PackageFile_v2
+     * @param array file attributes
+     * @param string file name
+     */
+    function setup(&$installer, $pkg, $atts, $file)
+    {
+        $this->installer = &$installer;
+        $reg = &$this->installer->config->getRegistry();
+        $package = $reg->getPackage($pkg->getPackage(), $pkg->getChannel());
+        if ($package) {
+            $filelist = $package->getFilelist();
+            if (isset($filelist[$file]) && isset($filelist[$file]['md5sum'])) {
+                $this->md5 = $filelist[$file]['md5sum'];
+            }
+        }
+    }
+
+    function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
+    {
+        $test = parent::processInstallation($pkg, $atts, $file, $tmp_path, $layer);
+        if (@file_exists($test[2]) && @file_exists($test[3])) {
+            $md5 = md5_file($test[2]);
+            // configuration has already been installed, check for mods
+            if ($md5 !== $this->md5 && $md5 !== md5_file($test[3])) {
+                // configuration has been modified, so save our version as
+                // configfile-version
+                $old = $test[2];
+                $test[2] .= '.new-' . $pkg->getVersion();
+                // backup original and re-install it
+                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                $tmpcfg = $this->config->get('temp_dir');
+                $newloc = System::mkdir(array('-p', $tmpcfg));
+                if (!$newloc) {
+                    // try temp_dir
+                    $newloc = System::mktemp(array('-d'));
+                    if (!$newloc || PEAR::isError($newloc)) {
+                        PEAR::popErrorHandling();
+                        return PEAR::raiseError('Could not save existing configuration file '.
+                            $old . ', unable to install.  Please set temp_dir ' .
+                            'configuration variable to a writeable location and try again');
+                    }
+                } else {
+                    $newloc = $tmpcfg;
+                }
+
+                $temp_file = $newloc . DIRECTORY_SEPARATOR . uniqid('savefile');
+                if (!@copy($old, $temp_file)) {
+                    PEAR::popErrorHandling();
+                    return PEAR::raiseError('Could not save existing configuration file '.
+                        $old . ', unable to install.  Please set temp_dir ' .
+                        'configuration variable to a writeable location and try again');
+                }
+
+                PEAR::popErrorHandling();
+                $this->installer->log(0, "WARNING: configuration file $old is being installed as $test[2], you should manually merge in changes to the existing configuration file");
+                $this->installer->addFileOperation('rename', array($temp_file, $old, false));
+                $this->installer->addFileOperation('delete', array($temp_file));
+            }
+        }
+
+        return $test;
+    }
+}
Index: trunk/library/PEAR/PEAR/Installer/Role/Ext.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Ext.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Ext.php	(revision 5146)
@@ -0,0 +1,28 @@
+<?php
+/**
+ * PEAR_Installer_Role_Ext
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Ext.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Installer_Role_Ext extends PEAR_Installer_Role_Common {}
+?>
Index: trunk/library/PEAR/PEAR/Installer/Role/Php.xml
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Php.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Php.xml	(revision 5146)
@@ -0,0 +1,15 @@
+<role version="1.0">
+ <releasetypes>php</releasetypes>
+ <releasetypes>extsrc</releasetypes>
+ <releasetypes>extbin</releasetypes>
+ <releasetypes>zendextsrc</releasetypes>
+ <releasetypes>zendextbin</releasetypes>
+ <installable>1</installable>
+ <locationconfig>php_dir</locationconfig>
+ <honorsbaseinstall>1</honorsbaseinstall>
+ <unusualbaseinstall />
+ <phpfile>1</phpfile>
+ <executable />
+ <phpextension />
+ <config_vars />
+</role>
Index: trunk/library/PEAR/PEAR/Installer/Role/Src.xml
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Src.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Src.xml	(revision 5146)
@@ -0,0 +1,12 @@
+<role version="1.0">
+ <releasetypes>extsrc</releasetypes>
+ <releasetypes>zendextsrc</releasetypes>
+ <installable>1</installable>
+ <locationconfig>temp_dir</locationconfig>
+ <honorsbaseinstall />
+ <unusualbaseinstall />
+ <phpfile />
+ <executable />
+ <phpextension />
+ <config_vars />
+</role>
Index: trunk/library/PEAR/PEAR/Installer/Role/Script.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Script.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Script.php	(revision 5146)
@@ -0,0 +1,28 @@
+<?php
+/**
+ * PEAR_Installer_Role_Script
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Script.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Installer_Role_Script extends PEAR_Installer_Role_Common {}
+?>
Index: trunk/library/PEAR/PEAR/Installer/Role/Doc.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Doc.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Doc.php	(revision 5146)
@@ -0,0 +1,28 @@
+<?php
+/**
+ * PEAR_Installer_Role_Doc
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Doc.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Installer_Role_Doc extends PEAR_Installer_Role_Common {}
+?>
Index: trunk/library/PEAR/PEAR/Installer/Role/Cfg.xml
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Cfg.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Cfg.xml	(revision 5146)
@@ -0,0 +1,15 @@
+<role version="1.0">
+ <releasetypes>php</releasetypes>
+ <releasetypes>extsrc</releasetypes>
+ <releasetypes>extbin</releasetypes>
+ <releasetypes>zendextsrc</releasetypes>
+ <releasetypes>zendextbin</releasetypes>
+ <installable>1</installable>
+ <locationconfig>cfg_dir</locationconfig>
+ <honorsbaseinstall />
+ <unusualbaseinstall>1</unusualbaseinstall>
+ <phpfile />
+ <executable />
+ <phpextension />
+ <config_vars />
+</role>
Index: trunk/library/PEAR/PEAR/Installer/Role/Data.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Data.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Data.php	(revision 5146)
@@ -0,0 +1,28 @@
+<?php
+/**
+ * PEAR_Installer_Role_Data
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Data.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Installer_Role_Data extends PEAR_Installer_Role_Common {}
+?>
Index: trunk/library/PEAR/PEAR/Installer/Role/Ext.xml
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Ext.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Ext.xml	(revision 5146)
@@ -0,0 +1,12 @@
+<role version="1.0">
+ <releasetypes>extbin</releasetypes>
+ <releasetypes>zendextbin</releasetypes>
+ <installable>1</installable>
+ <locationconfig>ext_dir</locationconfig>
+ <honorsbaseinstall>1</honorsbaseinstall>
+ <unusualbaseinstall />
+ <phpfile />
+ <executable />
+ <phpextension>1</phpextension>
+ <config_vars />
+</role>
Index: trunk/library/PEAR/PEAR/Installer/Role/Script.xml
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Script.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Script.xml	(revision 5146)
@@ -0,0 +1,15 @@
+<role version="1.0">
+ <releasetypes>php</releasetypes>
+ <releasetypes>extsrc</releasetypes>
+ <releasetypes>extbin</releasetypes>
+ <releasetypes>zendextsrc</releasetypes>
+ <releasetypes>zendextbin</releasetypes>
+ <installable>1</installable>
+ <locationconfig>bin_dir</locationconfig>
+ <honorsbaseinstall>1</honorsbaseinstall>
+ <unusualbaseinstall />
+ <phpfile />
+ <executable>1</executable>
+ <phpextension />
+ <config_vars />
+</role>
Index: trunk/library/PEAR/PEAR/Installer/Role/Doc.xml
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Doc.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Doc.xml	(revision 5146)
@@ -0,0 +1,15 @@
+<role version="1.0">
+ <releasetypes>php</releasetypes>
+ <releasetypes>extsrc</releasetypes>
+ <releasetypes>extbin</releasetypes>
+ <releasetypes>zendextsrc</releasetypes>
+ <releasetypes>zendextbin</releasetypes>
+ <installable>1</installable>
+ <locationconfig>doc_dir</locationconfig>
+ <honorsbaseinstall />
+ <unusualbaseinstall />
+ <phpfile />
+ <executable />
+ <phpextension />
+ <config_vars />
+</role>
Index: trunk/library/PEAR/PEAR/Installer/Role/Test.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Test.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Test.php	(revision 5146)
@@ -0,0 +1,28 @@
+<?php
+/**
+ * PEAR_Installer_Role_Test
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Test.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Installer_Role_Test extends PEAR_Installer_Role_Common {}
+?>
Index: trunk/library/PEAR/PEAR/Installer/Role/Data.xml
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Data.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Data.xml	(revision 5146)
@@ -0,0 +1,15 @@
+<role version="1.0">
+ <releasetypes>php</releasetypes>
+ <releasetypes>extsrc</releasetypes>
+ <releasetypes>extbin</releasetypes>
+ <releasetypes>zendextsrc</releasetypes>
+ <releasetypes>zendextbin</releasetypes>
+ <installable>1</installable>
+ <locationconfig>data_dir</locationconfig>
+ <honorsbaseinstall />
+ <unusualbaseinstall />
+ <phpfile />
+ <executable />
+ <phpextension />
+ <config_vars />
+</role>
Index: trunk/library/PEAR/PEAR/Installer/Role/Www.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Www.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Www.php	(revision 5146)
@@ -0,0 +1,28 @@
+<?php
+/**
+ * PEAR_Installer_Role_Www
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  2007-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Www.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.7.0
+ */
+
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  2007-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.7.0
+ */
+class PEAR_Installer_Role_Www extends PEAR_Installer_Role_Common {}
+?>
Index: trunk/library/PEAR/PEAR/Installer/Role/Src.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Src.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Src.php	(revision 5146)
@@ -0,0 +1,34 @@
+<?php
+/**
+ * PEAR_Installer_Role_Src
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Src.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Installer_Role_Src extends PEAR_Installer_Role_Common
+{
+    function setup(&$installer, $pkg, $atts, $file)
+    {
+        $installer->source_files++;
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Installer/Role/Php.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Php.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Php.php	(revision 5146)
@@ -0,0 +1,28 @@
+<?php
+/**
+ * PEAR_Installer_Role_Php
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Php.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Installer_Role_Php extends PEAR_Installer_Role_Common {}
+?>
Index: trunk/library/PEAR/PEAR/Installer/Role/Common.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Common.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Common.php	(revision 5146)
@@ -0,0 +1,174 @@
+<?php
+/**
+ * Base class for all installation roles.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2006 The PHP Group
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * Base class for all installation roles.
+ *
+ * This class allows extensibility of file roles.  Packages with complex
+ * customization can now provide custom file roles along with the possibility of
+ * adding configuration values to match.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2006 The PHP Group
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Installer_Role_Common
+{
+    /**
+     * @var PEAR_Config
+     * @access protected
+     */
+    var $config;
+
+    /**
+     * @param PEAR_Config
+     */
+    function PEAR_Installer_Role_Common(&$config)
+    {
+        $this->config = $config;
+    }
+
+    /**
+     * Retrieve configuration information about a file role from its XML info
+     *
+     * @param string $role Role Classname, as in "PEAR_Installer_Role_Data"
+     * @return array
+     */
+    function getInfo($role)
+    {
+        if (empty($GLOBALS['_PEAR_INSTALLER_ROLES'][$role])) {
+            return PEAR::raiseError('Unknown Role class: "' . $role . '"');
+        }
+        return $GLOBALS['_PEAR_INSTALLER_ROLES'][$role];
+    }
+
+    /**
+     * This is called for each file to set up the directories and files
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @param array attributes from the <file> tag
+     * @param string file name
+     * @return array an array consisting of:
+     *
+     *    1 the original, pre-baseinstalldir installation directory
+     *    2 the final installation directory
+     *    3 the full path to the final location of the file
+     *    4 the location of the pre-installation file
+     */
+    function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
+    {
+        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
+            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
+        if (PEAR::isError($roleInfo)) {
+            return $roleInfo;
+        }
+        if (!$roleInfo['locationconfig']) {
+            return false;
+        }
+        if ($roleInfo['honorsbaseinstall']) {
+            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'], $layer,
+                $pkg->getChannel());
+            if (!empty($atts['baseinstalldir'])) {
+                $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
+            }
+        } elseif ($roleInfo['unusualbaseinstall']) {
+            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
+                    $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
+            if (!empty($atts['baseinstalldir'])) {
+                $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
+            }
+        } else {
+            $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
+                    $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
+        }
+        if (dirname($file) != '.' && empty($atts['install-as'])) {
+            $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
+        }
+        if (empty($atts['install-as'])) {
+            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
+        } else {
+            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
+        }
+        $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
+
+        // Clean up the DIRECTORY_SEPARATOR mess
+        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
+        
+        list($dest_dir, $dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
+                                                    array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR,
+                                                          DIRECTORY_SEPARATOR),
+                                                    array($dest_dir, $dest_file, $orig_file));
+        return array($save_destdir, $dest_dir, $dest_file, $orig_file);
+    }
+
+    /**
+     * Get the name of the configuration variable that specifies the location of this file
+     * @return string|false
+     */
+    function getLocationConfig()
+    {
+        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
+            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
+        if (PEAR::isError($roleInfo)) {
+            return $roleInfo;
+        }
+        return $roleInfo['locationconfig'];
+    }
+
+    /**
+     * Do any unusual setup here
+     * @param PEAR_Installer
+     * @param PEAR_PackageFile_v2
+     * @param array file attributes
+     * @param string file name
+     */
+    function setup(&$installer, $pkg, $atts, $file)
+    {
+    }
+
+    function isExecutable()
+    {
+        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
+            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
+        if (PEAR::isError($roleInfo)) {
+            return $roleInfo;
+        }
+        return $roleInfo['executable'];
+    }
+
+    function isInstallable()
+    {
+        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
+            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
+        if (PEAR::isError($roleInfo)) {
+            return $roleInfo;
+        }
+        return $roleInfo['installable'];
+    }
+
+    function isExtension()
+    {
+        $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . 
+            ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
+        if (PEAR::isError($roleInfo)) {
+            return $roleInfo;
+        }
+        return $roleInfo['phpextension'];
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Installer/Role/Test.xml
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role/Test.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role/Test.xml	(revision 5146)
@@ -0,0 +1,15 @@
+<role version="1.0">
+ <releasetypes>php</releasetypes>
+ <releasetypes>extsrc</releasetypes>
+ <releasetypes>extbin</releasetypes>
+ <releasetypes>zendextsrc</releasetypes>
+ <releasetypes>zendextbin</releasetypes>
+ <installable>1</installable>
+ <locationconfig>test_dir</locationconfig>
+ <honorsbaseinstall />
+ <unusualbaseinstall />
+ <phpfile />
+ <executable />
+ <phpextension />
+ <config_vars />
+</role>
Index: trunk/library/PEAR/PEAR/Installer/Role.php
===================================================================
--- trunk/library/PEAR/PEAR/Installer/Role.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Installer/Role.php	(revision 5146)
@@ -0,0 +1,276 @@
+<?php
+/**
+ * PEAR_Installer_Role
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Role.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * base class for installer roles
+ */
+require_once 'PEAR/Installer/Role/Common.php';
+require_once 'PEAR/XMLParser.php';
+/**
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Installer_Role
+{
+    /**
+     * Set up any additional configuration variables that file roles require
+     *
+     * Never call this directly, it is called by the PEAR_Config constructor
+     * @param PEAR_Config
+     * @access private
+     * @static
+     */
+    function initializeConfig(&$config)
+    {
+        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
+            PEAR_Installer_Role::registerRoles();
+        }
+
+        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) {
+            if (!$info['config_vars']) {
+                continue;
+            }
+
+            $config->_addConfigVars($class, $info['config_vars']);
+        }
+    }
+
+    /**
+     * @param PEAR_PackageFile_v2
+     * @param string role name
+     * @param PEAR_Config
+     * @return PEAR_Installer_Role_Common
+     * @static
+     */
+    function &factory($pkg, $role, &$config)
+    {
+        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
+            PEAR_Installer_Role::registerRoles();
+        }
+
+        if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
+            $a = false;
+            return $a;
+        }
+
+        $a = 'PEAR_Installer_Role_' . ucfirst($role);
+        if (!class_exists($a)) {
+            require_once str_replace('_', '/', $a) . '.php';
+        }
+
+        $b = new $a($config);
+        return $b;
+    }
+
+    /**
+     * Get a list of file roles that are valid for the particular release type.
+     *
+     * For instance, src files serve no purpose in regular php releases.
+     * @param string
+     * @param bool clear cache
+     * @return array
+     * @static
+     */
+    function getValidRoles($release, $clear = false)
+    {
+        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
+            PEAR_Installer_Role::registerRoles();
+        }
+
+        static $ret = array();
+        if ($clear) {
+            $ret = array();
+        }
+
+        if (isset($ret[$release])) {
+            return $ret[$release];
+        }
+
+        $ret[$release] = array();
+        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
+            if (in_array($release, $okreleases['releasetypes'])) {
+                $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
+            }
+        }
+
+        return $ret[$release];
+    }
+
+    /**
+     * Get a list of roles that require their files to be installed
+     *
+     * Most roles must be installed, but src and package roles, for instance
+     * are pseudo-roles.  src files are compiled into a new extension.  Package
+     * roles are actually fully bundled releases of a package
+     * @param bool clear cache
+     * @return array
+     * @static
+     */
+    function getInstallableRoles($clear = false)
+    {
+        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
+            PEAR_Installer_Role::registerRoles();
+        }
+
+        static $ret;
+        if ($clear) {
+            unset($ret);
+        }
+
+        if (isset($ret)) {
+            return $ret;
+        }
+
+        $ret = array();
+        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
+            if ($okreleases['installable']) {
+                $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
+            }
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Return an array of roles that are affected by the baseinstalldir attribute
+     *
+     * Most roles ignore this attribute, and instead install directly into:
+     * PackageName/filepath
+     * so a tests file tests/file.phpt is installed into PackageName/tests/filepath.php
+     * @param bool clear cache
+     * @return array
+     * @static
+     */
+    function getBaseinstallRoles($clear = false)
+    {
+        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
+            PEAR_Installer_Role::registerRoles();
+        }
+
+        static $ret;
+        if ($clear) {
+            unset($ret);
+        }
+
+        if (isset($ret)) {
+            return $ret;
+        }
+
+        $ret = array();
+        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
+            if ($okreleases['honorsbaseinstall']) {
+                $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
+            }
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Return an array of file roles that should be analyzed for PHP content at package time,
+     * like the "php" role.
+     * @param bool clear cache
+     * @return array
+     * @static
+     */
+    function getPhpRoles($clear = false)
+    {
+        if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
+            PEAR_Installer_Role::registerRoles();
+        }
+
+        static $ret;
+        if ($clear) {
+            unset($ret);
+        }
+
+        if (isset($ret)) {
+            return $ret;
+        }
+
+        $ret = array();
+        foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
+            if ($okreleases['phpfile']) {
+                $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
+            }
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Scan through the Command directory looking for classes
+     * and see what commands they implement.
+     * @param string which directory to look for classes, defaults to
+     *               the Installer/Roles subdirectory of
+     *               the directory from where this file (__FILE__) is
+     *               included.
+     *
+     * @return bool TRUE on success, a PEAR error on failure
+     * @access public
+     * @static
+     */
+    function registerRoles($dir = null)
+    {
+        $GLOBALS['_PEAR_INSTALLER_ROLES'] = array();
+        $parser = new PEAR_XMLParser;
+        if ($dir === null) {
+            $dir = dirname(__FILE__) . '/Role';
+        }
+
+        if (!file_exists($dir) || !is_dir($dir)) {
+            return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory");
+        }
+
+        $dp = @opendir($dir);
+        if (empty($dp)) {
+            return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg");
+        }
+
+        while ($entry = readdir($dp)) {
+            if ($entry{0} == '.' || substr($entry, -4) != '.xml') {
+                continue;
+            }
+
+            $class = "PEAR_Installer_Role_".substr($entry, 0, -4);
+            // List of roles
+            if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) {
+                $file = "$dir/$entry";
+                $parser->parse(file_get_contents($file));
+                $data = $parser->getData();
+                if (!is_array($data['releasetypes'])) {
+                    $data['releasetypes'] = array($data['releasetypes']);
+                }
+
+                $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data;
+            }
+        }
+
+        closedir($dp);
+        ksort($GLOBALS['_PEAR_INSTALLER_ROLES']);
+        PEAR_Installer_Role::getBaseinstallRoles(true);
+        PEAR_Installer_Role::getInstallableRoles(true);
+        PEAR_Installer_Role::getPhpRoles(true);
+        PEAR_Installer_Role::getValidRoles('****', true);
+        return true;
+    }
+}
Index: trunk/library/PEAR/PEAR/ChannelFile.php
===================================================================
--- trunk/library/PEAR/PEAR/ChannelFile.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/ChannelFile.php	(revision 5146)
@@ -0,0 +1,1559 @@
+<?php
+/**
+ * PEAR_ChannelFile, the channel handling class
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: ChannelFile.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * Needed for error handling
+ */
+require_once 'PEAR/ErrorStack.php';
+require_once 'PEAR/XMLParser.php';
+require_once 'PEAR/Common.php';
+
+/**
+ * Error code if the channel.xml <channel> tag does not contain a valid version
+ */
+define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1);
+/**
+ * Error code if the channel.xml <channel> tag version is not supported (version 1.0 is the only supported version,
+ * currently
+ */
+define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2);
+
+/**
+ * Error code if parsing is attempted with no xml extension
+ */
+define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3);
+
+/**
+ * Error code if creating the xml parser resource fails
+ */
+define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4);
+
+/**
+ * Error code used for all sax xml parsing errors
+ */
+define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5);
+
+/**#@+
+ * Validation errors
+ */
+/**
+ * Error code when channel name is missing
+ */
+define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6);
+/**
+ * Error code when channel name is invalid
+ */
+define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7);
+/**
+ * Error code when channel summary is missing
+ */
+define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8);
+/**
+ * Error code when channel summary is multi-line
+ */
+define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9);
+/**
+ * Error code when channel server is missing for protocol
+ */
+define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10);
+/**
+ * Error code when channel server is invalid for protocol
+ */
+define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11);
+/**
+ * Error code when a mirror name is invalid
+ */
+define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21);
+/**
+ * Error code when a mirror type is invalid
+ */
+define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22);
+/**
+ * Error code when an attempt is made to generate xml, but the parsed content is invalid
+ */
+define('PEAR_CHANNELFILE_ERROR_INVALID', 23);
+/**
+ * Error code when an empty package name validate regex is passed in
+ */
+define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24);
+/**
+ * Error code when a <function> tag has no version
+ */
+define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25);
+/**
+ * Error code when a <function> tag has no name
+ */
+define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26);
+/**
+ * Error code when a <validatepackage> tag has no name
+ */
+define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27);
+/**
+ * Error code when a <validatepackage> tag has no version attribute
+ */
+define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28);
+/**
+ * Error code when a mirror does not exist but is called for in one of the set*
+ * methods.
+ */
+define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32);
+/**
+ * Error code when a server port is not numeric
+ */
+define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33);
+/**
+ * Error code when <static> contains no version attribute
+ */
+define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34);
+/**
+ * Error code when <baseurl> contains no type attribute in a <rest> protocol definition
+ */
+define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35);
+/**
+ * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel
+ */
+define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36);
+/**
+ * Error code when ssl attribute is present and is not "yes"
+ */
+define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37);
+/**#@-*/
+
+/**
+ * Mirror types allowed.  Currently only internet servers are recognized.
+ */
+$GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] =  array('server');
+
+
+/**
+ * The Channel handling class
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_ChannelFile
+{
+    /**
+     * @access private
+     * @var PEAR_ErrorStack
+     * @access private
+     */
+    var $_stack;
+
+    /**
+     * Supported channel.xml versions, for parsing
+     * @var array
+     * @access private
+     */
+    var $_supportedVersions = array('1.0');
+
+    /**
+     * Parsed channel information
+     * @var array
+     * @access private
+     */
+    var $_channelInfo;
+
+    /**
+     * index into the subchannels array, used for parsing xml
+     * @var int
+     * @access private
+     */
+    var $_subchannelIndex;
+
+    /**
+     * index into the mirrors array, used for parsing xml
+     * @var int
+     * @access private
+     */
+    var $_mirrorIndex;
+
+    /**
+     * Flag used to determine the validity of parsed content
+     * @var boolean
+     * @access private
+     */
+    var $_isValid = false;
+
+    function PEAR_ChannelFile()
+    {
+        $this->_stack = &new PEAR_ErrorStack('PEAR_ChannelFile');
+        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
+        $this->_isValid = false;
+    }
+
+    /**
+     * @return array
+     * @access protected
+     */
+    function _getErrorMessage()
+    {
+        return
+            array(
+                PEAR_CHANNELFILE_ERROR_INVALID_VERSION =>
+                    'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%',
+                PEAR_CHANNELFILE_ERROR_NO_VERSION =>
+                    'No version number found in <channel> tag',
+                PEAR_CHANNELFILE_ERROR_NO_XML_EXT =>
+                    '%error%',
+                PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER =>
+                    'Unable to create XML parser',
+                PEAR_CHANNELFILE_ERROR_PARSER_ERROR =>
+                    '%error%',
+                PEAR_CHANNELFILE_ERROR_NO_NAME =>
+                    'Missing channel name',
+                PEAR_CHANNELFILE_ERROR_INVALID_NAME =>
+                    'Invalid channel %tag% "%name%"',
+                PEAR_CHANNELFILE_ERROR_NO_SUMMARY =>
+                    'Missing channel summary',
+                PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY =>
+                    'Channel summary should be on one line, but is multi-line',
+                PEAR_CHANNELFILE_ERROR_NO_HOST =>
+                    'Missing channel server for %type% server',
+                PEAR_CHANNELFILE_ERROR_INVALID_HOST =>
+                    'Server name "%server%" is invalid for %type% server',
+                PEAR_CHANNELFILE_ERROR_INVALID_MIRROR =>
+                    'Invalid mirror name "%name%", mirror type %type%',
+                PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE =>
+                    'Invalid mirror type "%type%"',
+                PEAR_CHANNELFILE_ERROR_INVALID =>
+                    'Cannot generate xml, contents are invalid',
+                PEAR_CHANNELFILE_ERROR_EMPTY_REGEX =>
+                    'packagenameregex cannot be empty',
+                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION =>
+                    '%parent% %protocol% function has no version',
+                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME =>
+                    '%parent% %protocol% function has no name',
+                PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE =>
+                    '%parent% rest baseurl has no type',
+                PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME =>
+                    'Validation package has no name in <validatepackage> tag',
+                PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION =>
+                    'Validation package "%package%" has no version',
+                PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND =>
+                    'Mirror "%mirror%" does not exist',
+                PEAR_CHANNELFILE_ERROR_INVALID_PORT =>
+                    'Port "%port%" must be numeric',
+                PEAR_CHANNELFILE_ERROR_NO_STATICVERSION =>
+                    '<static> tag must contain version attribute',
+                PEAR_CHANNELFILE_URI_CANT_MIRROR =>
+                    'The __uri pseudo-channel cannot have mirrors',
+                PEAR_CHANNELFILE_ERROR_INVALID_SSL =>
+                    '%server% has invalid ssl attribute "%ssl%" can only be yes or not present',
+            );
+    }
+
+    /**
+     * @param string contents of package.xml file
+     * @return bool success of parsing
+     */
+    function fromXmlString($data)
+    {
+        if (preg_match('/<channel\s+version="([0-9]+\.[0-9]+)"/', $data, $channelversion)) {
+            if (!in_array($channelversion[1], $this->_supportedVersions)) {
+                $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error',
+                    array('version' => $channelversion[1]));
+                return false;
+            }
+            $parser = new PEAR_XMLParser;
+            $result = $parser->parse($data);
+            if ($result !== true) {
+                if ($result->getCode() == 1) {
+                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error',
+                        array('error' => $result->getMessage()));
+                } else {
+                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error');
+                }
+                return false;
+            }
+            $this->_channelInfo = $parser->getData();
+            return true;
+        } else {
+            $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data));
+            return false;
+        }
+    }
+
+    /**
+     * @return array
+     */
+    function toArray()
+    {
+        if (!$this->_isValid && !$this->validate()) {
+            return false;
+        }
+        return $this->_channelInfo;
+    }
+
+    /**
+     * @param array
+     * @static
+     * @return PEAR_ChannelFile|false false if invalid
+     */
+    function &fromArray($data, $compatibility = false, $stackClass = 'PEAR_ErrorStack')
+    {
+        $a = new PEAR_ChannelFile($compatibility, $stackClass);
+        $a->_fromArray($data);
+        if (!$a->validate()) {
+            $a = false;
+            return $a;
+        }
+        return $a;
+    }
+
+    /**
+     * Unlike {@link fromArray()} this does not do any validation
+     * @param array
+     * @static
+     * @return PEAR_ChannelFile
+     */
+    function &fromArrayWithErrors($data, $compatibility = false,
+                                  $stackClass = 'PEAR_ErrorStack')
+    {
+        $a = new PEAR_ChannelFile($compatibility, $stackClass);
+        $a->_fromArray($data);
+        return $a;
+    }
+
+    /**
+     * @param array
+     * @access private
+     */
+    function _fromArray($data)
+    {
+        $this->_channelInfo = $data;
+    }
+
+    /**
+     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
+     * @param boolean determines whether to purge the error stack after retrieving
+     * @return array
+     */
+    function getErrors($purge = false)
+    {
+        return $this->_stack->getErrors($purge);
+    }
+
+    /**
+     * Unindent given string (?)
+     *
+     * @param string $str The string that has to be unindented.
+     * @return string
+     * @access private
+     */
+    function _unIndent($str)
+    {
+        // remove leading newlines
+        $str = preg_replace('/^[\r\n]+/', '', $str);
+        // find whitespace at the beginning of the first line
+        $indent_len = strspn($str, " \t");
+        $indent = substr($str, 0, $indent_len);
+        $data = '';
+        // remove the same amount of whitespace from following lines
+        foreach (explode("\n", $str) as $line) {
+            if (substr($line, 0, $indent_len) == $indent) {
+                $data .= substr($line, $indent_len) . "\n";
+            }
+        }
+        return $data;
+    }
+
+    /**
+     * Parse a channel.xml file.  Expects the name of
+     * a channel xml file as input.
+     *
+     * @param string  $descfile  name of channel xml file
+     * @return bool success of parsing
+     */
+    function fromXmlFile($descfile)
+    {
+        if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
+             (!$fp = fopen($descfile, 'r'))) {
+            require_once 'PEAR.php';
+            return PEAR::raiseError("Unable to open $descfile");
+        }
+
+        // read the whole thing so we only get one cdata callback
+        // for each block of cdata
+        fclose($fp);
+        $data = file_get_contents($descfile);
+        return $this->fromXmlString($data);
+    }
+
+    /**
+     * Parse channel information from different sources
+     *
+     * This method is able to extract information about a channel
+     * from an .xml file or a string
+     *
+     * @access public
+     * @param  string Filename of the source or the source itself
+     * @return bool
+     */
+    function fromAny($info)
+    {
+        if (is_string($info) && file_exists($info) && strlen($info) < 255) {
+            $tmp = substr($info, -4);
+            if ($tmp == '.xml') {
+                $info = $this->fromXmlFile($info);
+            } else {
+                $fp = fopen($info, "r");
+                $test = fread($fp, 5);
+                fclose($fp);
+                if ($test == "<?xml") {
+                    $info = $this->fromXmlFile($info);
+                }
+            }
+            if (PEAR::isError($info)) {
+                require_once 'PEAR.php';
+                return PEAR::raiseError($info);
+            }
+        }
+        if (is_string($info)) {
+            $info = $this->fromXmlString($info);
+        }
+        return $info;
+    }
+
+    /**
+     * Return an XML document based on previous parsing and modifications
+     *
+     * @return string XML data
+     *
+     * @access public
+     */
+    function toXml()
+    {
+        if (!$this->_isValid && !$this->validate()) {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID);
+            return false;
+        }
+        if (!isset($this->_channelInfo['attribs']['version'])) {
+            $this->_channelInfo['attribs']['version'] = '1.0';
+        }
+        $channelInfo = $this->_channelInfo;
+        $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
+        $ret .= "<channel version=\"" .
+            $channelInfo['attribs']['version'] . "\" xmlns=\"http://pear.php.net/channel-1.0\"
+  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
+  xsi:schemaLocation=\"http://pear.php.net/dtd/channel-"
+            . $channelInfo['attribs']['version'] . " http://pear.php.net/dtd/channel-" .
+            $channelInfo['attribs']['version'] . ".xsd\">
+ <name>$channelInfo[name]</name>
+ <summary>" . htmlspecialchars($channelInfo['summary'])."</summary>
+";
+        if (isset($channelInfo['suggestedalias'])) {
+            $ret .= ' <suggestedalias>' . $channelInfo['suggestedalias'] . "</suggestedalias>\n";
+        }
+        if (isset($channelInfo['validatepackage'])) {
+            $ret .= ' <validatepackage version="' .
+                $channelInfo['validatepackage']['attribs']['version']. '">' .
+                htmlspecialchars($channelInfo['validatepackage']['_content']) .
+                "</validatepackage>\n";
+        }
+        $ret .= " <servers>\n";
+        $ret .= '  <primary';
+        if (isset($channelInfo['servers']['primary']['attribs']['ssl'])) {
+            $ret .= ' ssl="' . $channelInfo['servers']['primary']['attribs']['ssl'] . '"';
+        }
+        if (isset($channelInfo['servers']['primary']['attribs']['port'])) {
+            $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"';
+        }
+        $ret .= ">\n";
+        if (isset($channelInfo['servers']['primary']['rest'])) {
+            $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], '   ');
+        }
+        $ret .= "  </primary>\n";
+        if (isset($channelInfo['servers']['mirror'])) {
+            $ret .= $this->_makeMirrorsXml($channelInfo);
+        }
+        $ret .= " </servers>\n";
+        $ret .= "</channel>";
+        return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret));
+    }
+
+    /**
+     * Generate the <rest> tag
+     * @access private
+     */
+    function _makeRestXml($info, $indent)
+    {
+        $ret = $indent . "<rest>\n";
+        if (isset($info['baseurl']) && !isset($info['baseurl'][0])) {
+            $info['baseurl'] = array($info['baseurl']);
+        }
+
+        if (isset($info['baseurl'])) {
+            foreach ($info['baseurl'] as $url) {
+                $ret .= "$indent <baseurl type=\"" . $url['attribs']['type'] . "\"";
+                $ret .= ">" . $url['_content'] . "</baseurl>\n";
+            }
+        }
+        $ret .= $indent . "</rest>\n";
+        return $ret;
+    }
+
+    /**
+     * Generate the <mirrors> tag
+     * @access private
+     */
+    function _makeMirrorsXml($channelInfo)
+    {
+        $ret = "";
+        if (!isset($channelInfo['servers']['mirror'][0])) {
+            $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']);
+        }
+        foreach ($channelInfo['servers']['mirror'] as $mirror) {
+            $ret .= '  <mirror host="' . $mirror['attribs']['host'] . '"';
+            if (isset($mirror['attribs']['port'])) {
+                $ret .= ' port="' . $mirror['attribs']['port'] . '"';
+            }
+            if (isset($mirror['attribs']['ssl'])) {
+                $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"';
+            }
+            $ret .= ">\n";
+            if (isset($mirror['rest'])) {
+                if (isset($mirror['rest'])) {
+                    $ret .= $this->_makeRestXml($mirror['rest'], '   ');
+                }
+                $ret .= "  </mirror>\n";
+            } else {
+                $ret .= "/>\n";
+            }
+        }
+        return $ret;
+    }
+
+    /**
+     * Generate the <functions> tag
+     * @access private
+     */
+    function _makeFunctionsXml($functions, $indent, $rest = false)
+    {
+        $ret = '';
+        if (!isset($functions[0])) {
+            $functions = array($functions);
+        }
+        foreach ($functions as $function) {
+            $ret .= "$indent<function version=\"" . $function['attribs']['version'] . "\"";
+            if ($rest) {
+                $ret .= ' uri="' . $function['attribs']['uri'] . '"';
+            }
+            $ret .= ">" . $function['_content'] . "</function>\n";
+        }
+        return $ret;
+    }
+
+    /**
+     * Validation error.  Also marks the object contents as invalid
+     * @param error code
+     * @param array error information
+     * @access private
+     */
+    function _validateError($code, $params = array())
+    {
+        $this->_stack->push($code, 'error', $params);
+        $this->_isValid = false;
+    }
+
+    /**
+     * Validation warning.  Does not mark the object contents invalid.
+     * @param error code
+     * @param array error information
+     * @access private
+     */
+    function _validateWarning($code, $params = array())
+    {
+        $this->_stack->push($code, 'warning', $params);
+    }
+
+    /**
+     * Validate parsed file.
+     *
+     * @access public
+     * @return boolean
+     */
+    function validate()
+    {
+        $this->_isValid = true;
+        $info = $this->_channelInfo;
+        if (empty($info['name'])) {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME);
+        } elseif (!$this->validChannelServer($info['name'])) {
+            if ($info['name'] != '__uri') {
+                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name',
+                    'name' => $info['name']));
+            }
+        }
+        if (empty($info['summary'])) {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
+        } elseif (strpos(trim($info['summary']), "\n") !== false) {
+            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
+                array('summary' => $info['summary']));
+        }
+        if (isset($info['suggestedalias'])) {
+            if (!$this->validChannelServer($info['suggestedalias'])) {
+                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
+                    array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias']));
+            }
+        }
+        if (isset($info['localalias'])) {
+            if (!$this->validChannelServer($info['localalias'])) {
+                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
+                    array('tag' => 'localalias', 'name' =>$info['localalias']));
+            }
+        }
+        if (isset($info['validatepackage'])) {
+            if (!isset($info['validatepackage']['_content'])) {
+                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME);
+            }
+            if (!isset($info['validatepackage']['attribs']['version'])) {
+                $content = isset($info['validatepackage']['_content']) ?
+                    $info['validatepackage']['_content'] :
+                    null;
+                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION,
+                    array('package' => $content));
+            }
+        }
+
+        if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) &&
+              !is_numeric($info['servers']['primary']['attribs']['port'])) {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT,
+                array('port' => $info['servers']['primary']['attribs']['port']));
+        }
+
+        if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) &&
+              $info['servers']['primary']['attribs']['ssl'] != 'yes') {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
+                array('ssl' => $info['servers']['primary']['attribs']['ssl'],
+                    'server' => $info['name']));
+        }
+
+        if (isset($info['servers']['primary']['rest']) &&
+              isset($info['servers']['primary']['rest']['baseurl'])) {
+            $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']);
+        }
+        if (isset($info['servers']['mirror'])) {
+            if ($this->_channelInfo['name'] == '__uri') {
+                $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR);
+            }
+            if (!isset($info['servers']['mirror'][0])) {
+                $info['servers']['mirror'] = array($info['servers']['mirror']);
+            }
+            foreach ($info['servers']['mirror'] as $mirror) {
+                if (!isset($mirror['attribs']['host'])) {
+                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST,
+                      array('type' => 'mirror'));
+                } elseif (!$this->validChannelServer($mirror['attribs']['host'])) {
+                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST,
+                        array('server' => $mirror['attribs']['host'], 'type' => 'mirror'));
+                }
+                if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') {
+                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
+                        array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host']));
+                }
+                if (isset($mirror['rest'])) {
+                    $this->_validateFunctions('rest', $mirror['rest']['baseurl'],
+                        $mirror['attribs']['host']);
+                }
+            }
+        }
+        return $this->_isValid;
+    }
+
+    /**
+     * @param string  rest - protocol name this function applies to
+     * @param array the functions
+     * @param string the name of the parent element (mirror name, for instance)
+     */
+    function _validateFunctions($protocol, $functions, $parent = '')
+    {
+        if (!isset($functions[0])) {
+            $functions = array($functions);
+        }
+
+        foreach ($functions as $function) {
+            if (!isset($function['_content']) || empty($function['_content'])) {
+                $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME,
+                    array('parent' => $parent, 'protocol' => $protocol));
+            }
+
+            if ($protocol == 'rest') {
+                if (!isset($function['attribs']['type']) ||
+                      empty($function['attribs']['type'])) {
+                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE,
+                        array('parent' => $parent, 'protocol' => $protocol));
+                }
+            } else {
+                if (!isset($function['attribs']['version']) ||
+                      empty($function['attribs']['version'])) {
+                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION,
+                        array('parent' => $parent, 'protocol' => $protocol));
+                }
+            }
+        }
+    }
+
+    /**
+     * Test whether a string contains a valid channel server.
+     * @param string $ver the package version to test
+     * @return bool
+     */
+    function validChannelServer($server)
+    {
+        if ($server == '__uri') {
+            return true;
+        }
+        return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server);
+    }
+
+    /**
+     * @return string|false
+     */
+    function getName()
+    {
+        if (isset($this->_channelInfo['name'])) {
+            return $this->_channelInfo['name'];
+        }
+
+        return false;
+    }
+
+    /**
+     * @return string|false
+     */
+    function getServer()
+    {
+        if (isset($this->_channelInfo['name'])) {
+            return $this->_channelInfo['name'];
+        }
+
+        return false;
+    }
+
+    /**
+     * @return int|80 port number to connect to
+     */
+    function getPort($mirror = false)
+    {
+        if ($mirror) {
+            if ($mir = $this->getMirror($mirror)) {
+                if (isset($mir['attribs']['port'])) {
+                    return $mir['attribs']['port'];
+                }
+
+                if ($this->getSSL($mirror)) {
+                    return 443;
+                }
+
+                return 80;
+            }
+
+            return false;
+        }
+
+        if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) {
+            return $this->_channelInfo['servers']['primary']['attribs']['port'];
+        }
+
+        if ($this->getSSL()) {
+            return 443;
+        }
+
+        return 80;
+    }
+
+    /**
+     * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel
+     */
+    function getSSL($mirror = false)
+    {
+        if ($mirror) {
+            if ($mir = $this->getMirror($mirror)) {
+                if (isset($mir['attribs']['ssl'])) {
+                    return true;
+                }
+
+                return false;
+            }
+
+            return false;
+        }
+
+        if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @return string|false
+     */
+    function getSummary()
+    {
+        if (isset($this->_channelInfo['summary'])) {
+            return $this->_channelInfo['summary'];
+        }
+
+        return false;
+    }
+
+    /**
+     * @param string protocol type
+     * @param string Mirror name
+     * @return array|false
+     */
+    function getFunctions($protocol, $mirror = false)
+    {
+        if ($this->getName() == '__uri') {
+            return false;
+        }
+
+        $function = $protocol == 'rest' ? 'baseurl' : 'function';
+        if ($mirror) {
+            if ($mir = $this->getMirror($mirror)) {
+                if (isset($mir[$protocol][$function])) {
+                    return $mir[$protocol][$function];
+                }
+            }
+
+            return false;
+        }
+
+        if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) {
+            return $this->_channelInfo['servers']['primary'][$protocol][$function];
+        }
+
+        return false;
+    }
+
+    /**
+     * @param string Protocol type
+     * @param string Function name (null to return the
+     *               first protocol of the type requested)
+     * @param string Mirror name, if any
+     * @return array
+     */
+     function getFunction($type, $name = null, $mirror = false)
+     {
+        $protocols = $this->getFunctions($type, $mirror);
+        if (!$protocols) {
+            return false;
+        }
+
+        foreach ($protocols as $protocol) {
+            if ($name === null) {
+                return $protocol;
+            }
+
+            if ($protocol['_content'] != $name) {
+                continue;
+            }
+
+            return $protocol;
+        }
+
+        return false;
+     }
+
+    /**
+     * @param string protocol type
+     * @param string protocol name
+     * @param string version
+     * @param string mirror name
+     * @return boolean
+     */
+    function supports($type, $name = null, $mirror = false, $version = '1.0')
+    {
+        $protocols = $this->getFunctions($type, $mirror);
+        if (!$protocols) {
+            return false;
+        }
+
+        foreach ($protocols as $protocol) {
+            if ($protocol['attribs']['version'] != $version) {
+                continue;
+            }
+
+            if ($name === null) {
+                return true;
+            }
+
+            if ($protocol['_content'] != $name) {
+                continue;
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Determines whether a channel supports Representational State Transfer (REST) protocols
+     * for retrieving channel information
+     * @param string
+     * @return bool
+     */
+    function supportsREST($mirror = false)
+    {
+        if ($mirror == $this->_channelInfo['name']) {
+            $mirror = false;
+        }
+
+        if ($mirror) {
+            if ($mir = $this->getMirror($mirror)) {
+                return isset($mir['rest']);
+            }
+
+            return false;
+        }
+
+        return isset($this->_channelInfo['servers']['primary']['rest']);
+    }
+
+    /**
+     * Get the URL to access a base resource.
+     *
+     * Hyperlinks in the returned xml will be used to retrieve the proper information
+     * needed.  This allows extreme extensibility and flexibility in implementation
+     * @param string Resource Type to retrieve
+     */
+    function getBaseURL($resourceType, $mirror = false)
+    {
+        if ($mirror == $this->_channelInfo['name']) {
+            $mirror = false;
+        }
+
+        if ($mirror) {
+            $mir = $this->getMirror($mirror);
+            if (!$mir) {
+                return false;
+            }
+
+            $rest = $mir['rest'];
+        } else {
+            $rest = $this->_channelInfo['servers']['primary']['rest'];
+        }
+
+        if (!isset($rest['baseurl'][0])) {
+            $rest['baseurl'] = array($rest['baseurl']);
+        }
+
+        foreach ($rest['baseurl'] as $baseurl) {
+            if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) {
+                return $baseurl['_content'];
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Since REST does not implement RPC, provide this as a logical wrapper around
+     * resetFunctions for REST
+     * @param string|false mirror name, if any
+     */
+    function resetREST($mirror = false)
+    {
+        return $this->resetFunctions('rest', $mirror);
+    }
+
+    /**
+     * Empty all protocol definitions
+     * @param string protocol type
+     * @param string|false mirror name, if any
+     */
+    function resetFunctions($type, $mirror = false)
+    {
+        if ($mirror) {
+            if (isset($this->_channelInfo['servers']['mirror'])) {
+                $mirrors = $this->_channelInfo['servers']['mirror'];
+                if (!isset($mirrors[0])) {
+                    $mirrors = array($mirrors);
+                }
+
+                foreach ($mirrors as $i => $mir) {
+                    if ($mir['attribs']['host'] == $mirror) {
+                        if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) {
+                            unset($this->_channelInfo['servers']['mirror'][$i][$type]);
+                        }
+
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            return false;
+        }
+
+        if (isset($this->_channelInfo['servers']['primary'][$type])) {
+            unset($this->_channelInfo['servers']['primary'][$type]);
+        }
+
+        return true;
+    }
+
+    /**
+     * Set a channel's protocols to the protocols supported by pearweb
+     */
+    function setDefaultPEARProtocols($version = '1.0', $mirror = false)
+    {
+        switch ($version) {
+            case '1.0' :
+                $this->resetREST($mirror);
+
+                if (!isset($this->_channelInfo['servers'])) {
+                    $this->_channelInfo['servers'] = array('primary' =>
+                        array('rest' => array()));
+                } elseif (!isset($this->_channelInfo['servers']['primary'])) {
+                    $this->_channelInfo['servers']['primary'] = array('rest' => array());
+                }
+
+                return true;
+            break;
+            default :
+                return false;
+            break;
+        }
+    }
+
+    /**
+     * @return array
+     */
+    function getMirrors()
+    {
+        if (isset($this->_channelInfo['servers']['mirror'])) {
+            $mirrors = $this->_channelInfo['servers']['mirror'];
+            if (!isset($mirrors[0])) {
+                $mirrors = array($mirrors);
+            }
+
+            return $mirrors;
+        }
+
+        return array();
+    }
+
+    /**
+     * Get the unserialized XML representing a mirror
+     * @return array|false
+     */
+    function getMirror($server)
+    {
+        foreach ($this->getMirrors() as $mirror) {
+            if ($mirror['attribs']['host'] == $server) {
+                return $mirror;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @param string
+     * @return string|false
+     * @error PEAR_CHANNELFILE_ERROR_NO_NAME
+     * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME
+     */
+    function setName($name)
+    {
+        return $this->setServer($name);
+    }
+
+    /**
+     * Set the socket number (port) that is used to connect to this channel
+     * @param integer
+     * @param string|false name of the mirror server, or false for the primary
+     */
+    function setPort($port, $mirror = false)
+    {
+        if ($mirror) {
+            if (!isset($this->_channelInfo['servers']['mirror'])) {
+                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
+                    array('mirror' => $mirror));
+                return false;
+            }
+
+            if (isset($this->_channelInfo['servers']['mirror'][0])) {
+                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
+                    if ($mirror == $mir['attribs']['host']) {
+                        $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port;
+                        return true;
+                    }
+                }
+
+                return false;
+            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
+                $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port;
+                $this->_isValid = false;
+                return true;
+            }
+        }
+
+        $this->_channelInfo['servers']['primary']['attribs']['port'] = $port;
+        $this->_isValid = false;
+        return true;
+    }
+
+    /**
+     * Set the socket number (port) that is used to connect to this channel
+     * @param bool Determines whether to turn on SSL support or turn it off
+     * @param string|false name of the mirror server, or false for the primary
+     */
+    function setSSL($ssl = true, $mirror = false)
+    {
+        if ($mirror) {
+            if (!isset($this->_channelInfo['servers']['mirror'])) {
+                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
+                    array('mirror' => $mirror));
+                return false;
+            }
+
+            if (isset($this->_channelInfo['servers']['mirror'][0])) {
+                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
+                    if ($mirror == $mir['attribs']['host']) {
+                        if (!$ssl) {
+                            if (isset($this->_channelInfo['servers']['mirror'][$i]
+                                  ['attribs']['ssl'])) {
+                                unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']);
+                            }
+                        } else {
+                            $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes';
+                        }
+
+                        return true;
+                    }
+                }
+
+                return false;
+            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
+                if (!$ssl) {
+                    if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) {
+                        unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']);
+                    }
+                } else {
+                    $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes';
+                }
+
+                $this->_isValid = false;
+                return true;
+            }
+        }
+
+        if ($ssl) {
+            $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes';
+        } else {
+            if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
+                unset($this->_channelInfo['servers']['primary']['attribs']['ssl']);
+            }
+        }
+
+        $this->_isValid = false;
+        return true;
+    }
+
+    /**
+     * @param string
+     * @return string|false
+     * @error PEAR_CHANNELFILE_ERROR_NO_SERVER
+     * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER
+     */
+    function setServer($server, $mirror = false)
+    {
+        if (empty($server)) {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER);
+            return false;
+        } elseif (!$this->validChannelServer($server)) {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
+                array('tag' => 'name', 'name' => $server));
+            return false;
+        }
+
+        if ($mirror) {
+            $found = false;
+            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
+                if ($mirror == $mir['attribs']['host']) {
+                    $found = true;
+                    break;
+                }
+            }
+
+            if (!$found) {
+                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
+                    array('mirror' => $mirror));
+                return false;
+            }
+
+            $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server;
+            return true;
+        }
+
+        $this->_channelInfo['name'] = $server;
+        return true;
+    }
+
+    /**
+     * @param string
+     * @return boolean success
+     * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY
+     * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY
+     */
+    function setSummary($summary)
+    {
+        if (empty($summary)) {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
+            return false;
+        } elseif (strpos(trim($summary), "\n") !== false) {
+            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
+                array('summary' => $summary));
+        }
+
+        $this->_channelInfo['summary'] = $summary;
+        return true;
+    }
+
+    /**
+     * @param string
+     * @param boolean determines whether the alias is in channel.xml or local
+     * @return boolean success
+     */
+    function setAlias($alias, $local = false)
+    {
+        if (!$this->validChannelServer($alias)) {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
+                array('tag' => 'suggestedalias', 'name' => $alias));
+            return false;
+        }
+
+        if ($local) {
+            $this->_channelInfo['localalias'] = $alias;
+        } else {
+            $this->_channelInfo['suggestedalias'] = $alias;
+        }
+
+        return true;
+    }
+
+    /**
+     * @return string
+     */
+    function getAlias()
+    {
+        if (isset($this->_channelInfo['localalias'])) {
+            return $this->_channelInfo['localalias'];
+        }
+        if (isset($this->_channelInfo['suggestedalias'])) {
+            return $this->_channelInfo['suggestedalias'];
+        }
+        if (isset($this->_channelInfo['name'])) {
+            return $this->_channelInfo['name'];
+        }
+        return '';
+    }
+
+    /**
+     * Set the package validation object if it differs from PEAR's default
+     * The class must be includeable via changing _ in the classname to path separator,
+     * but no checking of this is made.
+     * @param string|false pass in false to reset to the default packagename regex
+     * @return boolean success
+     */
+    function setValidationPackage($validateclass, $version)
+    {
+        if (empty($validateclass)) {
+            unset($this->_channelInfo['validatepackage']);
+        }
+        $this->_channelInfo['validatepackage'] = array('_content' => $validateclass);
+        $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version);
+    }
+
+    /**
+     * Add a protocol to the provides section
+     * @param string protocol type
+     * @param string protocol version
+     * @param string protocol name, if any
+     * @param string mirror name, if this is a mirror's protocol
+     * @return bool
+     */
+    function addFunction($type, $version, $name = '', $mirror = false)
+    {
+        if ($mirror) {
+            return $this->addMirrorFunction($mirror, $type, $version, $name);
+        }
+
+        $set = array('attribs' => array('version' => $version), '_content' => $name);
+        if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) {
+            if (!isset($this->_channelInfo['servers'])) {
+                $this->_channelInfo['servers'] = array('primary' =>
+                    array($type => array()));
+            } elseif (!isset($this->_channelInfo['servers']['primary'])) {
+                $this->_channelInfo['servers']['primary'] = array($type => array());
+            }
+
+            $this->_channelInfo['servers']['primary'][$type]['function'] = $set;
+            $this->_isValid = false;
+            return true;
+        } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) {
+            $this->_channelInfo['servers']['primary'][$type]['function'] = array(
+                $this->_channelInfo['servers']['primary'][$type]['function']);
+        }
+
+        $this->_channelInfo['servers']['primary'][$type]['function'][] = $set;
+        return true;
+    }
+    /**
+     * Add a protocol to a mirror's provides section
+     * @param string mirror name (server)
+     * @param string protocol type
+     * @param string protocol version
+     * @param string protocol name, if any
+     */
+    function addMirrorFunction($mirror, $type, $version, $name = '')
+    {
+        if (!isset($this->_channelInfo['servers']['mirror'])) {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
+                array('mirror' => $mirror));
+            return false;
+        }
+
+        $setmirror = false;
+        if (isset($this->_channelInfo['servers']['mirror'][0])) {
+            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
+                if ($mirror == $mir['attribs']['host']) {
+                    $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
+                    break;
+                }
+            }
+        } else {
+            if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
+                $setmirror = &$this->_channelInfo['servers']['mirror'];
+            }
+        }
+
+        if (!$setmirror) {
+            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
+                array('mirror' => $mirror));
+            return false;
+        }
+
+        $set = array('attribs' => array('version' => $version), '_content' => $name);
+        if (!isset($setmirror[$type]['function'])) {
+            $setmirror[$type]['function'] = $set;
+            $this->_isValid = false;
+            return true;
+        } elseif (!isset($setmirror[$type]['function'][0])) {
+            $setmirror[$type]['function'] = array($setmirror[$type]['function']);
+        }
+
+        $setmirror[$type]['function'][] = $set;
+        $this->_isValid = false;
+        return true;
+    }
+
+    /**
+     * @param string Resource Type this url links to
+     * @param string URL
+     * @param string|false mirror name, if this is not a primary server REST base URL
+     */
+    function setBaseURL($resourceType, $url, $mirror = false)
+    {
+        if ($mirror) {
+            if (!isset($this->_channelInfo['servers']['mirror'])) {
+                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
+                    array('mirror' => $mirror));
+                return false;
+            }
+
+            $setmirror = false;
+            if (isset($this->_channelInfo['servers']['mirror'][0])) {
+                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
+                    if ($mirror == $mir['attribs']['host']) {
+                        $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
+                        break;
+                    }
+                }
+            } else {
+                if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
+                    $setmirror = &$this->_channelInfo['servers']['mirror'];
+                }
+            }
+        } else {
+            $setmirror = &$this->_channelInfo['servers']['primary'];
+        }
+
+        $set = array('attribs' => array('type' => $resourceType), '_content' => $url);
+        if (!isset($setmirror['rest'])) {
+            $setmirror['rest'] = array();
+        }
+
+        if (!isset($setmirror['rest']['baseurl'])) {
+            $setmirror['rest']['baseurl'] = $set;
+            $this->_isValid = false;
+            return true;
+        } elseif (!isset($setmirror['rest']['baseurl'][0])) {
+            $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']);
+        }
+
+        foreach ($setmirror['rest']['baseurl'] as $i => $url) {
+            if ($url['attribs']['type'] == $resourceType) {
+                $this->_isValid = false;
+                $setmirror['rest']['baseurl'][$i] = $set;
+                return true;
+            }
+        }
+
+        $setmirror['rest']['baseurl'][] = $set;
+        $this->_isValid = false;
+        return true;
+    }
+
+    /**
+     * @param string mirror server
+     * @param int mirror http port
+     * @return boolean
+     */
+    function addMirror($server, $port = null)
+    {
+        if ($this->_channelInfo['name'] == '__uri') {
+            return false; // the __uri channel cannot have mirrors by definition
+        }
+
+        $set = array('attribs' => array('host' => $server));
+        if (is_numeric($port)) {
+            $set['attribs']['port'] = $port;
+        }
+
+        if (!isset($this->_channelInfo['servers']['mirror'])) {
+            $this->_channelInfo['servers']['mirror'] = $set;
+            return true;
+        }
+
+        if (!isset($this->_channelInfo['servers']['mirror'][0])) {
+            $this->_channelInfo['servers']['mirror'] =
+                array($this->_channelInfo['servers']['mirror']);
+        }
+
+        $this->_channelInfo['servers']['mirror'][] = $set;
+        return true;
+    }
+
+    /**
+     * Retrieve the name of the validation package for this channel
+     * @return string|false
+     */
+    function getValidationPackage()
+    {
+        if (!$this->_isValid && !$this->validate()) {
+            return false;
+        }
+
+        if (!isset($this->_channelInfo['validatepackage'])) {
+            return array('attribs' => array('version' => 'default'),
+                '_content' => 'PEAR_Validate');
+        }
+
+        return $this->_channelInfo['validatepackage'];
+    }
+
+    /**
+     * Retrieve the object that can be used for custom validation
+     * @param string|false the name of the package to validate.  If the package is
+     *                     the channel validation package, PEAR_Validate is returned
+     * @return PEAR_Validate|false false is returned if the validation package
+     *         cannot be located
+     */
+    function &getValidationObject($package = false)
+    {
+        if (!class_exists('PEAR_Validate')) {
+            require_once 'PEAR/Validate.php';
+        }
+
+        if (!$this->_isValid) {
+            if (!$this->validate()) {
+                $a = false;
+                return $a;
+            }
+        }
+
+        if (isset($this->_channelInfo['validatepackage'])) {
+            if ($package == $this->_channelInfo['validatepackage']) {
+                // channel validation packages are always validated by PEAR_Validate
+                $val = &new PEAR_Validate;
+                return $val;
+            }
+
+            if (!class_exists(str_replace('.', '_',
+                  $this->_channelInfo['validatepackage']['_content']))) {
+                if ($this->isIncludeable(str_replace('_', '/',
+                      $this->_channelInfo['validatepackage']['_content']) . '.php')) {
+                    include_once str_replace('_', '/',
+                        $this->_channelInfo['validatepackage']['_content']) . '.php';
+                    $vclass = str_replace('.', '_',
+                        $this->_channelInfo['validatepackage']['_content']);
+                    $val = &new $vclass;
+                } else {
+                    $a = false;
+                    return $a;
+                }
+            } else {
+                $vclass = str_replace('.', '_',
+                    $this->_channelInfo['validatepackage']['_content']);
+                $val = &new $vclass;
+            }
+        } else {
+            $val = &new PEAR_Validate;
+        }
+
+        return $val;
+    }
+
+    function isIncludeable($path)
+    {
+        $possibilities = explode(PATH_SEPARATOR, ini_get('include_path'));
+        foreach ($possibilities as $dir) {
+            if (file_exists($dir . DIRECTORY_SEPARATOR . $path)
+                  && is_readable($dir . DIRECTORY_SEPARATOR . $path)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * This function is used by the channel updater and retrieves a value set by
+     * the registry, or the current time if it has not been set
+     * @return string
+     */
+    function lastModified()
+    {
+        if (isset($this->_channelInfo['_lastmodified'])) {
+            return $this->_channelInfo['_lastmodified'];
+        }
+
+        return time();
+    }
+}
Index: trunk/library/PEAR/PEAR/Downloader/Package.php
===================================================================
--- trunk/library/PEAR/PEAR/Downloader/Package.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Downloader/Package.php	(revision 5146)
@@ -0,0 +1,1988 @@
+<?php
+/**
+ * PEAR_Downloader_Package
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Package.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * Error code when parameter initialization fails because no releases
+ * exist within preferred_state, but releases do exist
+ */
+define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
+/**
+ * Error code when parameter initialization fails because no releases
+ * exist that will work with the existing PHP version
+ */
+define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004);
+
+/**
+ * Coordinates download parameters and manages their dependencies
+ * prior to downloading them.
+ *
+ * Input can come from three sources:
+ *
+ * - local files (archives or package.xml)
+ * - remote files (downloadable urls)
+ * - abstract package names
+ *
+ * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
+ * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
+ * format returned of dependencies is slightly different from that used in package.xml.
+ *
+ * This class hides the differences between these elements, and makes automatic
+ * dependency resolution a piece of cake.  It also manages conflicts when
+ * two classes depend on incompatible dependencies, or differing versions of the same
+ * package dependency.  In addition, download will not be attempted if the php version is
+ * not supported, PEAR installer version is not supported, or non-PECL extensions are not
+ * installed.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Downloader_Package
+{
+    /**
+     * @var PEAR_Downloader
+     */
+    var $_downloader;
+    /**
+     * @var PEAR_Config
+     */
+    var $_config;
+    /**
+     * @var PEAR_Registry
+     */
+    var $_registry;
+    /**
+     * Used to implement packagingroot properly
+     * @var PEAR_Registry
+     */
+    var $_installRegistry;
+    /**
+     * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
+     */
+    var $_packagefile;
+    /**
+     * @var array
+     */
+    var $_parsedname;
+    /**
+     * @var array
+     */
+    var $_downloadURL;
+    /**
+     * @var array
+     */
+    var $_downloadDeps = array();
+    /**
+     * @var boolean
+     */
+    var $_valid = false;
+    /**
+     * @var boolean
+     */
+    var $_analyzed = false;
+    /**
+     * if this or a parent package was invoked with Package-state, this is set to the
+     * state variable.
+     *
+     * This allows temporary reassignment of preferred_state for a parent package and all of
+     * its dependencies.
+     * @var string|false
+     */
+    var $_explicitState = false;
+    /**
+     * If this package is invoked with Package#group, this variable will be true
+     */
+    var $_explicitGroup = false;
+    /**
+     * Package type local|url
+     * @var string
+     */
+    var $_type;
+    /**
+     * Contents of package.xml, if downloaded from a remote channel
+     * @var string|false
+     * @access private
+     */
+    var $_rawpackagefile;
+    /**
+     * @var boolean
+     * @access private
+     */
+    var $_validated = false;
+
+    /**
+     * @param PEAR_Downloader
+     */
+    function PEAR_Downloader_Package(&$downloader)
+    {
+        $this->_downloader = &$downloader;
+        $this->_config = &$this->_downloader->config;
+        $this->_registry = &$this->_config->getRegistry();
+        $options = $downloader->getOptions();
+        if (isset($options['packagingroot'])) {
+            $this->_config->setInstallRoot($options['packagingroot']);
+            $this->_installRegistry = &$this->_config->getRegistry();
+            $this->_config->setInstallRoot(false);
+        } else {
+            $this->_installRegistry = &$this->_registry;
+        }
+        $this->_valid = $this->_analyzed = false;
+    }
+
+    /**
+     * Parse the input and determine whether this is a local file, a remote uri, or an
+     * abstract package name.
+     *
+     * This is the heart of the PEAR_Downloader_Package(), and is used in
+     * {@link PEAR_Downloader::download()}
+     * @param string
+     * @return bool|PEAR_Error
+     */
+    function initialize($param)
+    {
+        $origErr = $this->_fromFile($param);
+        if ($this->_valid) {
+            return true;
+        }
+
+        $options = $this->_downloader->getOptions();
+        if (isset($options['offline'])) {
+            if (PEAR::isError($origErr) && !isset($options['soft'])) {
+                foreach ($origErr->getUserInfo() as $userInfo) {
+                    if (isset($userInfo['message'])) {
+                        $this->_downloader->log(0, $userInfo['message']);
+                    }
+                }
+
+                $this->_downloader->log(0, $origErr->getMessage());
+            }
+
+            return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
+        }
+
+        $err = $this->_fromUrl($param);
+        if (PEAR::isError($err) || !$this->_valid) {
+            if ($this->_type == 'url') {
+                if (PEAR::isError($err) && !isset($options['soft'])) {
+                    $this->_downloader->log(0, $err->getMessage());
+                }
+
+                return PEAR::raiseError("Invalid or missing remote package file");
+            }
+
+            $err = $this->_fromString($param);
+            if (PEAR::isError($err) || !$this->_valid) {
+                if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
+                    return false; // instruct the downloader to silently skip
+                }
+
+                if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) {
+                    if (is_array($origErr->getUserInfo())) {
+                        foreach ($origErr->getUserInfo() as $err) {
+                            if (is_array($err)) {
+                                $err = $err['message'];
+                            }
+
+                            if (!isset($options['soft'])) {
+                                $this->_downloader->log(0, $err);
+                            }
+                        }
+                    }
+
+                    if (!isset($options['soft'])) {
+                        $this->_downloader->log(0, $origErr->getMessage());
+                    }
+
+                    if (is_array($param)) {
+                        $param = $this->_registry->parsedPackageNameToString($param, true);
+                    }
+
+                    if (!isset($options['soft'])) {
+                        $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
+                    }
+
+                    // Passing no message back - already logged above
+                    return PEAR::raiseError();
+                }
+
+                if (PEAR::isError($err) && !isset($options['soft'])) {
+                    $this->_downloader->log(0, $err->getMessage());
+                }
+
+                if (is_array($param)) {
+                    $param = $this->_registry->parsedPackageNameToString($param, true);
+                }
+
+                if (!isset($options['soft'])) {
+                    $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
+                }
+
+                // Passing no message back - already logged above
+                return PEAR::raiseError();
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Retrieve any non-local packages
+     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
+     */
+    function &download()
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile;
+        }
+
+        if (isset($this->_downloadURL['url'])) {
+            $this->_isvalid = false;
+            $info = $this->getParsedPackage();
+            foreach ($info as $i => $p) {
+                $info[$i] = strtolower($p);
+            }
+
+            $err = $this->_fromUrl($this->_downloadURL['url'],
+                $this->_registry->parsedPackageNameToString($this->_parsedname, true));
+            $newinfo = $this->getParsedPackage();
+            foreach ($newinfo as $i => $p) {
+                $newinfo[$i] = strtolower($p);
+            }
+
+            if ($info != $newinfo) {
+                do {
+                    if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') {
+                        $info['channel'] = 'pear.php.net';
+                        if ($info == $newinfo) {
+                            // skip the channel check if a pecl package says it's a PEAR package
+                            break;
+                        }
+                    }
+                    if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') {
+                        $info['channel'] = 'pecl.php.net';
+                        if ($info == $newinfo) {
+                            // skip the channel check if a pecl package says it's a PEAR package
+                            break;
+                        }
+                    }
+
+                    return PEAR::raiseError('CRITICAL ERROR: We are ' .
+                        $this->_registry->parsedPackageNameToString($info) . ', but the file ' .
+                        'downloaded claims to be ' .
+                        $this->_registry->parsedPackageNameToString($this->getParsedPackage()));
+                } while (false);
+            }
+
+            if (PEAR::isError($err) || !$this->_valid) {
+                return $err;
+            }
+        }
+
+        $this->_type = 'local';
+        return $this->_packagefile;
+    }
+
+    function &getPackageFile()
+    {
+        return $this->_packagefile;
+    }
+
+    function &getDownloader()
+    {
+        return $this->_downloader;
+    }
+
+    function getType()
+    {
+        return $this->_type;
+    }
+
+    /**
+     * Like {@link initialize()}, but operates on a dependency
+     */
+    function fromDepURL($dep)
+    {
+        $this->_downloadURL = $dep;
+        if (isset($dep['uri'])) {
+            $options = $this->_downloader->getOptions();
+            if (!extension_loaded("zlib") || isset($options['nocompress'])) {
+                $ext = '.tar';
+            } else {
+                $ext = '.tgz';
+            }
+
+            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+            $err = $this->_fromUrl($dep['uri'] . $ext);
+            PEAR::popErrorHandling();
+            if (PEAR::isError($err)) {
+                if (!isset($options['soft'])) {
+                    $this->_downloader->log(0, $err->getMessage());
+                }
+
+                return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
+                    'cannot download');
+            }
+        } else {
+            $this->_parsedname =
+                array(
+                    'package' => $dep['info']->getPackage(),
+                    'channel' => $dep['info']->getChannel(),
+                    'version' => $dep['version']
+                );
+            if (!isset($dep['nodefault'])) {
+                $this->_parsedname['group'] = 'default'; // download the default dependency group
+                $this->_explicitGroup = false;
+            }
+
+            $this->_rawpackagefile = $dep['raw'];
+        }
+    }
+
+    function detectDependencies($params)
+    {
+        $options = $this->_downloader->getOptions();
+        if (isset($options['downloadonly'])) {
+            return;
+        }
+
+        if (isset($options['offline'])) {
+            $this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
+            return;
+        }
+
+        $pname = $this->getParsedPackage();
+        if (!$pname) {
+            return;
+        }
+
+        $deps = $this->getDeps();
+        if (!$deps) {
+            return;
+        }
+
+        if (isset($deps['required'])) { // package.xml 2.0
+            return $this->_detect2($deps, $pname, $options, $params);
+        }
+
+        return $this->_detect1($deps, $pname, $options, $params);
+    }
+
+    function setValidated()
+    {
+        $this->_validated = true;
+    }
+
+    function alreadyValidated()
+    {
+        return $this->_validated;
+    }
+
+    /**
+     * Remove packages to be downloaded that are already installed
+     * @param array of PEAR_Downloader_Package objects
+     * @static
+     */
+    function removeInstalled(&$params)
+    {
+        if (!isset($params[0])) {
+            return;
+        }
+
+        $options = $params[0]->_downloader->getOptions();
+        if (!isset($options['downloadonly'])) {
+            foreach ($params as $i => $param) {
+                $package = $param->getPackage();
+                $channel = $param->getChannel();
+                // remove self if already installed with this version
+                // this does not need any pecl magic - we only remove exact matches
+                if ($param->_installRegistry->packageExists($package, $channel)) {
+                    $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel);
+                    if (version_compare($packageVersion, $param->getVersion(), '==')) {
+                        if (!isset($options['force'])) {
+                            $info = $param->getParsedPackage();
+                            unset($info['version']);
+                            unset($info['state']);
+                            if (!isset($options['soft'])) {
+                                $param->_downloader->log(1, 'Skipping package "' .
+                                    $param->getShortName() .
+                                    '", already installed as version ' . $packageVersion);
+                            }
+                            $params[$i] = false;
+                        }
+                    } elseif (!isset($options['force']) && !isset($options['upgrade']) &&
+                          !isset($options['soft'])) {
+                        $info = $param->getParsedPackage();
+                        $param->_downloader->log(1, 'Skipping package "' .
+                            $param->getShortName() .
+                            '", already installed as version ' . $packageVersion);
+                        $params[$i] = false;
+                    }
+                }
+            }
+        }
+
+        PEAR_Downloader_Package::removeDuplicates($params);
+    }
+
+    function _detect2($deps, $pname, $options, $params)
+    {
+        $this->_downloadDeps = array();
+        $groupnotfound = false;
+        foreach (array('package', 'subpackage') as $packagetype) {
+            // get required dependency group
+            if (isset($deps['required'][$packagetype])) {
+                if (isset($deps['required'][$packagetype][0])) {
+                    foreach ($deps['required'][$packagetype] as $dep) {
+                        if (isset($dep['conflicts'])) {
+                            // skip any package that this package conflicts with
+                            continue;
+                        }
+                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
+                        if (is_array($ret)) {
+                            $this->_downloadDeps[] = $ret;
+                        } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
+                            $this->_downloader->log(0, $ret->getMessage());
+                        }
+                    }
+                } else {
+                    $dep = $deps['required'][$packagetype];
+                    if (!isset($dep['conflicts'])) {
+                        // skip any package that this package conflicts with
+                        $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
+                        if (is_array($ret)) {
+                            $this->_downloadDeps[] = $ret;
+                        } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
+                            $this->_downloader->log(0, $ret->getMessage());
+                        }
+                    }
+                }
+            }
+
+            // get optional dependency group, if any
+            if (isset($deps['optional'][$packagetype])) {
+                $skipnames = array();
+                if (!isset($deps['optional'][$packagetype][0])) {
+                    $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
+                }
+
+                foreach ($deps['optional'][$packagetype] as $dep) {
+                    $skip = false;
+                    if (!isset($options['alldeps'])) {
+                        $dep['package'] = $dep['name'];
+                        if (!isset($options['soft'])) {
+                            $this->_downloader->log(3, 'Notice: package "' .
+                              $this->_registry->parsedPackageNameToString($this->getParsedPackage(),
+                                    true) . '" optional dependency "' .
+                                $this->_registry->parsedPackageNameToString(array('package' =>
+                                    $dep['name'], 'channel' => 'pear.php.net'), true) .
+                                '" will not be automatically downloaded');
+                        }
+                        $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
+                        $skip = true;
+                        unset($dep['package']);
+                    }
+
+                    $ret = $this->_detect2Dep($dep, $pname, 'optional', $params);
+                    if (PEAR::isError($ret) && !isset($options['soft'])) {
+                        $this->_downloader->log(0, $ret->getMessage());
+                    }
+
+                    if (!$ret) {
+                        $dep['package'] = $dep['name'];
+                        $skip = count($skipnames) ?
+                            $skipnames[count($skipnames) - 1] : '';
+                        if ($skip ==
+                              $this->_registry->parsedPackageNameToString($dep, true)) {
+                            array_pop($skipnames);
+                        }
+                    }
+
+                    if (!$skip && is_array($ret)) {
+                        $this->_downloadDeps[] = $ret;
+                    }
+                }
+
+                if (count($skipnames)) {
+                    if (!isset($options['soft'])) {
+                        $this->_downloader->log(1, 'Did not download optional dependencies: ' .
+                            implode(', ', $skipnames) .
+                            ', use --alldeps to download automatically');
+                    }
+                }
+            }
+
+            // get requested dependency group, if any
+            $groupname = $this->getGroup();
+            $explicit  = $this->_explicitGroup;
+            if (!$groupname) {
+                if (!$this->canDefault()) {
+                    continue;
+                }
+
+                $groupname = 'default'; // try the default dependency group
+            }
+
+            if ($groupnotfound) {
+                continue;
+            }
+
+            if (isset($deps['group'])) {
+                if (isset($deps['group']['attribs'])) {
+                    if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
+                        $group = $deps['group'];
+                    } elseif ($explicit) {
+                        if (!isset($options['soft'])) {
+                            $this->_downloader->log(0, 'Warning: package "' .
+                                $this->_registry->parsedPackageNameToString($pname, true) .
+                                '" has no dependency ' . 'group named "' . $groupname . '"');
+                        }
+
+                        $groupnotfound = true;
+                        continue;
+                    }
+                } else {
+                    $found = false;
+                    foreach ($deps['group'] as $group) {
+                        if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
+                            $found = true;
+                            break;
+                        }
+                    }
+
+                    if (!$found) {
+                        if ($explicit) {
+                            if (!isset($options['soft'])) {
+                                $this->_downloader->log(0, 'Warning: package "' .
+                                    $this->_registry->parsedPackageNameToString($pname, true) .
+                                    '" has no dependency ' . 'group named "' . $groupname . '"');
+                            }
+                        }
+
+                        $groupnotfound = true;
+                        continue;
+                    }
+                }
+            }
+
+            if (isset($group) && isset($group[$packagetype])) {
+                if (isset($group[$packagetype][0])) {
+                    foreach ($group[$packagetype] as $dep) {
+                        $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
+                            $group['attribs']['name'] . '"', $params);
+                        if (is_array($ret)) {
+                            $this->_downloadDeps[] = $ret;
+                        } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
+                            $this->_downloader->log(0, $ret->getMessage());
+                        }
+                    }
+                } else {
+                    $ret = $this->_detect2Dep($group[$packagetype], $pname,
+                        'dependency group "' .
+                        $group['attribs']['name'] . '"', $params);
+                    if (is_array($ret)) {
+                        $this->_downloadDeps[] = $ret;
+                    } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
+                        $this->_downloader->log(0, $ret->getMessage());
+                    }
+                }
+            }
+        }
+    }
+
+    function _detect2Dep($dep, $pname, $group, $params)
+    {
+        if (isset($dep['conflicts'])) {
+            return true;
+        }
+
+        $options = $this->_downloader->getOptions();
+        if (isset($dep['uri'])) {
+            return array('uri' => $dep['uri'], 'dep' => $dep);;
+        }
+
+        $testdep = $dep;
+        $testdep['package'] = $dep['name'];
+        if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
+            $dep['package'] = $dep['name'];
+            if (!isset($options['soft'])) {
+                $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
+                    ' dependency "' .
+                    $this->_registry->parsedPackageNameToString($dep, true) .
+                    '", will be installed');
+            }
+            return false;
+        }
+
+        $options = $this->_downloader->getOptions();
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+        if ($this->_explicitState) {
+            $pname['state'] = $this->_explicitState;
+        }
+
+        $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
+        if (PEAR::isError($url)) {
+            PEAR::popErrorHandling();
+            return $url;
+        }
+
+        $dep['package'] = $dep['name'];
+        $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
+            !isset($options['alldeps']), true);
+        PEAR::popErrorHandling();
+        if (PEAR::isError($ret)) {
+            if (!isset($options['soft'])) {
+                $this->_downloader->log(0, $ret->getMessage());
+            }
+
+            return false;
+        }
+
+        // check to see if a dep is already installed and is the same or newer
+        if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
+            $oper = 'has';
+        } else {
+            $oper = 'gt';
+        }
+
+        // do not try to move this before getDepPackageDownloadURL
+        // we can't determine whether upgrade is necessary until we know what
+        // version would be downloaded
+        if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
+            $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']);
+            $dep['package'] = $dep['name'];
+            if (!isset($options['soft'])) {
+                $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
+                    ' dependency "' .
+                $this->_registry->parsedPackageNameToString($dep, true) .
+                    '" version ' . $url['version'] . ', already installed as version ' .
+                    $version);
+            }
+
+            return false;
+        }
+
+        if (isset($dep['nodefault'])) {
+            $ret['nodefault'] = true;
+        }
+
+        return $ret;
+    }
+
+    function _detect1($deps, $pname, $options, $params)
+    {
+        $this->_downloadDeps = array();
+        $skipnames = array();
+        foreach ($deps as $dep) {
+            $nodownload = false;
+            if (isset ($dep['type']) && $dep['type'] === 'pkg') {
+                $dep['channel'] = 'pear.php.net';
+                $dep['package'] = $dep['name'];
+                switch ($dep['rel']) {
+                    case 'not' :
+                        continue 2;
+                    case 'ge' :
+                    case 'eq' :
+                    case 'gt' :
+                    case 'has' :
+                        $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
+                            'required' :
+                            'optional';
+                        if (PEAR_Downloader_Package::willDownload($dep, $params)) {
+                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
+                                . ' dependency "' .
+                                $this->_registry->parsedPackageNameToString($dep, true) .
+                                '", will be installed');
+                            continue 2;
+                        }
+                        $fakedp = new PEAR_PackageFile_v1;
+                        $fakedp->setPackage($dep['name']);
+                        // skip internet check if we are not upgrading (bug #5810)
+                        if (!isset($options['upgrade']) && $this->isInstalled(
+                              $fakedp, $dep['rel'])) {
+                            $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
+                                . ' dependency "' .
+                                $this->_registry->parsedPackageNameToString($dep, true) .
+                                '", is already installed');
+                            continue 2;
+                        }
+                }
+
+                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                if ($this->_explicitState) {
+                    $pname['state'] = $this->_explicitState;
+                }
+
+                $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
+                $chan = 'pear.php.net';
+                if (PEAR::isError($url)) {
+                    // check to see if this is a pecl package that has jumped
+                    // from pear.php.net to pecl.php.net channel
+                    if (!class_exists('PEAR_Dependency2')) {
+                        require_once 'PEAR/Dependency2.php';
+                    }
+
+                    $newdep = PEAR_Dependency2::normalizeDep($dep);
+                    $newdep = $newdep[0];
+                    $newdep['channel'] = 'pecl.php.net';
+                    $chan = 'pecl.php.net';
+                    $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
+                    $obj = &$this->_installRegistry->getPackage($dep['name']);
+                    if (PEAR::isError($url)) {
+                        PEAR::popErrorHandling();
+                        if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
+                            $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
+                                'required' :
+                                'optional';
+                            $dep['package'] = $dep['name'];
+                            if (!isset($options['soft'])) {
+                                $this->_downloader->log(3, $this->getShortName() .
+                                    ': Skipping ' . $group . ' dependency "' .
+                                    $this->_registry->parsedPackageNameToString($dep, true) .
+                                    '", already installed as version ' . $obj->getVersion());
+                            }
+                            $skip = count($skipnames) ?
+                                $skipnames[count($skipnames) - 1] : '';
+                            if ($skip ==
+                                  $this->_registry->parsedPackageNameToString($dep, true)) {
+                                array_pop($skipnames);
+                            }
+                            continue;
+                        } else {
+                            if (isset($dep['optional']) && $dep['optional'] == 'yes') {
+                                $this->_downloader->log(2, $this->getShortName() .
+                                    ': Skipping optional dependency "' .
+                                    $this->_registry->parsedPackageNameToString($dep, true) .
+                                    '", no releases exist');
+                                continue;
+                            } else {
+                                return $url;
+                            }
+                        }
+                    }
+                }
+
+                PEAR::popErrorHandling();
+                if (!isset($options['alldeps'])) {
+                    if (isset($dep['optional']) && $dep['optional'] == 'yes') {
+                        if (!isset($options['soft'])) {
+                            $this->_downloader->log(3, 'Notice: package "' .
+                                $this->getShortName() .
+                                '" optional dependency "' .
+                                $this->_registry->parsedPackageNameToString(
+                                    array('channel' => $chan, 'package' =>
+                                    $dep['name']), true) .
+                                '" will not be automatically downloaded');
+                        }
+                        $skipnames[] = $this->_registry->parsedPackageNameToString(
+                                array('channel' => $chan, 'package' =>
+                                $dep['name']), true);
+                        $nodownload = true;
+                    }
+                }
+
+                if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
+                    if (!isset($dep['optional']) || $dep['optional'] == 'no') {
+                        if (!isset($options['soft'])) {
+                            $this->_downloader->log(3, 'Notice: package "' .
+                                $this->getShortName() .
+                                '" required dependency "' .
+                                $this->_registry->parsedPackageNameToString(
+                                    array('channel' => $chan, 'package' =>
+                                    $dep['name']), true) .
+                                '" will not be automatically downloaded');
+                        }
+                        $skipnames[] = $this->_registry->parsedPackageNameToString(
+                                array('channel' => $chan, 'package' =>
+                                $dep['name']), true);
+                        $nodownload = true;
+                    }
+                }
+
+                // check to see if a dep is already installed
+                // do not try to move this before getDepPackageDownloadURL
+                // we can't determine whether upgrade is necessary until we know what
+                // version would be downloaded
+                if (!isset($options['force']) && $this->isInstalled(
+                        $url, $dep['rel'])) {
+                    $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
+                        'required' :
+                        'optional';
+                    $dep['package'] = $dep['name'];
+                    if (isset($newdep)) {
+                        $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']);
+                    } else {
+                        $version = $this->_installRegistry->packageInfo($dep['name'], 'version');
+                    }
+
+                    $dep['version'] = $url['version'];
+                    if (!isset($options['soft'])) {
+                        $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
+                            ' dependency "' .
+                            $this->_registry->parsedPackageNameToString($dep, true) .
+                            '", already installed as version ' . $version);
+                    }
+
+                    $skip = count($skipnames) ?
+                        $skipnames[count($skipnames) - 1] : '';
+                    if ($skip ==
+                          $this->_registry->parsedPackageNameToString($dep, true)) {
+                        array_pop($skipnames);
+                    }
+
+                    continue;
+                }
+
+                if ($nodownload) {
+                    continue;
+                }
+
+                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                if (isset($newdep)) {
+                    $dep = $newdep;
+                }
+
+                $dep['package'] = $dep['name'];
+                $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
+                    isset($dep['optional']) && $dep['optional'] == 'yes' &&
+                    !isset($options['alldeps']), true);
+                PEAR::popErrorHandling();
+                if (PEAR::isError($ret)) {
+                    if (!isset($options['soft'])) {
+                        $this->_downloader->log(0, $ret->getMessage());
+                    }
+                    continue;
+                }
+
+                $this->_downloadDeps[] = $ret;
+            }
+        }
+
+        if (count($skipnames)) {
+            if (!isset($options['soft'])) {
+                $this->_downloader->log(1, 'Did not download dependencies: ' .
+                    implode(', ', $skipnames) .
+                    ', use --alldeps or --onlyreqdeps to download automatically');
+            }
+        }
+    }
+
+    function setDownloadURL($pkg)
+    {
+        $this->_downloadURL = $pkg;
+    }
+
+    /**
+     * Set the package.xml object for this downloaded package
+     *
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
+     */
+    function setPackageFile(&$pkg)
+    {
+        $this->_packagefile = &$pkg;
+    }
+
+    function getShortName()
+    {
+        return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
+            'package' => $this->getPackage()), true);
+    }
+
+    function getParsedPackage()
+    {
+        if (isset($this->_packagefile) || isset($this->_parsedname)) {
+            return array('channel' => $this->getChannel(),
+                'package' => $this->getPackage(),
+                'version' => $this->getVersion());
+        }
+
+        return false;
+    }
+
+    function getDownloadURL()
+    {
+        return $this->_downloadURL;
+    }
+
+    function canDefault()
+    {
+        if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) {
+            return false;
+        }
+
+        return true;
+    }
+
+    function getPackage()
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile->getPackage();
+        } elseif (isset($this->_downloadURL['info'])) {
+            return $this->_downloadURL['info']->getPackage();
+        }
+
+        return false;
+    }
+
+    /**
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     */
+    function isSubpackage(&$pf)
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile->isSubpackage($pf);
+        } elseif (isset($this->_downloadURL['info'])) {
+            return $this->_downloadURL['info']->isSubpackage($pf);
+        }
+
+        return false;
+    }
+
+    function getPackageType()
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile->getPackageType();
+        } elseif (isset($this->_downloadURL['info'])) {
+            return $this->_downloadURL['info']->getPackageType();
+        }
+
+        return false;
+    }
+
+    function isBundle()
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile->getPackageType() == 'bundle';
+        }
+
+        return false;
+    }
+
+    function getPackageXmlVersion()
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile->getPackagexmlVersion();
+        } elseif (isset($this->_downloadURL['info'])) {
+            return $this->_downloadURL['info']->getPackagexmlVersion();
+        }
+
+        return '1.0';
+    }
+
+    function getChannel()
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile->getChannel();
+        } elseif (isset($this->_downloadURL['info'])) {
+            return $this->_downloadURL['info']->getChannel();
+        }
+
+        return false;
+    }
+
+    function getURI()
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile->getURI();
+        } elseif (isset($this->_downloadURL['info'])) {
+            return $this->_downloadURL['info']->getURI();
+        }
+
+        return false;
+    }
+
+    function getVersion()
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile->getVersion();
+        } elseif (isset($this->_downloadURL['version'])) {
+            return $this->_downloadURL['version'];
+        }
+
+        return false;
+    }
+
+    function isCompatible($pf)
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile->isCompatible($pf);
+        } elseif (isset($this->_downloadURL['info'])) {
+            return $this->_downloadURL['info']->isCompatible($pf);
+        }
+
+        return true;
+    }
+
+    function setGroup($group)
+    {
+        $this->_parsedname['group'] = $group;
+    }
+
+    function getGroup()
+    {
+        if (isset($this->_parsedname['group'])) {
+            return $this->_parsedname['group'];
+        }
+
+        return '';
+    }
+
+    function isExtension($name)
+    {
+        if (isset($this->_packagefile)) {
+            return $this->_packagefile->isExtension($name);
+        } elseif (isset($this->_downloadURL['info'])) {
+            if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
+                return $this->_downloadURL['info']->getProvidesExtension() == $name;
+            }
+
+            return false;
+        }
+
+        return false;
+    }
+
+    function getDeps()
+    {
+        if (isset($this->_packagefile)) {
+            $ver = $this->_packagefile->getPackagexmlVersion();
+            if (version_compare($ver, '2.0', '>=')) {
+                return $this->_packagefile->getDeps(true);
+            }
+
+            return $this->_packagefile->getDeps();
+        } elseif (isset($this->_downloadURL['info'])) {
+            $ver = $this->_downloadURL['info']->getPackagexmlVersion();
+            if (version_compare($ver, '2.0', '>=')) {
+                return $this->_downloadURL['info']->getDeps(true);
+            }
+
+            return $this->_downloadURL['info']->getDeps();
+        }
+
+        return array();
+    }
+
+    /**
+     * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
+     *                     returned from getDepDownloadURL()
+     */
+    function isEqual($param)
+    {
+        if (is_object($param)) {
+            $channel = $param->getChannel();
+            $package = $param->getPackage();
+            if ($param->getURI()) {
+                $param = array(
+                    'channel' => $param->getChannel(),
+                    'package' => $param->getPackage(),
+                    'version' => $param->getVersion(),
+                    'uri' => $param->getURI(),
+                );
+            } else {
+                $param = array(
+                    'channel' => $param->getChannel(),
+                    'package' => $param->getPackage(),
+                    'version' => $param->getVersion(),
+                );
+            }
+        } else {
+            if (isset($param['uri'])) {
+                if ($this->getChannel() != '__uri') {
+                    return false;
+                }
+                return $param['uri'] == $this->getURI();
+            }
+
+            $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage();
+            $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel();
+            if (isset($param['rel'])) {
+                if (!class_exists('PEAR_Dependency2')) {
+                    require_once 'PEAR/Dependency2.php';
+                }
+
+                $newdep = PEAR_Dependency2::normalizeDep($param);
+                $newdep = $newdep[0];
+            } elseif (isset($param['min'])) {
+                $newdep = $param;
+            }
+        }
+
+        if (isset($newdep)) {
+            if (!isset($newdep['min'])) {
+                $newdep['min'] = '0';
+            }
+
+            if (!isset($newdep['max'])) {
+                $newdep['max'] = '100000000000000000000';
+            }
+
+            // use magic to support pecl packages suddenly jumping to the pecl channel
+            // we need to support both dependency possibilities
+            if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
+                if ($package == $this->getPackage()) {
+                    $channel = 'pecl.php.net';
+                }
+            }
+            if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
+                if ($package == $this->getPackage()) {
+                    $channel = 'pear.php.net';
+                }
+            }
+
+            return (strtolower($package) == strtolower($this->getPackage()) &&
+                $channel == $this->getChannel() &&
+                version_compare($newdep['min'], $this->getVersion(), '<=') &&
+                version_compare($newdep['max'], $this->getVersion(), '>='));
+        }
+
+        // use magic to support pecl packages suddenly jumping to the pecl channel
+        if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
+            if (strtolower($package) == strtolower($this->getPackage())) {
+                $channel = 'pear.php.net';
+            }
+        }
+
+        if (isset($param['version'])) {
+            return (strtolower($package) == strtolower($this->getPackage()) &&
+                $channel == $this->getChannel() &&
+                $param['version'] == $this->getVersion());
+        }
+
+        return strtolower($package) == strtolower($this->getPackage()) &&
+            $channel == $this->getChannel();
+    }
+
+    function isInstalled($dep, $oper = '==')
+    {
+        if (!$dep) {
+            return false;
+        }
+
+        if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
+            return false;
+        }
+
+        if (is_object($dep)) {
+            $package = $dep->getPackage();
+            $channel = $dep->getChannel();
+            if ($dep->getURI()) {
+                $dep = array(
+                    'uri' => $dep->getURI(),
+                    'version' => $dep->getVersion(),
+                );
+            } else {
+                $dep = array(
+                    'version' => $dep->getVersion(),
+                );
+            }
+        } else {
+            if (isset($dep['uri'])) {
+                $channel = '__uri';
+                $package = $dep['dep']['name'];
+            } else {
+                $channel = $dep['info']->getChannel();
+                $package = $dep['info']->getPackage();
+            }
+        }
+
+        $options = $this->_downloader->getOptions();
+        $test    = $this->_installRegistry->packageExists($package, $channel);
+        if (!$test && $channel == 'pecl.php.net') {
+            // do magic to allow upgrading from old pecl packages to new ones
+            $test = $this->_installRegistry->packageExists($package, 'pear.php.net');
+            $channel = 'pear.php.net';
+        }
+
+        if ($test) {
+            if (isset($dep['uri'])) {
+                if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
+                    return true;
+                }
+            }
+
+            if (isset($options['upgrade'])) {
+                $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel);
+                if (version_compare($packageVersion, $dep['version'], '>=')) {
+                    return true;
+                }
+
+                return false;
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Detect duplicate package names with differing versions
+     *
+     * If a user requests to install Date 1.4.6 and Date 1.4.7,
+     * for instance, this is a logic error.  This method
+     * detects this situation.
+     *
+     * @param array $params array of PEAR_Downloader_Package objects
+     * @param array $errorparams empty array
+     * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts
+     */
+    function detectStupidDuplicates($params, &$errorparams)
+    {
+        $existing = array();
+        foreach ($params as $i => $param) {
+            $package = $param->getPackage();
+            $channel = $param->getChannel();
+            $group   = $param->getGroup();
+            if (!isset($existing[$channel . '/' . $package])) {
+                $existing[$channel . '/' . $package] = array();
+            }
+
+            if (!isset($existing[$channel . '/' . $package][$group])) {
+                $existing[$channel . '/' . $package][$group] = array();
+            }
+
+            $existing[$channel . '/' . $package][$group][] = $i;
+        }
+
+        $indices = array();
+        foreach ($existing as $package => $groups) {
+            foreach ($groups as $group => $dupes) {
+                if (count($dupes) > 1) {
+                    $indices = $indices + $dupes;
+                }
+            }
+        }
+
+        $indices = array_unique($indices);
+        foreach ($indices as $index) {
+            $errorparams[] = $params[$index];
+        }
+
+        return count($errorparams);
+    }
+
+    /**
+     * @param array
+     * @param bool ignore install groups - for final removal of dupe packages
+     * @static
+     */
+    function removeDuplicates(&$params, $ignoreGroups = false)
+    {
+        $pnames = array();
+        foreach ($params as $i => $param) {
+            if (!$param) {
+                continue;
+            }
+
+            if ($param->getPackage()) {
+                $group = $ignoreGroups ? '' : $param->getGroup();
+                $pnames[$i] = $param->getChannel() . '/' .
+                    $param->getPackage() . '-' . $param->getVersion() . '#' . $group;
+            }
+        }
+
+        $pnames = array_unique($pnames);
+        $unset  = array_diff(array_keys($params), array_keys($pnames));
+        $testp  = array_flip($pnames);
+        foreach ($params as $i => $param) {
+            if (!$param) {
+                $unset[] = $i;
+                continue;
+            }
+
+            if (!is_a($param, 'PEAR_Downloader_Package')) {
+                $unset[] = $i;
+                continue;
+            }
+
+            $group = $ignoreGroups ? '' : $param->getGroup();
+            if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
+                  $param->getVersion() . '#' . $group])) {
+                $unset[] = $i;
+            }
+        }
+
+        foreach ($unset as $i) {
+            unset($params[$i]);
+        }
+
+        $ret = array();
+        foreach ($params as $i => $param) {
+            $ret[] = &$params[$i];
+        }
+
+        $params = array();
+        foreach ($ret as $i => $param) {
+            $params[] = &$ret[$i];
+        }
+    }
+
+    function explicitState()
+    {
+        return $this->_explicitState;
+    }
+
+    function setExplicitState($s)
+    {
+        $this->_explicitState = $s;
+    }
+
+    /**
+     * @static
+     */
+    function mergeDependencies(&$params)
+    {
+        $bundles = $newparams = array();
+        foreach ($params as $i => $param) {
+            if (!$param->isBundle()) {
+                continue;
+            }
+
+            $bundles[] = $i;
+            $pf = &$param->getPackageFile();
+            $newdeps = array();
+            $contents = $pf->getBundledPackages();
+            if (!is_array($contents)) {
+                $contents = array($contents);
+            }
+
+            foreach ($contents as $file) {
+                $filecontents = $pf->getFileContents($file);
+                $dl = &$param->getDownloader();
+                $options = $dl->getOptions();
+                if (PEAR::isError($dir = $dl->getDownloadDir())) {
+                    return $dir;
+                }
+
+                $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb');
+                if (!$fp) {
+                    continue;
+                }
+
+                // FIXME do symlink check
+
+                fwrite($fp, $filecontents, strlen($filecontents));
+                fclose($fp);
+                if ($s = $params[$i]->explicitState()) {
+                    $obj->setExplicitState($s);
+                }
+
+                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
+                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                if (PEAR::isError($dir = $dl->getDownloadDir())) {
+                    PEAR::popErrorHandling();
+                    return $dir;
+                }
+
+                $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file);
+                PEAR::popErrorHandling();
+                if (PEAR::isError($e)) {
+                    if (!isset($options['soft'])) {
+                        $dl->log(0, $e->getMessage());
+                    }
+                    continue;
+                }
+
+                $j = &$obj;
+                if (!PEAR_Downloader_Package::willDownload($j,
+                      array_merge($params, $newparams)) && !$param->isInstalled($j)) {
+                    $newparams[] = &$j;
+                }
+            }
+        }
+
+        foreach ($bundles as $i) {
+            unset($params[$i]); // remove bundles - only their contents matter for installation
+        }
+
+        PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
+        if (count($newparams)) { // add in bundled packages for install
+            foreach ($newparams as $i => $unused) {
+                $params[] = &$newparams[$i];
+            }
+            $newparams = array();
+        }
+
+        foreach ($params as $i => $param) {
+            $newdeps = array();
+            foreach ($param->_downloadDeps as $dep) {
+                $merge = array_merge($params, $newparams);
+                if (!PEAR_Downloader_Package::willDownload($dep, $merge)
+                    && !$param->isInstalled($dep)
+                ) {
+                    $newdeps[] = $dep;
+                } else {
+                    //var_dump($dep);
+                    // detect versioning conflicts here
+                }
+            }
+
+            // convert the dependencies into PEAR_Downloader_Package objects for the next time around
+            $params[$i]->_downloadDeps = array();
+            foreach ($newdeps as $dep) {
+                $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader());
+                if ($s = $params[$i]->explicitState()) {
+                    $obj->setExplicitState($s);
+                }
+
+                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                $e = $obj->fromDepURL($dep);
+                PEAR::popErrorHandling();
+                if (PEAR::isError($e)) {
+                    if (!isset($options['soft'])) {
+                        $obj->_downloader->log(0, $e->getMessage());
+                    }
+                    continue;
+                }
+
+                $e = $obj->detectDependencies($params);
+                if (PEAR::isError($e)) {
+                    if (!isset($options['soft'])) {
+                        $obj->_downloader->log(0, $e->getMessage());
+                    }
+                }
+
+                $j = &$obj;
+                $newparams[] = &$j;
+            }
+        }
+
+        if (count($newparams)) {
+            foreach ($newparams as $i => $unused) {
+                $params[] = &$newparams[$i];
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * @static
+     */
+    function willDownload($param, $params)
+    {
+        if (!is_array($params)) {
+            return false;
+        }
+
+        foreach ($params as $obj) {
+            if ($obj->isEqual($param)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * For simpler unit-testing
+     * @param PEAR_Config
+     * @param int
+     * @param string
+     */
+    function &getPackagefileObject(&$c, $d)
+    {
+        $a = &new PEAR_PackageFile($c, $d);
+        return $a;
+    }
+
+    /**
+     * This will retrieve from a local file if possible, and parse out
+     * a group name as well.  The original parameter will be modified to reflect this.
+     * @param string|array can be a parsed package name as well
+     * @access private
+     */
+    function _fromFile(&$param)
+    {
+        $saveparam = $param;
+        if (is_string($param)) {
+            if (!@file_exists($param)) {
+                $test = explode('#', $param);
+                $group = array_pop($test);
+                if (@file_exists(implode('#', $test))) {
+                    $this->setGroup($group);
+                    $param = implode('#', $test);
+                    $this->_explicitGroup = true;
+                }
+            }
+
+            if (@is_file($param)) {
+                $this->_type = 'local';
+                $options = $this->_downloader->getOptions();
+                $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug);
+                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
+                PEAR::popErrorHandling();
+                if (PEAR::isError($pf)) {
+                    $this->_valid = false;
+                    $param = $saveparam;
+                    return $pf;
+                }
+                $this->_packagefile = &$pf;
+                if (!$this->getGroup()) {
+                    $this->setGroup('default'); // install the default dependency group
+                }
+                return $this->_valid = true;
+            }
+        }
+        $param = $saveparam;
+        return $this->_valid = false;
+    }
+
+    function _fromUrl($param, $saveparam = '')
+    {
+        if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) {
+            $options = $this->_downloader->getOptions();
+            $this->_type = 'url';
+            $callback = $this->_downloader->ui ?
+                array(&$this->_downloader, '_downloadCallback') : null;
+            $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
+            if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
+                $this->_downloader->popErrorHandling();
+                return $dir;
+            }
+
+            $this->_downloader->log(3, 'Downloading "' . $param . '"');
+            $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
+                $dir, $callback, null, false, $this->getChannel());
+            $this->_downloader->popErrorHandling();
+            if (PEAR::isError($file)) {
+                if (!empty($saveparam)) {
+                    $saveparam = ", cannot download \"$saveparam\"";
+                }
+                $err = PEAR::raiseError('Could not download from "' . $param .
+                    '"' . $saveparam . ' (' . $file->getMessage() . ')');
+                    return $err;
+            }
+
+            if ($this->_rawpackagefile) {
+                require_once 'Archive/Tar.php';
+                $tar = &new Archive_Tar($file);
+                $packagexml = $tar->extractInString('package2.xml');
+                if (!$packagexml) {
+                    $packagexml = $tar->extractInString('package.xml');
+                }
+
+                if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
+                      str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
+                    if ($this->getChannel() != 'pear.php.net') {
+                        return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
+                            'not match value returned from xml-rpc');
+                    }
+
+                    // be more lax for the existing PEAR packages that have not-ok
+                    // characters in their package.xml
+                    $this->_downloader->log(0, 'CRITICAL WARNING: The "' .
+                        $this->getPackage() . '" package has invalid characters in its ' .
+                        'package.xml.  The next version of PEAR may not be able to install ' .
+                        'this package for security reasons.  Please open a bug report at ' .
+                        'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
+                }
+            }
+
+            // whew, download worked!
+            $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
+
+            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+            $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
+            PEAR::popErrorHandling();
+            if (PEAR::isError($pf)) {
+                if (is_array($pf->getUserInfo())) {
+                    foreach ($pf->getUserInfo() as $err) {
+                        if (is_array($err)) {
+                            $err = $err['message'];
+                        }
+
+                        if (!isset($options['soft'])) {
+                            $this->_downloader->log(0, "Validation Error: $err");
+                        }
+                    }
+                }
+
+                if (!isset($options['soft'])) {
+                    $this->_downloader->log(0, $pf->getMessage());
+                }
+
+                ///FIXME need to pass back some error code that we can use to match with to cancel all further operations
+                /// At least stop all deps of this package from being installed
+                $out = $saveparam ? $saveparam : $param;
+                $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive');
+                $this->_valid = false;
+                return $err;
+            }
+
+            $this->_packagefile = &$pf;
+            $this->setGroup('default'); // install the default dependency group
+            return $this->_valid = true;
+        }
+
+        return $this->_valid = false;
+    }
+
+    /**
+     *
+     * @param string|array pass in an array of format
+     *                     array(
+     *                      'package' => 'pname',
+     *                     ['channel' => 'channame',]
+     *                     ['version' => 'version',]
+     *                     ['state' => 'state',])
+     *                     or a string of format [channame/]pname[-version|-state]
+     */
+    function _fromString($param)
+    {
+        $options = $this->_downloader->getOptions();
+        $channel = $this->_config->get('default_channel');
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+        $pname = $this->_registry->parsePackageName($param, $channel);
+        PEAR::popErrorHandling();
+        if (PEAR::isError($pname)) {
+            if ($pname->getCode() == 'invalid') {
+                $this->_valid = false;
+                return false;
+            }
+
+            if ($pname->getCode() == 'channel') {
+                $parsed = $pname->getUserInfo();
+                if ($this->_downloader->discover($parsed['channel'])) {
+                    if ($this->_config->get('auto_discover')) {
+                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                        $pname = $this->_registry->parsePackageName($param, $channel);
+                        PEAR::popErrorHandling();
+                    } else {
+                        if (!isset($options['soft'])) {
+                            $this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
+                                '" is not initialized, use ' .
+                                '"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
+                                'or pear config-set auto_discover 1');
+                        }
+                    }
+                }
+
+                if (PEAR::isError($pname)) {
+                    if (!isset($options['soft'])) {
+                        $this->_downloader->log(0, $pname->getMessage());
+                    }
+
+                    if (is_array($param)) {
+                        $param = $this->_registry->parsedPackageNameToString($param);
+                    }
+
+                    $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
+                    $this->_valid = false;
+                    return $err;
+                }
+            } else {
+                if (!isset($options['soft'])) {
+                    $this->_downloader->log(0, $pname->getMessage());
+                }
+
+                $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
+                $this->_valid = false;
+                return $err;
+            }
+        }
+
+        if (!isset($this->_type)) {
+            $this->_type = 'rest';
+        }
+
+        $this->_parsedname    = $pname;
+        $this->_explicitState = isset($pname['state']) ? $pname['state'] : false;
+        $this->_explicitGroup = isset($pname['group']) ? true : false;
+
+        $info = $this->_downloader->_getPackageDownloadUrl($pname);
+        if (PEAR::isError($info)) {
+            if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') {
+                // try pecl
+                $pname['channel'] = 'pecl.php.net';
+                if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
+                    if (!PEAR::isError($test)) {
+                        $info = PEAR::raiseError($info->getMessage() . ' - package ' .
+                            $this->_registry->parsedPackageNameToString($pname, true) .
+                            ' can be installed with "pecl install ' . $pname['package'] .
+                            '"');
+                    } else {
+                        $pname['channel'] = 'pear.php.net';
+                    }
+                } else {
+                    $pname['channel'] = 'pear.php.net';
+                }
+            }
+
+            return $info;
+        }
+
+        $this->_rawpackagefile = $info['raw'];
+        $ret = $this->_analyzeDownloadURL($info, $param, $pname);
+        if (PEAR::isError($ret)) {
+            return $ret;
+        }
+
+        if ($ret) {
+            $this->_downloadURL = $ret;
+            return $this->_valid = (bool) $ret;
+        }
+    }
+
+    /**
+     * @param array output of package.getDownloadURL
+     * @param string|array|object information for detecting packages to be downloaded, and
+     *                            for errors
+     * @param array name information of the package
+     * @param array|null packages to be downloaded
+     * @param bool is this an optional dependency?
+     * @param bool is this any kind of dependency?
+     * @access private
+     */
+    function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false,
+                                 $isdependency = false)
+    {
+        if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
+            return false;
+        }
+
+        if ($info === false) {
+            $saveparam = !is_string($param) ? ", cannot download \"$param\"" : '';
+
+            // no releases exist
+            return PEAR::raiseError('No releases for package "' .
+                $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
+        }
+
+        if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
+            $err = false;
+            if ($pname['channel'] == 'pecl.php.net') {
+                if ($info['info']->getChannel() != 'pear.php.net') {
+                    $err = true;
+                }
+            } elseif ($info['info']->getChannel() == 'pecl.php.net') {
+                if ($pname['channel'] != 'pear.php.net') {
+                    $err = true;
+                }
+            } else {
+                $err = true;
+            }
+
+            if ($err) {
+                return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
+                    '" retrieved another channel\'s name for download! ("' .
+                    $info['info']->getChannel() . '")');
+            }
+        }
+
+        $preferred_state = $this->_config->get('preferred_state');
+        if (!isset($info['url'])) {
+            $package_version = $this->_registry->packageInfo($info['info']->getPackage(),
+            'version', $info['info']->getChannel());
+            if ($this->isInstalled($info)) {
+                if ($isdependency && version_compare($info['version'], $package_version, '<=')) {
+                    // ignore bogus errors of "failed to download dependency"
+                    // if it is already installed and the one that would be
+                    // downloaded is older or the same version (Bug #7219)
+                    return false;
+                }
+            }
+
+            if ($info['version'] === $package_version) {
+                if (!isset($options['soft'])) {
+                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
+                        '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' .
+                        ' (' . $package_version . ') is the same as the locally installed one.');
+                }
+
+                return false;
+            }
+
+            if (version_compare($info['version'], $package_version, '<=')) {
+                if (!isset($options['soft'])) {
+                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
+                        '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' .
+                        ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').');
+                }
+
+                return false;
+            }
+
+            $instead =  ', will instead download version ' . $info['version'] .
+                        ', stability "' . $info['info']->getState() . '"';
+            // releases exist, but we failed to get any
+            if (isset($this->_downloader->_options['force'])) {
+                if (isset($pname['version'])) {
+                    $vs = ', version "' . $pname['version'] . '"';
+                } elseif (isset($pname['state'])) {
+                    $vs = ', stability "' . $pname['state'] . '"';
+                } elseif ($param == 'dependency') {
+                    if (!class_exists('PEAR_Common')) {
+                        require_once 'PEAR/Common.php';
+                    }
+
+                    if (!in_array($info['info']->getState(),
+                          PEAR_Common::betterStates($preferred_state, true))) {
+                        if ($optional) {
+                            // don't spit out confusing error message
+                            return $this->_downloader->_getPackageDownloadUrl(
+                                array('package' => $pname['package'],
+                                      'channel' => $pname['channel'],
+                                      'version' => $info['version']));
+                        }
+                        $vs = ' within preferred state "' . $preferred_state .
+                            '"';
+                    } else {
+                        if (!class_exists('PEAR_Dependency2')) {
+                            require_once 'PEAR/Dependency2.php';
+                        }
+
+                        if ($optional) {
+                            // don't spit out confusing error message
+                            return $this->_downloader->_getPackageDownloadUrl(
+                                array('package' => $pname['package'],
+                                      'channel' => $pname['channel'],
+                                      'version' => $info['version']));
+                        }
+                        $vs = PEAR_Dependency2::_getExtraString($pname);
+                        $instead = '';
+                    }
+                } else {
+                    $vs = ' within preferred state "' . $preferred_state . '"';
+                }
+
+                if (!isset($options['soft'])) {
+                    $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
+                        '/' . $pname['package'] . $vs . $instead);
+                }
+
+                // download the latest release
+                return $this->_downloader->_getPackageDownloadUrl(
+                    array('package' => $pname['package'],
+                          'channel' => $pname['channel'],
+                          'version' => $info['version']));
+            } else {
+                if (isset($info['php']) && $info['php']) {
+                    $err = PEAR::raiseError('Failed to download ' .
+                        $this->_registry->parsedPackageNameToString(
+                            array('channel' => $pname['channel'],
+                                  'package' => $pname['package']),
+                                true) .
+                        ', latest release is version ' . $info['php']['v'] .
+                        ', but it requires PHP version "' .
+                        $info['php']['m'] . '", use "' .
+                        $this->_registry->parsedPackageNameToString(
+                            array('channel' => $pname['channel'], 'package' => $pname['package'],
+                            'version' => $info['php']['v'])) . '" to install',
+                            PEAR_DOWNLOADER_PACKAGE_PHPVERSION);
+                    return $err;
+                }
+
+                // construct helpful error message
+                if (isset($pname['version'])) {
+                    $vs = ', version "' . $pname['version'] . '"';
+                } elseif (isset($pname['state'])) {
+                    $vs = ', stability "' . $pname['state'] . '"';
+                } elseif ($param == 'dependency') {
+                    if (!class_exists('PEAR_Common')) {
+                        require_once 'PEAR/Common.php';
+                    }
+
+                    if (!in_array($info['info']->getState(),
+                          PEAR_Common::betterStates($preferred_state, true))) {
+                        if ($optional) {
+                            // don't spit out confusing error message, and don't die on
+                            // optional dep failure!
+                            return $this->_downloader->_getPackageDownloadUrl(
+                                array('package' => $pname['package'],
+                                      'channel' => $pname['channel'],
+                                      'version' => $info['version']));
+                        }
+                        $vs = ' within preferred state "' . $preferred_state . '"';
+                    } else {
+                        if (!class_exists('PEAR_Dependency2')) {
+                            require_once 'PEAR/Dependency2.php';
+                        }
+
+                        if ($optional) {
+                            // don't spit out confusing error message, and don't die on
+                            // optional dep failure!
+                            return $this->_downloader->_getPackageDownloadUrl(
+                                array('package' => $pname['package'],
+                                      'channel' => $pname['channel'],
+                                      'version' => $info['version']));
+                        }
+                        $vs = PEAR_Dependency2::_getExtraString($pname);
+                    }
+                } else {
+                    $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"';
+                }
+
+                $options = $this->_downloader->getOptions();
+                // this is only set by the "download-all" command
+                if (isset($options['ignorepreferred_state'])) {
+                    $err = PEAR::raiseError(
+                        'Failed to download ' . $this->_registry->parsedPackageNameToString(
+                            array('channel' => $pname['channel'], 'package' => $pname['package']),
+                                true)
+                         . $vs .
+                        ', latest release is version ' . $info['version'] .
+                        ', stability "' . $info['info']->getState() . '", use "' .
+                        $this->_registry->parsedPackageNameToString(
+                            array('channel' => $pname['channel'], 'package' => $pname['package'],
+                            'version' => $info['version'])) . '" to install',
+                            PEAR_DOWNLOADER_PACKAGE_STATE);
+                    return $err;
+                }
+
+                // Checks if the user has a package installed already and checks the release against
+                // the state against the installed package, this allows upgrades for packages
+                // with lower stability than the preferred_state
+                $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']);
+                if (!$this->isInstalled($info)
+                    || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true))
+                ) {
+                    $err = PEAR::raiseError(
+                        'Failed to download ' . $this->_registry->parsedPackageNameToString(
+                            array('channel' => $pname['channel'], 'package' => $pname['package']),
+                                true)
+                         . $vs .
+                        ', latest release is version ' . $info['version'] .
+                        ', stability "' . $info['info']->getState() . '", use "' .
+                        $this->_registry->parsedPackageNameToString(
+                            array('channel' => $pname['channel'], 'package' => $pname['package'],
+                            'version' => $info['version'])) . '" to install');
+                    return $err;
+                }
+            }
+        }
+
+        if (isset($info['deprecated']) && $info['deprecated']) {
+            $this->_downloader->log(0,
+                'WARNING: "' .
+                    $this->_registry->parsedPackageNameToString(
+                            array('channel' => $info['info']->getChannel(),
+                                  'package' => $info['info']->getPackage()), true) .
+                '" is deprecated in favor of "' .
+                    $this->_registry->parsedPackageNameToString($info['deprecated'], true) .
+                '"');
+        }
+
+        return $info;
+    }
+}
Index: trunk/library/PEAR/PEAR/Registry.php
===================================================================
--- trunk/library/PEAR/PEAR/Registry.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Registry.php	(revision 5146)
@@ -0,0 +1,2395 @@
+<?php
+/**
+ * PEAR_Registry
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Registry.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * for PEAR_Error
+ */
+require_once 'PEAR.php';
+require_once 'PEAR/DependencyDB.php';
+
+define('PEAR_REGISTRY_ERROR_LOCK',         -2);
+define('PEAR_REGISTRY_ERROR_FORMAT',       -3);
+define('PEAR_REGISTRY_ERROR_FILE',         -4);
+define('PEAR_REGISTRY_ERROR_CONFLICT',     -5);
+define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6);
+
+/**
+ * Administration class used to maintain the installed package database.
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Registry extends PEAR
+{
+    /**
+     * File containing all channel information.
+     * @var string
+     */
+    var $channels = '';
+
+    /** Directory where registry files are stored.
+     * @var string
+     */
+    var $statedir = '';
+
+    /** File where the file map is stored
+     * @var string
+     */
+    var $filemap = '';
+
+    /** Directory where registry files for channels are stored.
+     * @var string
+     */
+    var $channelsdir = '';
+
+    /** Name of file used for locking the registry
+     * @var string
+     */
+    var $lockfile = '';
+
+    /** File descriptor used during locking
+     * @var resource
+     */
+    var $lock_fp = null;
+
+    /** Mode used during locking
+     * @var int
+     */
+    var $lock_mode = 0; // XXX UNUSED
+
+    /** Cache of package information.  Structure:
+     * array(
+     *   'package' => array('id' => ... ),
+     *   ... )
+     * @var array
+     */
+    var $pkginfo_cache = array();
+
+    /** Cache of file map.  Structure:
+     * array( '/path/to/file' => 'package', ... )
+     * @var array
+     */
+    var $filemap_cache = array();
+
+    /**
+     * @var false|PEAR_ChannelFile
+     */
+    var $_pearChannel;
+
+    /**
+     * @var false|PEAR_ChannelFile
+     */
+    var $_peclChannel;
+
+    /**
+     * @var false|PEAR_ChannelFile
+     */
+    var $_docChannel;
+
+    /**
+     * @var PEAR_DependencyDB
+     */
+    var $_dependencyDB;
+
+    /**
+     * @var PEAR_Config
+     */
+    var $_config;
+
+    /**
+     * PEAR_Registry constructor.
+     *
+     * @param string (optional) PEAR install directory (for .php files)
+     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if
+     *        default values are not desired.  Only used the very first time a PEAR
+     *        repository is initialized
+     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if
+     *        default values are not desired.  Only used the very first time a PEAR
+     *        repository is initialized
+     *
+     * @access public
+     */
+    function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false,
+                           $pecl_channel = false)
+    {
+        parent::PEAR();
+        $this->setInstallDir($pear_install_dir);
+        $this->_pearChannel = $pear_channel;
+        $this->_peclChannel = $pecl_channel;
+        $this->_config      = false;
+    }
+
+    function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR)
+    {
+        $ds = DIRECTORY_SEPARATOR;
+        $this->install_dir = $pear_install_dir;
+        $this->channelsdir = $pear_install_dir.$ds.'.channels';
+        $this->statedir    = $pear_install_dir.$ds.'.registry';
+        $this->filemap     = $pear_install_dir.$ds.'.filemap';
+        $this->lockfile    = $pear_install_dir.$ds.'.lock';
+    }
+
+    function hasWriteAccess()
+    {
+        if (!file_exists($this->install_dir)) {
+            $dir = $this->install_dir;
+            while ($dir && $dir != '.') {
+                $olddir = $dir;
+                $dir    = dirname($dir);
+                if ($dir != '.' && file_exists($dir)) {
+                    if (is_writeable($dir)) {
+                        return true;
+                    }
+
+                    return false;
+                }
+
+                if ($dir == $olddir) { // this can happen in safe mode
+                    return @is_writable($dir);
+                }
+            }
+
+            return false;
+        }
+
+        return is_writeable($this->install_dir);
+    }
+
+    function setConfig(&$config, $resetInstallDir = true)
+    {
+        $this->_config = &$config;
+        if ($resetInstallDir) {
+            $this->setInstallDir($config->get('php_dir'));
+        }
+    }
+
+    function _initializeChannelDirs()
+    {
+        static $running = false;
+        if (!$running) {
+            $running = true;
+            $ds = DIRECTORY_SEPARATOR;
+            if (!is_dir($this->channelsdir) ||
+                  !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
+                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
+                  !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
+                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
+                if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
+                    $pear_channel = $this->_pearChannel;
+                    if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) {
+                        if (!class_exists('PEAR_ChannelFile')) {
+                            require_once 'PEAR/ChannelFile.php';
+                        }
+
+                        $pear_channel = new PEAR_ChannelFile;
+                        $pear_channel->setAlias('pear');
+                        $pear_channel->setServer('pear.php.net');
+                        $pear_channel->setSummary('PHP Extension and Application Repository');
+                        $pear_channel->setDefaultPEARProtocols();
+                        $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
+                        $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
+                        $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
+                        //$pear_channel->setBaseURL('REST1.4', 'http://pear.php.net/rest/');
+                    } else {
+                        $pear_channel->setServer('pear.php.net');
+                        $pear_channel->setAlias('pear');
+                    }
+
+                    $pear_channel->validate();
+                    $this->_addChannel($pear_channel);
+                }
+
+                if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) {
+                    $pecl_channel = $this->_peclChannel;
+                    if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) {
+                        if (!class_exists('PEAR_ChannelFile')) {
+                            require_once 'PEAR/ChannelFile.php';
+                        }
+
+                        $pecl_channel = new PEAR_ChannelFile;
+                        $pecl_channel->setAlias('pecl');
+                        $pecl_channel->setServer('pecl.php.net');
+                        $pecl_channel->setSummary('PHP Extension Community Library');
+                        $pecl_channel->setDefaultPEARProtocols();
+                        $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
+                        $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
+                        $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
+                    } else {
+                        $pecl_channel->setServer('pecl.php.net');
+                        $pecl_channel->setAlias('pecl');
+                    }
+
+                    $pecl_channel->validate();
+                    $this->_addChannel($pecl_channel);
+                }
+
+                if (!file_exists($this->channelsdir . $ds . 'doc.php.net.reg')) {
+                    $doc_channel = $this->_docChannel;
+                    if (!is_a($doc_channel, 'PEAR_ChannelFile') || !$doc_channel->validate()) {
+                        if (!class_exists('PEAR_ChannelFile')) {
+                            require_once 'PEAR/ChannelFile.php';
+                        }
+
+                        $doc_channel = new PEAR_ChannelFile;
+                        $doc_channel->setAlias('phpdocs');
+                        $doc_channel->setServer('doc.php.net');
+                        $doc_channel->setSummary('PHP Documentation Team');
+                        $doc_channel->setDefaultPEARProtocols();
+                        $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
+                        $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
+                        $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
+                    } else {
+                        $doc_channel->setServer('doc.php.net');
+                        $doc_channel->setAlias('doc');
+                    }
+
+                    $doc_channel->validate();
+                    $this->_addChannel($doc_channel);
+                }
+
+                if (!file_exists($this->channelsdir . $ds . '__uri.reg')) {
+                    if (!class_exists('PEAR_ChannelFile')) {
+                        require_once 'PEAR/ChannelFile.php';
+                    }
+
+                    $private = new PEAR_ChannelFile;
+                    $private->setName('__uri');
+                    $private->setDefaultPEARProtocols();
+                    $private->setBaseURL('REST1.0', '****');
+                    $private->setSummary('Pseudo-channel for static packages');
+                    $this->_addChannel($private);
+                }
+                $this->_rebuildFileMap();
+            }
+
+            $running = false;
+        }
+    }
+
+    function _initializeDirs()
+    {
+        $ds = DIRECTORY_SEPARATOR;
+        // XXX Compatibility code should be removed in the future
+        // rename all registry files if any to lowercase
+        if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) &&
+              $handle = opendir($this->statedir)) {
+            $dest = $this->statedir . $ds;
+            while (false !== ($file = readdir($handle))) {
+                if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) {
+                    rename($dest . $file, $dest . strtolower($file));
+                }
+            }
+            closedir($handle);
+        }
+
+        $this->_initializeChannelDirs();
+        if (!file_exists($this->filemap)) {
+            $this->_rebuildFileMap();
+        }
+        $this->_initializeDepDB();
+    }
+
+    function _initializeDepDB()
+    {
+        if (!isset($this->_dependencyDB)) {
+            static $initializing = false;
+            if (!$initializing) {
+                $initializing = true;
+                if (!$this->_config) { // never used?
+                    $file = OS_WINDOWS ? 'pear.ini' : '.pearrc';
+                    $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR .
+                        $file);
+                    $this->_config->setRegistry($this);
+                    $this->_config->set('php_dir', $this->install_dir);
+                }
+
+                $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
+                if (PEAR::isError($this->_dependencyDB)) {
+                    // attempt to recover by removing the dep db
+                    if (file_exists($this->_config->get('php_dir', null, 'pear.php.net') .
+                        DIRECTORY_SEPARATOR . '.depdb')) {
+                        @unlink($this->_config->get('php_dir', null, 'pear.php.net') .
+                            DIRECTORY_SEPARATOR . '.depdb');
+                    }
+
+                    $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
+                    if (PEAR::isError($this->_dependencyDB)) {
+                        echo $this->_dependencyDB->getMessage();
+                        echo 'Unrecoverable error';
+                        exit(1);
+                    }
+                }
+
+                $initializing = false;
+            }
+        }
+    }
+
+    /**
+     * PEAR_Registry destructor.  Makes sure no locks are forgotten.
+     *
+     * @access private
+     */
+    function _PEAR_Registry()
+    {
+        parent::_PEAR();
+        if (is_resource($this->lock_fp)) {
+            $this->_unlock();
+        }
+    }
+
+    /**
+     * Make sure the directory where we keep registry files exists.
+     *
+     * @return bool TRUE if directory exists, FALSE if it could not be
+     * created
+     *
+     * @access private
+     */
+    function _assertStateDir($channel = false)
+    {
+        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
+            return $this->_assertChannelStateDir($channel);
+        }
+
+        static $init = false;
+        if (!file_exists($this->statedir)) {
+            if (!$this->hasWriteAccess()) {
+                return false;
+            }
+
+            require_once 'System.php';
+            if (!System::mkdir(array('-p', $this->statedir))) {
+                return $this->raiseError("could not create directory '{$this->statedir}'");
+            }
+            $init = true;
+        } elseif (!is_dir($this->statedir)) {
+            return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' .
+                'it already exists and is not a directory');
+        }
+
+        $ds = DIRECTORY_SEPARATOR;
+        if (!file_exists($this->channelsdir)) {
+            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
+                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
+                  !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
+                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
+                $init = true;
+            }
+        } elseif (!is_dir($this->channelsdir)) {
+            return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' .
+                'it already exists and is not a directory');
+        }
+
+        if ($init) {
+            static $running = false;
+            if (!$running) {
+                $running = true;
+                $this->_initializeDirs();
+                $running = false;
+                $init = false;
+            }
+        } else {
+            $this->_initializeDepDB();
+        }
+
+        return true;
+    }
+
+    /**
+     * Make sure the directory where we keep registry files exists for a non-standard channel.
+     *
+     * @param string channel name
+     * @return bool TRUE if directory exists, FALSE if it could not be
+     * created
+     *
+     * @access private
+     */
+    function _assertChannelStateDir($channel)
+    {
+        $ds = DIRECTORY_SEPARATOR;
+        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
+            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
+                $this->_initializeChannelDirs();
+            }
+            return $this->_assertStateDir($channel);
+        }
+
+        $channelDir = $this->_channelDirectoryName($channel);
+        if (!is_dir($this->channelsdir) ||
+              !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
+            $this->_initializeChannelDirs();
+        }
+
+        if (!file_exists($channelDir)) {
+            if (!$this->hasWriteAccess()) {
+                return false;
+            }
+
+            require_once 'System.php';
+            if (!System::mkdir(array('-p', $channelDir))) {
+                return $this->raiseError("could not create directory '" . $channelDir .
+                    "'");
+            }
+        } elseif (!is_dir($channelDir)) {
+            return $this->raiseError("could not create directory '" . $channelDir .
+                "', already exists and is not a directory");
+        }
+
+        return true;
+    }
+
+    /**
+     * Make sure the directory where we keep registry files for channels exists
+     *
+     * @return bool TRUE if directory exists, FALSE if it could not be
+     * created
+     *
+     * @access private
+     */
+    function _assertChannelDir()
+    {
+        if (!file_exists($this->channelsdir)) {
+            if (!$this->hasWriteAccess()) {
+                return false;
+            }
+
+            require_once 'System.php';
+            if (!System::mkdir(array('-p', $this->channelsdir))) {
+                return $this->raiseError("could not create directory '{$this->channelsdir}'");
+            }
+        } elseif (!is_dir($this->channelsdir)) {
+            return $this->raiseError("could not create directory '{$this->channelsdir}" .
+                "', it already exists and is not a directory");
+        }
+
+        if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
+            if (!$this->hasWriteAccess()) {
+                return false;
+            }
+
+            require_once 'System.php';
+            if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) {
+                return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'");
+            }
+        } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
+            return $this->raiseError("could not create directory '{$this->channelsdir}" .
+                "/.alias', it already exists and is not a directory");
+        }
+
+        return true;
+    }
+
+    /**
+     * Get the name of the file where data for a given package is stored.
+     *
+     * @param string channel name, or false if this is a PEAR package
+     * @param string package name
+     *
+     * @return string registry file name
+     *
+     * @access public
+     */
+    function _packageFileName($package, $channel = false)
+    {
+        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
+            return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR .
+                strtolower($package) . '.reg';
+        }
+
+        return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
+    }
+
+    /**
+     * Get the name of the file where data for a given channel is stored.
+     * @param string channel name
+     * @return string registry file name
+     */
+    function _channelFileName($channel, $noaliases = false)
+    {
+        if (!$noaliases) {
+            if (file_exists($this->_getChannelAliasFileName($channel))) {
+                $channel = implode('', file($this->_getChannelAliasFileName($channel)));
+            }
+        }
+        return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_',
+            strtolower($channel)) . '.reg';
+    }
+
+    /**
+     * @param string
+     * @return string
+     */
+    function _getChannelAliasFileName($alias)
+    {
+        return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' .
+              DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt';
+    }
+
+    /**
+     * Get the name of a channel from its alias
+     */
+    function _getChannelFromAlias($channel)
+    {
+        if (!$this->_channelExists($channel)) {
+            if ($channel == 'pear.php.net') {
+                return 'pear.php.net';
+            }
+
+            if ($channel == 'pecl.php.net') {
+                return 'pecl.php.net';
+            }
+
+            if ($channel == 'doc.php.net') {
+                return 'doc.php.net';
+            }
+
+            if ($channel == '__uri') {
+                return '__uri';
+            }
+
+            return false;
+        }
+
+        $channel = strtolower($channel);
+        if (file_exists($this->_getChannelAliasFileName($channel))) {
+            // translate an alias to an actual channel
+            return implode('', file($this->_getChannelAliasFileName($channel)));
+        }
+
+        return $channel;
+    }
+
+    /**
+     * Get the alias of a channel from its alias or its name
+     */
+    function _getAlias($channel)
+    {
+        if (!$this->_channelExists($channel)) {
+            if ($channel == 'pear.php.net') {
+                return 'pear';
+            }
+
+            if ($channel == 'pecl.php.net') {
+                return 'pecl';
+            }
+
+            if ($channel == 'doc.php.net') {
+                return 'phpdocs';
+            }
+
+            return false;
+        }
+
+        $channel = $this->_getChannel($channel);
+        if (PEAR::isError($channel)) {
+            return $channel;
+        }
+
+        return $channel->getAlias();
+    }
+
+    /**
+     * Get the name of the file where data for a given package is stored.
+     *
+     * @param string channel name, or false if this is a PEAR package
+     * @param string package name
+     *
+     * @return string registry file name
+     *
+     * @access public
+     */
+    function _channelDirectoryName($channel)
+    {
+        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
+            return $this->statedir;
+        }
+
+        $ch = $this->_getChannelFromAlias($channel);
+        if (!$ch) {
+            $ch = $channel;
+        }
+
+        return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' .
+            str_replace('/', '_', $ch));
+    }
+
+    function _openPackageFile($package, $mode, $channel = false)
+    {
+        if (!$this->_assertStateDir($channel)) {
+            return null;
+        }
+
+        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
+            return null;
+        }
+
+        $file = $this->_packageFileName($package, $channel);
+        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
+            return null;
+        }
+
+        $fp = @fopen($file, $mode);
+        if (!$fp) {
+            return null;
+        }
+
+        return $fp;
+    }
+
+    function _closePackageFile($fp)
+    {
+        fclose($fp);
+    }
+
+    function _openChannelFile($channel, $mode)
+    {
+        if (!$this->_assertChannelDir()) {
+            return null;
+        }
+
+        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
+            return null;
+        }
+
+        $file = $this->_channelFileName($channel);
+        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
+            return null;
+        }
+
+        $fp = @fopen($file, $mode);
+        if (!$fp) {
+            return null;
+        }
+
+        return $fp;
+    }
+
+    function _closeChannelFile($fp)
+    {
+        fclose($fp);
+    }
+
+    function _rebuildFileMap()
+    {
+        if (!class_exists('PEAR_Installer_Role')) {
+            require_once 'PEAR/Installer/Role.php';
+        }
+
+        $channels = $this->_listAllPackages();
+        $files = array();
+        foreach ($channels as $channel => $packages) {
+            foreach ($packages as $package) {
+                $version = $this->_packageInfo($package, 'version', $channel);
+                $filelist = $this->_packageInfo($package, 'filelist', $channel);
+                if (!is_array($filelist)) {
+                    continue;
+                }
+
+                foreach ($filelist as $name => $attrs) {
+                    if (isset($attrs['attribs'])) {
+                        $attrs = $attrs['attribs'];
+                    }
+
+                    // it is possible for conflicting packages in different channels to
+                    // conflict with data files/doc files
+                    if ($name == 'dirtree') {
+                        continue;
+                    }
+
+                    if (isset($attrs['role']) && !in_array($attrs['role'],
+                          PEAR_Installer_Role::getInstallableRoles())) {
+                        // these are not installed
+                        continue;
+                    }
+
+                    if (isset($attrs['role']) && !in_array($attrs['role'],
+                          PEAR_Installer_Role::getBaseinstallRoles())) {
+                        $attrs['baseinstalldir'] = $package;
+                    }
+
+                    if (isset($attrs['baseinstalldir'])) {
+                        $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
+                    } else {
+                        $file = $name;
+                    }
+
+                    $file = preg_replace(',^/+,', '', $file);
+                    if ($channel != 'pear.php.net') {
+                        if (!isset($files[$attrs['role']])) {
+                            $files[$attrs['role']] = array();
+                        }
+                        $files[$attrs['role']][$file] = array(strtolower($channel),
+                            strtolower($package));
+                    } else {
+                        if (!isset($files[$attrs['role']])) {
+                            $files[$attrs['role']] = array();
+                        }
+                        $files[$attrs['role']][$file] = strtolower($package);
+                    }
+                }
+            }
+        }
+
+
+        $this->_assertStateDir();
+        if (!$this->hasWriteAccess()) {
+            return false;
+        }
+
+        $fp = @fopen($this->filemap, 'wb');
+        if (!$fp) {
+            return false;
+        }
+
+        $this->filemap_cache = $files;
+        fwrite($fp, serialize($files));
+        fclose($fp);
+        return true;
+    }
+
+    function _readFileMap()
+    {
+        if (!file_exists($this->filemap)) {
+            return array();
+        }
+
+        $fp = @fopen($this->filemap, 'r');
+        if (!$fp) {
+            return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg);
+        }
+
+        clearstatcache();
+        $rt = get_magic_quotes_runtime();
+        set_magic_quotes_runtime(0);
+        $fsize = filesize($this->filemap);
+        fclose($fp);
+        $data = file_get_contents($this->filemap);
+        set_magic_quotes_runtime($rt);
+        $tmp = unserialize($data);
+        if (!$tmp && $fsize > 7) {
+            return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
+        }
+
+        $this->filemap_cache = $tmp;
+        return true;
+    }
+
+    /**
+     * Lock the registry.
+     *
+     * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
+     *                See flock manual for more information.
+     *
+     * @return bool TRUE on success, FALSE if locking failed, or a
+     *              PEAR error if some other error occurs (such as the
+     *              lock file not being writable).
+     *
+     * @access private
+     */
+    function _lock($mode = LOCK_EX)
+    {
+        if (stristr(php_uname(), 'Windows 9')) {
+            return true;
+        }
+
+        if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
+            // XXX does not check type of lock (LOCK_SH/LOCK_EX)
+            return true;
+        }
+
+        if (!$this->_assertStateDir()) {
+            if ($mode == LOCK_EX) {
+                return $this->raiseError('Registry directory is not writeable by the current user');
+            }
+
+            return true;
+        }
+
+        $open_mode = 'w';
+        // XXX People reported problems with LOCK_SH and 'w'
+        if ($mode === LOCK_SH || $mode === LOCK_UN) {
+            if (!file_exists($this->lockfile)) {
+                touch($this->lockfile);
+            }
+            $open_mode = 'r';
+        }
+
+        if (!is_resource($this->lock_fp)) {
+            $this->lock_fp = @fopen($this->lockfile, $open_mode);
+        }
+
+        if (!is_resource($this->lock_fp)) {
+            $this->lock_fp = null;
+            return $this->raiseError("could not create lock file" .
+                                     (isset($php_errormsg) ? ": " . $php_errormsg : ""));
+        }
+
+        if (!(int)flock($this->lock_fp, $mode)) {
+            switch ($mode) {
+                case LOCK_SH: $str = 'shared';    break;
+                case LOCK_EX: $str = 'exclusive'; break;
+                case LOCK_UN: $str = 'unlock';    break;
+                default:      $str = 'unknown';   break;
+            }
+
+            //is resource at this point, close it on error.
+            fclose($this->lock_fp);
+            $this->lock_fp = null;
+            return $this->raiseError("could not acquire $str lock ($this->lockfile)",
+                                     PEAR_REGISTRY_ERROR_LOCK);
+        }
+
+        return true;
+    }
+
+    function _unlock()
+    {
+        $ret = $this->_lock(LOCK_UN);
+        if (is_resource($this->lock_fp)) {
+            fclose($this->lock_fp);
+        }
+
+        $this->lock_fp = null;
+        return $ret;
+    }
+
+    function _packageExists($package, $channel = false)
+    {
+        return file_exists($this->_packageFileName($package, $channel));
+    }
+
+    /**
+     * Determine whether a channel exists in the registry
+     *
+     * @param string Channel name
+     * @param bool if true, then aliases will be ignored
+     * @return boolean
+     */
+    function _channelExists($channel, $noaliases = false)
+    {
+        $a = file_exists($this->_channelFileName($channel, $noaliases));
+        if (!$a && $channel == 'pear.php.net') {
+            return true;
+        }
+
+        if (!$a && $channel == 'pecl.php.net') {
+            return true;
+        }
+
+        if (!$a && $channel == 'doc.php.net') {
+            return true;
+        }
+
+        return $a;
+    }
+
+    /**
+     * Determine whether a mirror exists within the deafult channel in the registry
+     *
+     * @param string Channel name
+     * @param string Mirror name
+     *
+     * @return boolean
+     */
+    function _mirrorExists($channel, $mirror)
+    {
+        $data = $this->_channelInfo($channel);
+        if (!isset($data['servers']['mirror'])) {
+            return false;
+        }
+
+        foreach ($data['servers']['mirror'] as $m) {
+            if ($m['attribs']['host'] == $mirror) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @param PEAR_ChannelFile Channel object
+     * @param donotuse
+     * @param string Last-Modified HTTP tag from remote request
+     * @return boolean|PEAR_Error True on creation, false if it already exists
+     */
+    function _addChannel($channel, $update = false, $lastmodified = false)
+    {
+        if (!is_a($channel, 'PEAR_ChannelFile')) {
+            return false;
+        }
+
+        if (!$channel->validate()) {
+            return false;
+        }
+
+        if (file_exists($this->_channelFileName($channel->getName()))) {
+            if (!$update) {
+                return false;
+            }
+
+            $checker = $this->_getChannel($channel->getName());
+            if (PEAR::isError($checker)) {
+                return $checker;
+            }
+
+            if ($channel->getAlias() != $checker->getAlias()) {
+                if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) {
+                    @unlink($this->_getChannelAliasFileName($checker->getAlias()));
+                }
+            }
+        } else {
+            if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net', 'doc.php.net'))) {
+                return false;
+            }
+        }
+
+        $ret = $this->_assertChannelDir();
+        if (PEAR::isError($ret)) {
+            return $ret;
+        }
+
+        $ret = $this->_assertChannelStateDir($channel->getName());
+        if (PEAR::isError($ret)) {
+            return $ret;
+        }
+
+        if ($channel->getAlias() != $channel->getName()) {
+            if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) &&
+                  $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) {
+                $channel->setAlias($channel->getName());
+            }
+
+            if (!$this->hasWriteAccess()) {
+                return false;
+            }
+
+            $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w');
+            if (!$fp) {
+                return false;
+            }
+
+            fwrite($fp, $channel->getName());
+            fclose($fp);
+        }
+
+        if (!$this->hasWriteAccess()) {
+            return false;
+        }
+
+        $fp = @fopen($this->_channelFileName($channel->getName()), 'wb');
+        if (!$fp) {
+            return false;
+        }
+
+        $info = $channel->toArray();
+        if ($lastmodified) {
+            $info['_lastmodified'] = $lastmodified;
+        } else {
+            $info['_lastmodified'] = date('r');
+        }
+
+        fwrite($fp, serialize($info));
+        fclose($fp);
+        return true;
+    }
+
+    /**
+     * Deletion fails if there are any packages installed from the channel
+     * @param string|PEAR_ChannelFile channel name
+     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
+     */
+    function _deleteChannel($channel)
+    {
+        if (!is_string($channel)) {
+            if (!is_a($channel, 'PEAR_ChannelFile')) {
+                return false;
+            }
+
+            if (!$channel->validate()) {
+                return false;
+            }
+            $channel = $channel->getName();
+        }
+
+        if ($this->_getChannelFromAlias($channel) == '__uri') {
+            return false;
+        }
+
+        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
+            return false;
+        }
+
+        if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
+            return false;
+        }
+
+        if (!$this->_channelExists($channel)) {
+            return false;
+        }
+
+        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
+            return false;
+        }
+
+        $channel = $this->_getChannelFromAlias($channel);
+        if ($channel == 'pear.php.net') {
+            return false;
+        }
+
+        $test = $this->_listChannelPackages($channel);
+        if (count($test)) {
+            return false;
+        }
+
+        $test = @rmdir($this->_channelDirectoryName($channel));
+        if (!$test) {
+            return false;
+        }
+
+        $file = $this->_getChannelAliasFileName($this->_getAlias($channel));
+        if (file_exists($file)) {
+            $test = @unlink($file);
+            if (!$test) {
+                return false;
+            }
+        }
+
+        $file = $this->_channelFileName($channel);
+        $ret = true;
+        if (file_exists($file)) {
+            $ret = @unlink($file);
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Determine whether a channel exists in the registry
+     * @param string Channel Alias
+     * @return boolean
+     */
+    function _isChannelAlias($alias)
+    {
+        return file_exists($this->_getChannelAliasFileName($alias));
+    }
+
+    /**
+     * @param string|null
+     * @param string|null
+     * @param string|null
+     * @return array|null
+     * @access private
+     */
+    function _packageInfo($package = null, $key = null, $channel = 'pear.php.net')
+    {
+        if ($package === null) {
+            if ($channel === null) {
+                $channels = $this->_listChannels();
+                $ret = array();
+                foreach ($channels as $channel) {
+                    $channel = strtolower($channel);
+                    $ret[$channel] = array();
+                    $packages = $this->_listPackages($channel);
+                    foreach ($packages as $package) {
+                        $ret[$channel][] = $this->_packageInfo($package, null, $channel);
+                    }
+                }
+
+                return $ret;
+            }
+
+            $ps = $this->_listPackages($channel);
+            if (!count($ps)) {
+                return array();
+            }
+            return array_map(array(&$this, '_packageInfo'),
+                             $ps, array_fill(0, count($ps), null),
+                             array_fill(0, count($ps), $channel));
+        }
+
+        $fp = $this->_openPackageFile($package, 'r', $channel);
+        if ($fp === null) {
+            return null;
+        }
+
+        $rt = get_magic_quotes_runtime();
+        set_magic_quotes_runtime(0);
+        clearstatcache();
+        $this->_closePackageFile($fp);
+        $data = file_get_contents($this->_packageFileName($package, $channel));
+        set_magic_quotes_runtime($rt);
+        $data = unserialize($data);
+        if ($key === null) {
+            return $data;
+        }
+
+        // compatibility for package.xml version 2.0
+        if (isset($data['old'][$key])) {
+            return $data['old'][$key];
+        }
+
+        if (isset($data[$key])) {
+            return $data[$key];
+        }
+
+        return null;
+    }
+
+    /**
+     * @param string Channel name
+     * @param bool whether to strictly retrieve info of channels, not just aliases
+     * @return array|null
+     */
+    function _channelInfo($channel, $noaliases = false)
+    {
+        if (!$this->_channelExists($channel, $noaliases)) {
+            return null;
+        }
+
+        $fp = $this->_openChannelFile($channel, 'r');
+        if ($fp === null) {
+            return null;
+        }
+
+        $rt = get_magic_quotes_runtime();
+        set_magic_quotes_runtime(0);
+        clearstatcache();
+        $this->_closeChannelFile($fp);
+        $data = file_get_contents($this->_channelFileName($channel));
+        set_magic_quotes_runtime($rt);
+        $data = unserialize($data);
+        return $data;
+    }
+
+    function _listChannels()
+    {
+        $channellist = array();
+        if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) {
+            return array('pear.php.net', 'pecl.php.net', 'doc.php.net', '__uri');
+        }
+
+        $dp = opendir($this->channelsdir);
+        while ($ent = readdir($dp)) {
+            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
+                continue;
+            }
+
+            if ($ent == '__uri.reg') {
+                $channellist[] = '__uri';
+                continue;
+            }
+
+            $channellist[] = str_replace('_', '/', substr($ent, 0, -4));
+        }
+
+        closedir($dp);
+        if (!in_array('pear.php.net', $channellist)) {
+            $channellist[] = 'pear.php.net';
+        }
+
+        if (!in_array('pecl.php.net', $channellist)) {
+            $channellist[] = 'pecl.php.net';
+        }
+
+        if (!in_array('doc.php.net', $channellist)) {
+            $channellist[] = 'doc.php.net';
+        }
+
+
+        if (!in_array('__uri', $channellist)) {
+            $channellist[] = '__uri';
+        }
+
+        natsort($channellist);
+        return $channellist;
+    }
+
+    function _listPackages($channel = false)
+    {
+        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
+            return $this->_listChannelPackages($channel);
+        }
+
+        if (!file_exists($this->statedir) || !is_dir($this->statedir)) {
+            return array();
+        }
+
+        $pkglist = array();
+        $dp = opendir($this->statedir);
+        if (!$dp) {
+            return $pkglist;
+        }
+
+        while ($ent = readdir($dp)) {
+            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
+                continue;
+            }
+
+            $pkglist[] = substr($ent, 0, -4);
+        }
+        closedir($dp);
+        return $pkglist;
+    }
+
+    function _listChannelPackages($channel)
+    {
+        $pkglist = array();
+        if (!file_exists($this->_channelDirectoryName($channel)) ||
+              !is_dir($this->_channelDirectoryName($channel))) {
+            return array();
+        }
+
+        $dp = opendir($this->_channelDirectoryName($channel));
+        if (!$dp) {
+            return $pkglist;
+        }
+
+        while ($ent = readdir($dp)) {
+            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
+                continue;
+            }
+            $pkglist[] = substr($ent, 0, -4);
+        }
+
+        closedir($dp);
+        return $pkglist;
+    }
+
+    function _listAllPackages()
+    {
+        $ret = array();
+        foreach ($this->_listChannels() as $channel) {
+            $ret[$channel] = $this->_listPackages($channel);
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Add an installed package to the registry
+     * @param string package name
+     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
+     * @return bool success of saving
+     * @access private
+     */
+    function _addPackage($package, $info)
+    {
+        if ($this->_packageExists($package)) {
+            return false;
+        }
+
+        $fp = $this->_openPackageFile($package, 'wb');
+        if ($fp === null) {
+            return false;
+        }
+
+        $info['_lastmodified'] = time();
+        fwrite($fp, serialize($info));
+        $this->_closePackageFile($fp);
+        if (isset($info['filelist'])) {
+            $this->_rebuildFileMap();
+        }
+
+        return true;
+    }
+
+    /**
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @return bool
+     * @access private
+     */
+    function _addPackage2($info)
+    {
+        if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) {
+            return false;
+        }
+
+        if (!$info->validate()) {
+            if (class_exists('PEAR_Common')) {
+                $ui = PEAR_Frontend::singleton();
+                if ($ui) {
+                    foreach ($info->getValidationWarnings() as $err) {
+                        $ui->log($err['message'], true);
+                    }
+                }
+            }
+            return false;
+        }
+
+        $channel = $info->getChannel();
+        $package = $info->getPackage();
+        $save = $info;
+        if ($this->_packageExists($package, $channel)) {
+            return false;
+        }
+
+        if (!$this->_channelExists($channel, true)) {
+            return false;
+        }
+
+        $info = $info->toArray(true);
+        if (!$info) {
+            return false;
+        }
+
+        $fp = $this->_openPackageFile($package, 'wb', $channel);
+        if ($fp === null) {
+            return false;
+        }
+
+        $info['_lastmodified'] = time();
+        fwrite($fp, serialize($info));
+        $this->_closePackageFile($fp);
+        $this->_rebuildFileMap();
+        return true;
+    }
+
+    /**
+     * @param string Package name
+     * @param array parsed package.xml 1.0
+     * @param bool this parameter is only here for BC.  Don't use it.
+     * @access private
+     */
+    function _updatePackage($package, $info, $merge = true)
+    {
+        $oldinfo = $this->_packageInfo($package);
+        if (empty($oldinfo)) {
+            return false;
+        }
+
+        $fp = $this->_openPackageFile($package, 'w');
+        if ($fp === null) {
+            return false;
+        }
+
+        if (is_object($info)) {
+            $info = $info->toArray();
+        }
+        $info['_lastmodified'] = time();
+
+        $newinfo = $info;
+        if ($merge) {
+            $info = array_merge($oldinfo, $info);
+        } else {
+            $diff = $info;
+        }
+
+        fwrite($fp, serialize($info));
+        $this->_closePackageFile($fp);
+        if (isset($newinfo['filelist'])) {
+            $this->_rebuildFileMap();
+        }
+
+        return true;
+    }
+
+    /**
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @return bool
+     * @access private
+     */
+    function _updatePackage2($info)
+    {
+        if (!$this->_packageExists($info->getPackage(), $info->getChannel())) {
+            return false;
+        }
+
+        $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel());
+        if ($fp === null) {
+            return false;
+        }
+
+        $save = $info;
+        $info = $save->getArray(true);
+        $info['_lastmodified'] = time();
+        fwrite($fp, serialize($info));
+        $this->_closePackageFile($fp);
+        $this->_rebuildFileMap();
+        return true;
+    }
+
+    /**
+     * @param string Package name
+     * @param string Channel name
+     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
+     * @access private
+     */
+    function &_getPackage($package, $channel = 'pear.php.net')
+    {
+        $info = $this->_packageInfo($package, null, $channel);
+        if ($info === null) {
+            return $info;
+        }
+
+        $a = $this->_config;
+        if (!$a) {
+            $this->_config = &new PEAR_Config;
+            $this->_config->set('php_dir', $this->statedir);
+        }
+
+        if (!class_exists('PEAR_PackageFile')) {
+            require_once 'PEAR/PackageFile.php';
+        }
+
+        $pkg = &new PEAR_PackageFile($this->_config);
+        $pf = &$pkg->fromArray($info);
+        return $pf;
+    }
+
+    /**
+     * @param string channel name
+     * @param bool whether to strictly retrieve channel names
+     * @return PEAR_ChannelFile|PEAR_Error
+     * @access private
+     */
+    function &_getChannel($channel, $noaliases = false)
+    {
+        $ch = false;
+        if ($this->_channelExists($channel, $noaliases)) {
+            $chinfo = $this->_channelInfo($channel, $noaliases);
+            if ($chinfo) {
+                if (!class_exists('PEAR_ChannelFile')) {
+                    require_once 'PEAR/ChannelFile.php';
+                }
+
+                $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo);
+            }
+        }
+
+        if ($ch) {
+            if ($ch->validate()) {
+                return $ch;
+            }
+
+            foreach ($ch->getErrors(true) as $err) {
+                $message = $err['message'] . "\n";
+            }
+
+            $ch = PEAR::raiseError($message);
+            return $ch;
+        }
+
+        if ($this->_getChannelFromAlias($channel) == 'pear.php.net') {
+            // the registry is not properly set up, so use defaults
+            if (!class_exists('PEAR_ChannelFile')) {
+                require_once 'PEAR/ChannelFile.php';
+            }
+
+            $pear_channel = new PEAR_ChannelFile;
+            $pear_channel->setServer('pear.php.net');
+            $pear_channel->setAlias('pear');
+            $pear_channel->setSummary('PHP Extension and Application Repository');
+            $pear_channel->setDefaultPEARProtocols();
+            $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
+            $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
+            $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
+            return $pear_channel;
+        }
+
+        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
+            // the registry is not properly set up, so use defaults
+            if (!class_exists('PEAR_ChannelFile')) {
+                require_once 'PEAR/ChannelFile.php';
+            }
+            $pear_channel = new PEAR_ChannelFile;
+            $pear_channel->setServer('pecl.php.net');
+            $pear_channel->setAlias('pecl');
+            $pear_channel->setSummary('PHP Extension Community Library');
+            $pear_channel->setDefaultPEARProtocols();
+            $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
+            $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
+            $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
+            return $pear_channel;
+        }
+
+        if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
+            // the registry is not properly set up, so use defaults
+            if (!class_exists('PEAR_ChannelFile')) {
+                require_once 'PEAR/ChannelFile.php';
+            }
+
+            $doc_channel = new PEAR_ChannelFile;
+            $doc_channel->setServer('doc.php.net');
+            $doc_channel->setAlias('phpdocs');
+            $doc_channel->setSummary('PHP Documentation Team');
+            $doc_channel->setDefaultPEARProtocols();
+            $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
+            $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
+            $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
+            return $doc_channel;
+        }
+
+
+        if ($this->_getChannelFromAlias($channel) == '__uri') {
+            // the registry is not properly set up, so use defaults
+            if (!class_exists('PEAR_ChannelFile')) {
+                require_once 'PEAR/ChannelFile.php';
+            }
+
+            $private = new PEAR_ChannelFile;
+            $private->setName('__uri');
+            $private->setDefaultPEARProtocols();
+            $private->setBaseURL('REST1.0', '****');
+            $private->setSummary('Pseudo-channel for static packages');
+            return $private;
+        }
+
+        return $ch;
+    }
+
+    /**
+     * @param string Package name
+     * @param string Channel name
+     * @return bool
+     */
+    function packageExists($package, $channel = 'pear.php.net')
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_packageExists($package, $channel);
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+
+    // {{{ channelExists()
+
+    /**
+     * @param string channel name
+     * @param bool if true, then aliases will be ignored
+     * @return bool
+     */
+    function channelExists($channel, $noaliases = false)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_channelExists($channel, $noaliases);
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+
+    /**
+     * @param string channel name mirror is in
+     * @param string mirror name
+     *
+     * @return bool
+     */
+    function mirrorExists($channel, $mirror)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+
+        $ret = $this->_mirrorExists($channel, $mirror);
+        $this->_unlock();
+        return $ret;
+    }
+
+    // {{{ isAlias()
+
+    /**
+     * Determines whether the parameter is an alias of a channel
+     * @param string
+     * @return bool
+     */
+    function isAlias($alias)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_isChannelAlias($alias);
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ packageInfo()
+
+    /**
+     * @param string|null
+     * @param string|null
+     * @param string
+     * @return array|null
+     */
+    function packageInfo($package = null, $key = null, $channel = 'pear.php.net')
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_packageInfo($package, $key, $channel);
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ channelInfo()
+
+    /**
+     * Retrieve a raw array of channel data.
+     *
+     * Do not use this, instead use {@link getChannel()} for normal
+     * operations.  Array structure is undefined in this method
+     * @param string channel name
+     * @param bool whether to strictly retrieve information only on non-aliases
+     * @return array|null|PEAR_Error
+     */
+    function channelInfo($channel = null, $noaliases = false)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_channelInfo($channel, $noaliases);
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+
+    /**
+     * @param string
+     */
+    function channelName($channel)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_getChannelFromAlias($channel);
+        $this->_unlock();
+        return $ret;
+    }
+
+    /**
+     * @param string
+     */
+    function channelAlias($channel)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_getAlias($channel);
+        $this->_unlock();
+        return $ret;
+    }
+    // {{{ listPackages()
+
+    function listPackages($channel = false)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_listPackages($channel);
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ listAllPackages()
+
+    function listAllPackages()
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_listAllPackages();
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ listChannel()
+
+    function listChannels()
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_listChannels();
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ addPackage()
+
+    /**
+     * Add an installed package to the registry
+     * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object
+     *               that will be passed to {@link addPackage2()}
+     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
+     * @return bool success of saving
+     */
+    function addPackage($package, $info)
+    {
+        if (is_object($info)) {
+            return $this->addPackage2($info);
+        }
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+        $ret = $this->_addPackage($package, $info);
+        $this->_unlock();
+        if ($ret) {
+            if (!class_exists('PEAR_PackageFile_v1')) {
+                require_once 'PEAR/PackageFile/v1.php';
+            }
+            $pf = new PEAR_PackageFile_v1;
+            $pf->setConfig($this->_config);
+            $pf->fromArray($info);
+            $this->_dependencyDB->uninstallPackage($pf);
+            $this->_dependencyDB->installPackage($pf);
+        }
+        return $ret;
+    }
+
+    // }}}
+    // {{{ addPackage2()
+
+    function addPackage2($info)
+    {
+        if (!is_object($info)) {
+            return $this->addPackage($info['package'], $info);
+        }
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+        $ret = $this->_addPackage2($info);
+        $this->_unlock();
+        if ($ret) {
+            $this->_dependencyDB->uninstallPackage($info);
+            $this->_dependencyDB->installPackage($info);
+        }
+        return $ret;
+    }
+
+    // }}}
+    // {{{ updateChannel()
+
+    /**
+     * For future expandibility purposes, separate this
+     * @param PEAR_ChannelFile
+     */
+    function updateChannel($channel, $lastmodified = null)
+    {
+        if ($channel->getName() == '__uri') {
+            return false;
+        }
+        return $this->addChannel($channel, $lastmodified, true);
+    }
+
+    // }}}
+    // {{{ deleteChannel()
+
+    /**
+     * Deletion fails if there are any packages installed from the channel
+     * @param string|PEAR_ChannelFile channel name
+     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
+     */
+    function deleteChannel($channel)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+
+        $ret = $this->_deleteChannel($channel);
+        $this->_unlock();
+        if ($ret && is_a($this->_config, 'PEAR_Config')) {
+            $this->_config->setChannels($this->listChannels());
+        }
+
+        return $ret;
+    }
+
+    // }}}
+    // {{{ addChannel()
+
+    /**
+     * @param PEAR_ChannelFile Channel object
+     * @param string Last-Modified header from HTTP for caching
+     * @return boolean|PEAR_Error True on creation, false if it already exists
+     */
+    function addChannel($channel, $lastmodified = false, $update = false)
+    {
+        if (!is_a($channel, 'PEAR_ChannelFile') || !$channel->validate()) {
+            return false;
+        }
+
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+
+        $ret = $this->_addChannel($channel, $update, $lastmodified);
+        $this->_unlock();
+        if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) {
+            $this->_config->setChannels($this->listChannels());
+        }
+
+        return $ret;
+    }
+
+    // }}}
+    // {{{ deletePackage()
+
+    function deletePackage($package, $channel = 'pear.php.net')
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+
+        $file = $this->_packageFileName($package, $channel);
+        $ret  = file_exists($file) ? @unlink($file) : false;
+        $this->_rebuildFileMap();
+        $this->_unlock();
+        $p = array('channel' => $channel, 'package' => $package);
+        $this->_dependencyDB->uninstallPackage($p);
+        return $ret;
+    }
+
+    // }}}
+    // {{{ updatePackage()
+
+    function updatePackage($package, $info, $merge = true)
+    {
+        if (is_object($info)) {
+            return $this->updatePackage2($info, $merge);
+        }
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+        $ret = $this->_updatePackage($package, $info, $merge);
+        $this->_unlock();
+        if ($ret) {
+            if (!class_exists('PEAR_PackageFile_v1')) {
+                require_once 'PEAR/PackageFile/v1.php';
+            }
+            $pf = new PEAR_PackageFile_v1;
+            $pf->setConfig($this->_config);
+            $pf->fromArray($this->packageInfo($package));
+            $this->_dependencyDB->uninstallPackage($pf);
+            $this->_dependencyDB->installPackage($pf);
+        }
+        return $ret;
+    }
+
+    // }}}
+    // {{{ updatePackage2()
+
+    function updatePackage2($info)
+    {
+
+        if (!is_object($info)) {
+            return $this->updatePackage($info['package'], $info, $merge);
+        }
+
+        if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) {
+            return false;
+        }
+
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+
+        $ret = $this->_updatePackage2($info);
+        $this->_unlock();
+        if ($ret) {
+            $this->_dependencyDB->uninstallPackage($info);
+            $this->_dependencyDB->installPackage($info);
+        }
+
+        return $ret;
+    }
+
+    // }}}
+    // {{{ getChannel()
+    /**
+     * @param string channel name
+     * @param bool whether to strictly return raw channels (no aliases)
+     * @return PEAR_ChannelFile|PEAR_Error
+     */
+    function &getChannel($channel, $noaliases = false)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = &$this->_getChannel($channel, $noaliases);
+        $this->_unlock();
+        if (!$ret) {
+            return PEAR::raiseError('Unknown channel: ' . $channel);
+        }
+        return $ret;
+    }
+
+    // }}}
+    // {{{ getPackage()
+    /**
+     * @param string package name
+     * @param string channel name
+     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
+     */
+    function &getPackage($package, $channel = 'pear.php.net')
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $pf = &$this->_getPackage($package, $channel);
+        $this->_unlock();
+        return $pf;
+    }
+
+    // }}}
+
+    /**
+     * Get PEAR_PackageFile_v[1/2] objects representing the contents of
+     * a dependency group that are installed.
+     *
+     * This is used at uninstall-time
+     * @param array
+     * @return array|false
+     */
+    function getInstalledGroup($group)
+    {
+        $ret = array();
+        if (isset($group['package'])) {
+            if (!isset($group['package'][0])) {
+                $group['package'] = array($group['package']);
+            }
+            foreach ($group['package'] as $package) {
+                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
+                $p = &$this->getPackage($package['name'], $depchannel);
+                if ($p) {
+                    $save = &$p;
+                    $ret[] = &$save;
+                }
+            }
+        }
+        if (isset($group['subpackage'])) {
+            if (!isset($group['subpackage'][0])) {
+                $group['subpackage'] = array($group['subpackage']);
+            }
+            foreach ($group['subpackage'] as $package) {
+                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
+                $p = &$this->getPackage($package['name'], $depchannel);
+                if ($p) {
+                    $save = &$p;
+                    $ret[] = &$save;
+                }
+            }
+        }
+        if (!count($ret)) {
+            return false;
+        }
+        return $ret;
+    }
+
+    // {{{ getChannelValidator()
+    /**
+     * @param string channel name
+     * @return PEAR_Validate|false
+     */
+    function &getChannelValidator($channel)
+    {
+        $chan = $this->getChannel($channel);
+        if (PEAR::isError($chan)) {
+            return $chan;
+        }
+        $val = $chan->getValidationObject();
+        return $val;
+    }
+    // }}}
+    // {{{ getChannels()
+    /**
+     * @param string channel name
+     * @return array an array of PEAR_ChannelFile objects representing every installed channel
+     */
+    function &getChannels()
+    {
+        $ret = array();
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        foreach ($this->_listChannels() as $channel) {
+            $e = &$this->_getChannel($channel);
+            if (!$e || PEAR::isError($e)) {
+                continue;
+            }
+            $ret[] = $e;
+        }
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ checkFileMap()
+
+    /**
+     * Test whether a file or set of files belongs to a package.
+     *
+     * If an array is passed in
+     * @param string|array file path, absolute or relative to the pear
+     *                     install dir
+     * @param string|array name of PEAR package or array('package' => name, 'channel' =>
+     *                     channel) of a package that will be ignored
+     * @param string API version - 1.1 will exclude any files belonging to a package
+     * @param array private recursion variable
+     * @return array|false which package and channel the file belongs to, or an empty
+     *                     string if the file does not belong to an installed package,
+     *                     or belongs to the second parameter's package
+     */
+    function checkFileMap($path, $package = false, $api = '1.0', $attrs = false)
+    {
+        if (is_array($path)) {
+            static $notempty;
+            if (empty($notempty)) {
+                if (!class_exists('PEAR_Installer_Role')) {
+                    require_once 'PEAR/Installer/Role.php';
+                }
+                $notempty = create_function('$a','return !empty($a);');
+            }
+            $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1]))
+                : strtolower($package);
+            $pkgs = array();
+            foreach ($path as $name => $attrs) {
+                if (is_array($attrs)) {
+                    if (isset($attrs['install-as'])) {
+                        $name = $attrs['install-as'];
+                    }
+                    if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) {
+                        // these are not installed
+                        continue;
+                    }
+                    if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) {
+                        $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package;
+                    }
+                    if (isset($attrs['baseinstalldir'])) {
+                        $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name;
+                    }
+                }
+                $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs);
+                if (PEAR::isError($pkgs[$name])) {
+                    return $pkgs[$name];
+                }
+            }
+            return array_filter($pkgs, $notempty);
+        }
+        if (empty($this->filemap_cache)) {
+            if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+                return $e;
+            }
+            $err = $this->_readFileMap();
+            $this->_unlock();
+            if (PEAR::isError($err)) {
+                return $err;
+            }
+        }
+        if (!$attrs) {
+            $attrs = array('role' => 'php'); // any old call would be for PHP role only
+        }
+        if (isset($this->filemap_cache[$attrs['role']][$path])) {
+            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
+                return false;
+            }
+            return $this->filemap_cache[$attrs['role']][$path];
+        }
+        $l = strlen($this->install_dir);
+        if (substr($path, 0, $l) == $this->install_dir) {
+            $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
+        }
+        if (isset($this->filemap_cache[$attrs['role']][$path])) {
+            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
+                return false;
+            }
+            return $this->filemap_cache[$attrs['role']][$path];
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ flush()
+    /**
+     * Force a reload of the filemap
+     * @since 1.5.0RC3
+     */
+    function flushFileMap()
+    {
+        $this->filemap_cache = null;
+        clearstatcache(); // ensure that the next read gets the full, current filemap
+    }
+
+    // }}}
+    // {{{ apiVersion()
+    /**
+     * Get the expected API version.  Channels API is version 1.1, as it is backwards
+     * compatible with 1.0
+     * @return string
+     */
+    function apiVersion()
+    {
+        return '1.1';
+    }
+    // }}}
+
+
+    /**
+     * Parse a package name, or validate a parsed package name array
+     * @param string|array pass in an array of format
+     *                     array(
+     *                      'package' => 'pname',
+     *                     ['channel' => 'channame',]
+     *                     ['version' => 'version',]
+     *                     ['state' => 'state',]
+     *                     ['group' => 'groupname'])
+     *                     or a string of format
+     *                     [channel://][channame/]pname[-version|-state][/group=groupname]
+     * @return array|PEAR_Error
+     */
+    function parsePackageName($param, $defaultchannel = 'pear.php.net')
+    {
+        $saveparam = $param;
+        if (is_array($param)) {
+            // convert to string for error messages
+            $saveparam = $this->parsedPackageNameToString($param);
+            // process the array
+            if (!isset($param['package'])) {
+                return PEAR::raiseError('parsePackageName(): array $param ' .
+                    'must contain a valid package name in index "param"',
+                    'package', null, null, $param);
+            }
+            if (!isset($param['uri'])) {
+                if (!isset($param['channel'])) {
+                    $param['channel'] = $defaultchannel;
+                }
+            } else {
+                $param['channel'] = '__uri';
+            }
+        } else {
+            $components = @parse_url((string) $param);
+            if (isset($components['scheme'])) {
+                if ($components['scheme'] == 'http') {
+                    // uri package
+                    $param = array('uri' => $param, 'channel' => '__uri');
+                } elseif($components['scheme'] != 'channel') {
+                    return PEAR::raiseError('parsePackageName(): only channel:// uris may ' .
+                        'be downloaded, not "' . $param . '"', 'invalid', null, null, $param);
+                }
+            }
+            if (!isset($components['path'])) {
+                return PEAR::raiseError('parsePackageName(): array $param ' .
+                    'must contain a valid package name in "' . $param . '"',
+                    'package', null, null, $param);
+            }
+            if (isset($components['host'])) {
+                // remove the leading "/"
+                $components['path'] = substr($components['path'], 1);
+            }
+            if (!isset($components['scheme'])) {
+                if (strpos($components['path'], '/') !== false) {
+                    if ($components['path']{0} == '/') {
+                        return PEAR::raiseError('parsePackageName(): this is not ' .
+                            'a package name, it begins with "/" in "' . $param . '"',
+                            'invalid', null, null, $param);
+                    }
+                    $parts = explode('/', $components['path']);
+                    $components['host'] = array_shift($parts);
+                    if (count($parts) > 1) {
+                        $components['path'] = array_pop($parts);
+                        $components['host'] .= '/' . implode('/', $parts);
+                    } else {
+                        $components['path'] = implode('/', $parts);
+                    }
+                } else {
+                    $components['host'] = $defaultchannel;
+                }
+            } else {
+                if (strpos($components['path'], '/')) {
+                    $parts = explode('/', $components['path']);
+                    $components['path'] = array_pop($parts);
+                    $components['host'] .= '/' . implode('/', $parts);
+                }
+            }
+
+            if (is_array($param)) {
+                $param['package'] = $components['path'];
+            } else {
+                $param = array(
+                    'package' => $components['path']
+                    );
+                if (isset($components['host'])) {
+                    $param['channel'] = $components['host'];
+                }
+            }
+            if (isset($components['fragment'])) {
+                $param['group'] = $components['fragment'];
+            }
+            if (isset($components['user'])) {
+                $param['user'] = $components['user'];
+            }
+            if (isset($components['pass'])) {
+                $param['pass'] = $components['pass'];
+            }
+            if (isset($components['query'])) {
+                parse_str($components['query'], $param['opts']);
+            }
+            // check for extension
+            $pathinfo = pathinfo($param['package']);
+            if (isset($pathinfo['extension']) &&
+                  in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) {
+                $param['extension'] = $pathinfo['extension'];
+                $param['package'] = substr($pathinfo['basename'], 0,
+                    strlen($pathinfo['basename']) - 4);
+            }
+            // check for version
+            if (strpos($param['package'], '-')) {
+                $test = explode('-', $param['package']);
+                if (count($test) != 2) {
+                    return PEAR::raiseError('parsePackageName(): only one version/state ' .
+                        'delimiter "-" is allowed in "' . $saveparam . '"',
+                        'version', null, null, $param);
+                }
+                list($param['package'], $param['version']) = $test;
+            }
+        }
+        // validation
+        $info = $this->channelExists($param['channel']);
+        if (PEAR::isError($info)) {
+            return $info;
+        }
+        if (!$info) {
+            return PEAR::raiseError('unknown channel "' . $param['channel'] .
+                '" in "' . $saveparam . '"', 'channel', null, null, $param);
+        }
+        $chan = $this->getChannel($param['channel']);
+        if (PEAR::isError($chan)) {
+            return $chan;
+        }
+        if (!$chan) {
+            return PEAR::raiseError("Exception: corrupt registry, could not " .
+                "retrieve channel " . $param['channel'] . " information",
+                'registry', null, null, $param);
+        }
+        $param['channel'] = $chan->getName();
+        $validate = $chan->getValidationObject();
+        $vpackage = $chan->getValidationPackage();
+        // validate package name
+        if (!$validate->validPackageName($param['package'], $vpackage['_content'])) {
+            return PEAR::raiseError('parsePackageName(): invalid package name "' .
+                $param['package'] . '" in "' . $saveparam . '"',
+                'package', null, null, $param);
+        }
+        if (isset($param['group'])) {
+            if (!PEAR_Validate::validGroupName($param['group'])) {
+                return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] .
+                    '" is not a valid group name in "' . $saveparam . '"', 'group', null, null,
+                    $param);
+            }
+        }
+        if (isset($param['state'])) {
+            if (!in_array(strtolower($param['state']), $validate->getValidStates())) {
+                return PEAR::raiseError('parsePackageName(): state "' . $param['state']
+                    . '" is not a valid state in "' . $saveparam . '"',
+                    'state', null, null, $param);
+            }
+        }
+        if (isset($param['version'])) {
+            if (isset($param['state'])) {
+                return PEAR::raiseError('parsePackageName(): cannot contain both ' .
+                    'a version and a stability (state) in "' . $saveparam . '"',
+                    'version/state', null, null, $param);
+            }
+            // check whether version is actually a state
+            if (in_array(strtolower($param['version']), $validate->getValidStates())) {
+                $param['state'] = strtolower($param['version']);
+                unset($param['version']);
+            } else {
+                if (!$validate->validVersion($param['version'])) {
+                    return PEAR::raiseError('parsePackageName(): "' . $param['version'] .
+                        '" is neither a valid version nor a valid state in "' .
+                        $saveparam . '"', 'version/state', null, null, $param);
+                }
+            }
+        }
+        return $param;
+    }
+
+    /**
+     * @param array
+     * @return string
+     */
+    function parsedPackageNameToString($parsed, $brief = false)
+    {
+        if (is_string($parsed)) {
+            return $parsed;
+        }
+        if (is_object($parsed)) {
+            $p = $parsed;
+            $parsed = array(
+                'package' => $p->getPackage(),
+                'channel' => $p->getChannel(),
+                'version' => $p->getVersion(),
+            );
+        }
+        if (isset($parsed['uri'])) {
+            return $parsed['uri'];
+        }
+        if ($brief) {
+            if ($channel = $this->channelAlias($parsed['channel'])) {
+                return $channel . '/' . $parsed['package'];
+            }
+        }
+        $upass = '';
+        if (isset($parsed['user'])) {
+            $upass = $parsed['user'];
+            if (isset($parsed['pass'])) {
+                $upass .= ':' . $parsed['pass'];
+            }
+            $upass = "$upass@";
+        }
+        $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package'];
+        if (isset($parsed['version']) || isset($parsed['state'])) {
+            $ver = isset($parsed['version']) ? $parsed['version'] : '';
+            $ver .= isset($parsed['state']) ? $parsed['state'] : '';
+            $ret .= '-' . $ver;
+        }
+        if (isset($parsed['extension'])) {
+            $ret .= '.' . $parsed['extension'];
+        }
+        if (isset($parsed['opts'])) {
+            $ret .= '?';
+            foreach ($parsed['opts'] as $name => $value) {
+                $parsed['opts'][$name] = "$name=$value";
+            }
+            $ret .= implode('&', $parsed['opts']);
+        }
+        if (isset($parsed['group'])) {
+            $ret .= '#' . $parsed['group'];
+        }
+        return $ret;
+    }
+}
Index: trunk/library/PEAR/PEAR/Task/Windowseol.php
===================================================================
--- trunk/library/PEAR/PEAR/Task/Windowseol.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Task/Windowseol.php	(revision 5146)
@@ -0,0 +1,77 @@
+<?php
+/**
+ * <tasks:windowseol>
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Windowseol.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * Base class
+ */
+require_once 'PEAR/Task/Common.php';
+/**
+ * Implements the windows line endsings file task.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Task_Windowseol extends PEAR_Task_Common
+{
+    var $type = 'simple';
+    var $phase = PEAR_TASK_PACKAGE;
+    var $_replacements;
+
+    /**
+     * Validate the raw xml at parsing-time.
+     * @param PEAR_PackageFile_v2
+     * @param array raw, parsed xml
+     * @param PEAR_Config
+     * @static
+     */
+    function validateXml($pkg, $xml, $config, $fileXml)
+    {
+        if ($xml != '') {
+            return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
+        }
+        return true;
+    }
+
+    /**
+     * Initialize a task instance with the parameters
+     * @param array raw, parsed xml
+     * @param unused
+     */
+    function init($xml, $attribs)
+    {
+    }
+
+    /**
+     * Replace all line endings with windows line endings
+     *
+     * See validateXml() source for the complete list of allowed fields
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @param string file contents
+     * @param string the eventual final file location (informational only)
+     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
+     *         (use $this->throwError), otherwise return the new contents
+     */
+    function startSession($pkg, $contents, $dest)
+    {
+        $this->logger->log(3, "replacing all line endings with \\r\\n in $dest");
+        return preg_replace("/\r\n|\n\r|\r|\n/", "\r\n", $contents);
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Task/Postinstallscript/rw.php
===================================================================
--- trunk/library/PEAR/PEAR/Task/Postinstallscript/rw.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Task/Postinstallscript/rw.php	(revision 5146)
@@ -0,0 +1,169 @@
+<?php
+/**
+ * <tasks:postinstallscript> - read/write version
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a10
+ */
+/**
+ * Base class
+ */
+require_once 'PEAR/Task/Postinstallscript.php';
+/**
+ * Abstracts the postinstallscript file task xml.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a10
+ */
+class PEAR_Task_Postinstallscript_rw extends PEAR_Task_Postinstallscript
+{
+    /**
+     * parent package file object
+     *
+     * @var PEAR_PackageFile_v2_rw
+     */
+    var $_pkg;
+    /**
+     * Enter description here...
+     *
+     * @param PEAR_PackageFile_v2_rw $pkg
+     * @param PEAR_Config $config
+     * @param PEAR_Frontend $logger
+     * @param array $fileXml
+     * @return PEAR_Task_Postinstallscript_rw
+     */
+    function PEAR_Task_Postinstallscript_rw(&$pkg, &$config, &$logger, $fileXml)
+    {
+        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
+        $this->_contents = $fileXml;
+        $this->_pkg = &$pkg;
+        $this->_params = array();
+    }
+
+    function validate()
+    {
+        return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
+    }
+
+    function getName()
+    {
+        return 'postinstallscript';
+    }
+
+    /**
+     * add a simple <paramgroup> to the post-install script
+     *
+     * Order is significant, so call this method in the same
+     * sequence the users should see the paramgroups.  The $params
+     * parameter should either be the result of a call to {@link getParam()}
+     * or an array of calls to getParam().
+     *
+     * Use {@link addConditionTypeGroup()} to add a <paramgroup> containing
+     * a <conditiontype> tag
+     * @param string $id <paramgroup> id as seen by the script
+     * @param array|false $params array of getParam() calls, or false for no params
+     * @param string|false $instructions
+     */
+    function addParamGroup($id, $params = false, $instructions = false)
+    {
+        if ($params && isset($params[0]) && !isset($params[1])) {
+            $params = $params[0];
+        }
+        $stuff =
+            array(
+                $this->_pkg->getTasksNs() . ':id' => $id,
+            );
+        if ($instructions) {
+            $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions;
+        }
+        if ($params) {
+            $stuff[$this->_pkg->getTasksNs() . ':param'] = $params;
+        }
+        $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff;
+    }
+
+    /**
+     * add a complex <paramgroup> to the post-install script with conditions
+     *
+     * This inserts a <paramgroup> with
+     *
+     * Order is significant, so call this method in the same
+     * sequence the users should see the paramgroups.  The $params
+     * parameter should either be the result of a call to {@link getParam()}
+     * or an array of calls to getParam().
+     *
+     * Use {@link addParamGroup()} to add a simple <paramgroup>
+     *
+     * @param string $id <paramgroup> id as seen by the script
+     * @param string $oldgroup <paramgroup> id of the section referenced by
+     *                         <conditiontype>
+     * @param string $param name of the <param> from the older section referenced
+     *                      by <contitiontype>
+     * @param string $value value to match of the parameter
+     * @param string $conditiontype one of '=', '!=', 'preg_match'
+     * @param array|false $params array of getParam() calls, or false for no params
+     * @param string|false $instructions
+     */
+    function addConditionTypeGroup($id, $oldgroup, $param, $value, $conditiontype = '=',
+                                   $params = false, $instructions = false)
+    {
+        if ($params && isset($params[0]) && !isset($params[1])) {
+            $params = $params[0];
+        }
+        $stuff = array(
+            $this->_pkg->getTasksNs() . ':id' => $id,
+        );
+        if ($instructions) {
+            $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions;
+        }
+        $stuff[$this->_pkg->getTasksNs() . ':name'] = $oldgroup . '::' . $param;
+        $stuff[$this->_pkg->getTasksNs() . ':conditiontype'] = $conditiontype;
+        $stuff[$this->_pkg->getTasksNs() . ':value'] = $value;
+        if ($params) {
+            $stuff[$this->_pkg->getTasksNs() . ':param'] = $params;
+        }
+        $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff;
+    }
+
+    function getXml()
+    {
+        return $this->_params;
+    }
+
+    /**
+     * Use to set up a param tag for use in creating a paramgroup
+     * @static
+     */
+    function getParam($name, $prompt, $type = 'string', $default = null)
+    {
+        if ($default !== null) {
+            return
+            array(
+                $this->_pkg->getTasksNs() . ':name' => $name,
+                $this->_pkg->getTasksNs() . ':prompt' => $prompt,
+                $this->_pkg->getTasksNs() . ':type' => $type,
+                $this->_pkg->getTasksNs() . ':default' => $default
+            );
+        }
+        return
+            array(
+                $this->_pkg->getTasksNs() . ':name' => $name,
+                $this->_pkg->getTasksNs() . ':prompt' => $prompt,
+                $this->_pkg->getTasksNs() . ':type' => $type,
+            );
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Task/Replace.php
===================================================================
--- trunk/library/PEAR/PEAR/Task/Replace.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Task/Replace.php	(revision 5146)
@@ -0,0 +1,176 @@
+<?php
+/**
+ * <tasks:replace>
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Replace.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * Base class
+ */
+require_once 'PEAR/Task/Common.php';
+/**
+ * Implements the replace file task.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Task_Replace extends PEAR_Task_Common
+{
+    var $type = 'simple';
+    var $phase = PEAR_TASK_PACKAGEANDINSTALL;
+    var $_replacements;
+
+    /**
+     * Validate the raw xml at parsing-time.
+     * @param PEAR_PackageFile_v2
+     * @param array raw, parsed xml
+     * @param PEAR_Config
+     * @static
+     */
+    function validateXml($pkg, $xml, $config, $fileXml)
+    {
+        if (!isset($xml['attribs'])) {
+            return array(PEAR_TASK_ERROR_NOATTRIBS);
+        }
+        if (!isset($xml['attribs']['type'])) {
+            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'type');
+        }
+        if (!isset($xml['attribs']['to'])) {
+            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'to');
+        }
+        if (!isset($xml['attribs']['from'])) {
+            return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'from');
+        }
+        if ($xml['attribs']['type'] == 'pear-config') {
+            if (!in_array($xml['attribs']['to'], $config->getKeys())) {
+                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
+                    $config->getKeys());
+            }
+        } elseif ($xml['attribs']['type'] == 'php-const') {
+            if (defined($xml['attribs']['to'])) {
+                return true;
+            } else {
+                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
+                    array('valid PHP constant'));
+            }
+        } elseif ($xml['attribs']['type'] == 'package-info') {
+            if (in_array($xml['attribs']['to'],
+                array('name', 'summary', 'channel', 'notes', 'extends', 'description',
+                    'release_notes', 'license', 'release-license', 'license-uri',
+                    'version', 'api-version', 'state', 'api-state', 'release_date',
+                    'date', 'time'))) {
+                return true;
+            } else {
+                return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
+                    array('name', 'summary', 'channel', 'notes', 'extends', 'description',
+                    'release_notes', 'license', 'release-license', 'license-uri',
+                    'version', 'api-version', 'state', 'api-state', 'release_date',
+                    'date', 'time'));
+            }
+        } else {
+            return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'type', $xml['attribs']['type'],
+                array('pear-config', 'package-info', 'php-const'));
+        }
+        return true;
+    }
+
+    /**
+     * Initialize a task instance with the parameters
+     * @param array raw, parsed xml
+     * @param unused
+     */
+    function init($xml, $attribs)
+    {
+        $this->_replacements = isset($xml['attribs']) ? array($xml) : $xml;
+    }
+
+    /**
+     * Do a package.xml 1.0 replacement, with additional package-info fields available
+     *
+     * See validateXml() source for the complete list of allowed fields
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @param string file contents
+     * @param string the eventual final file location (informational only)
+     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
+     *         (use $this->throwError), otherwise return the new contents
+     */
+    function startSession($pkg, $contents, $dest)
+    {
+        $subst_from = $subst_to = array();
+        foreach ($this->_replacements as $a) {
+            $a = $a['attribs'];
+            $to = '';
+            if ($a['type'] == 'pear-config') {
+                if ($this->installphase == PEAR_TASK_PACKAGE) {
+                    return false;
+                }
+                if ($a['to'] == 'master_server') {
+                    $chan = $this->registry->getChannel($pkg->getChannel());
+                    if (!PEAR::isError($chan)) {
+                        $to = $chan->getServer();
+                    } else {
+                        $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
+                        return false;
+                    }
+                } else {
+                    if ($this->config->isDefinedLayer('ftp')) {
+                        // try the remote config file first
+                        $to = $this->config->get($a['to'], 'ftp', $pkg->getChannel());
+                        if (is_null($to)) {
+                            // then default to local
+                            $to = $this->config->get($a['to'], null, $pkg->getChannel());
+                        }
+                    } else {
+                        $to = $this->config->get($a['to'], null, $pkg->getChannel());
+                    }
+                }
+                if (is_null($to)) {
+                    $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
+                    return false;
+                }
+            } elseif ($a['type'] == 'php-const') {
+                if ($this->installphase == PEAR_TASK_PACKAGE) {
+                    return false;
+                }
+                if (defined($a['to'])) {
+                    $to = constant($a['to']);
+                } else {
+                    $this->logger->log(0, "$dest: invalid php-const replacement: $a[to]");
+                    return false;
+                }
+            } else {
+                if ($t = $pkg->packageInfo($a['to'])) {
+                    $to = $t;
+                } else {
+                    $this->logger->log(0, "$dest: invalid package-info replacement: $a[to]");
+                    return false;
+                }
+            }
+            if (!is_null($to)) {
+                $subst_from[] = $a['from'];
+                $subst_to[] = $to;
+            }
+        }
+        $this->logger->log(3, "doing " . sizeof($subst_from) .
+            " substitution(s) for $dest");
+        if (sizeof($subst_from)) {
+            $contents = str_replace($subst_from, $subst_to, $contents);
+        }
+        return $contents;
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Task/Unixeol/rw.php
===================================================================
--- trunk/library/PEAR/PEAR/Task/Unixeol/rw.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Task/Unixeol/rw.php	(revision 5146)
@@ -0,0 +1,56 @@
+<?php
+/**
+ * <tasks:unixeol> - read/write version
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a10
+ */
+/**
+ * Base class
+ */
+require_once 'PEAR/Task/Unixeol.php';
+/**
+ * Abstracts the unixeol task xml.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a10
+ */
+class PEAR_Task_Unixeol_rw extends PEAR_Task_Unixeol
+{
+    function PEAR_Task_Unixeol_rw(&$pkg, &$config, &$logger, $fileXml)
+    {
+        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
+        $this->_contents = $fileXml;
+        $this->_pkg = &$pkg;
+        $this->_params = array();
+    }
+
+    function validate()
+    {
+        return true;
+    }
+
+    function getName()
+    {
+        return 'unixeol';
+    }
+
+    function getXml()
+    {
+        return '';
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Task/Postinstallscript.php
===================================================================
--- trunk/library/PEAR/PEAR/Task/Postinstallscript.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Task/Postinstallscript.php	(revision 5146)
@@ -0,0 +1,323 @@
+<?php
+/**
+ * <tasks:postinstallscript>
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Postinstallscript.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * Base class
+ */
+require_once 'PEAR/Task/Common.php';
+/**
+ * Implements the postinstallscript file task.
+ *
+ * Note that post-install scripts are handled separately from installation, by the
+ * "pear run-scripts" command
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Task_Postinstallscript extends PEAR_Task_Common
+{
+    var $type = 'script';
+    var $_class;
+    var $_params;
+    var $_obj;
+    /**
+     *
+     * @var PEAR_PackageFile_v2
+     */
+    var $_pkg;
+    var $_contents;
+    var $phase = PEAR_TASK_INSTALL;
+
+    /**
+     * Validate the raw xml at parsing-time.
+     *
+     * This also attempts to validate the script to make sure it meets the criteria
+     * for a post-install script
+     * @param PEAR_PackageFile_v2
+     * @param array The XML contents of the <postinstallscript> tag
+     * @param PEAR_Config
+     * @param array the entire parsed <file> tag
+     * @static
+     */
+    function validateXml($pkg, $xml, $config, $fileXml)
+    {
+        if ($fileXml['role'] != 'php') {
+            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+            $fileXml['name'] . '" must be role="php"');
+        }
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+        $file = $pkg->getFileContents($fileXml['name']);
+        if (PEAR::isError($file)) {
+            PEAR::popErrorHandling();
+            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                $fileXml['name'] . '" is not valid: ' .
+                $file->getMessage());
+        } elseif ($file === null) {
+            return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                $fileXml['name'] . '" could not be retrieved for processing!');
+        } else {
+            $analysis = $pkg->analyzeSourceCode($file, true);
+            if (!$analysis) {
+                PEAR::popErrorHandling();
+                $warnings = '';
+                foreach ($pkg->getValidationWarnings() as $warn) {
+                    $warnings .= $warn['message'] . "\n";
+                }
+                return array(PEAR_TASK_ERROR_INVALID, 'Analysis of post-install script "' .
+                    $fileXml['name'] . '" failed: ' . $warnings);
+            }
+            if (count($analysis['declared_classes']) != 1) {
+                PEAR::popErrorHandling();
+                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                    $fileXml['name'] . '" must declare exactly 1 class');
+            }
+            $class = $analysis['declared_classes'][0];
+            if ($class != str_replace(array('/', '.php'), array('_', ''),
+                  $fileXml['name']) . '_postinstall') {
+                PEAR::popErrorHandling();
+                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                    $fileXml['name'] . '" class "' . $class . '" must be named "' .
+                    str_replace(array('/', '.php'), array('_', ''),
+                    $fileXml['name']) . '_postinstall"');
+            }
+            if (!isset($analysis['declared_methods'][$class])) {
+                PEAR::popErrorHandling();
+                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                    $fileXml['name'] . '" must declare methods init() and run()');
+            }
+            $methods = array('init' => 0, 'run' => 1);
+            foreach ($analysis['declared_methods'][$class] as $method) {
+                if (isset($methods[$method])) {
+                    unset($methods[$method]);
+                }
+            }
+            if (count($methods)) {
+                PEAR::popErrorHandling();
+                return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                    $fileXml['name'] . '" must declare methods init() and run()');
+            }
+        }
+        PEAR::popErrorHandling();
+        $definedparams = array();
+        $tasksNamespace = $pkg->getTasksNs() . ':';
+        if (!isset($xml[$tasksNamespace . 'paramgroup']) && isset($xml['paramgroup'])) {
+            // in order to support the older betas, which did not expect internal tags
+            // to also use the namespace
+            $tasksNamespace = '';
+        }
+        if (isset($xml[$tasksNamespace . 'paramgroup'])) {
+            $params = $xml[$tasksNamespace . 'paramgroup'];
+            if (!is_array($params) || !isset($params[0])) {
+                $params = array($params);
+            }
+            foreach ($params as $param) {
+                if (!isset($param[$tasksNamespace . 'id'])) {
+                    return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                        $fileXml['name'] . '" <paramgroup> must have ' .
+                        'an ' . $tasksNamespace . 'id> tag');
+                }
+                if (isset($param[$tasksNamespace . 'name'])) {
+                    if (!in_array($param[$tasksNamespace . 'name'], $definedparams)) {
+                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                            $fileXml['name'] . '" ' . $tasksNamespace .
+                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
+                            '" parameter "' . $param[$tasksNamespace . 'name'] .
+                            '" has not been previously defined');
+                    }
+                    if (!isset($param[$tasksNamespace . 'conditiontype'])) {
+                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                            $fileXml['name'] . '" ' . $tasksNamespace .
+                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
+                            '" must have a ' . $tasksNamespace .
+                            'conditiontype> tag containing either "=", ' .
+                            '"!=", or "preg_match"');
+                    }
+                    if (!in_array($param[$tasksNamespace . 'conditiontype'],
+                          array('=', '!=', 'preg_match'))) {
+                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                            $fileXml['name'] . '" ' . $tasksNamespace .
+                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
+                            '" must have a ' . $tasksNamespace .
+                            'conditiontype> tag containing either "=", ' .
+                            '"!=", or "preg_match"');
+                    }
+                    if (!isset($param[$tasksNamespace . 'value'])) {
+                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                            $fileXml['name'] . '" ' . $tasksNamespace .
+                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
+                            '" must have a ' . $tasksNamespace .
+                            'value> tag containing expected parameter value');
+                    }
+                }
+                if (isset($param[$tasksNamespace . 'instructions'])) {
+                    if (!is_string($param[$tasksNamespace . 'instructions'])) {
+                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                            $fileXml['name'] . '" ' . $tasksNamespace .
+                            'paramgroup> id "' . $param[$tasksNamespace . 'id'] .
+                            '" ' . $tasksNamespace . 'instructions> must be simple text');
+                    }
+                }
+                if (!isset($param[$tasksNamespace . 'param'])) {
+                    continue; // <param> is no longer required
+                }
+                $subparams = $param[$tasksNamespace . 'param'];
+                if (!is_array($subparams) || !isset($subparams[0])) {
+                    $subparams = array($subparams);
+                }
+                foreach ($subparams as $subparam) {
+                    if (!isset($subparam[$tasksNamespace . 'name'])) {
+                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                            $fileXml['name'] . '" parameter for ' .
+                            $tasksNamespace . 'paramgroup> id "' .
+                            $param[$tasksNamespace . 'id'] . '" must have ' .
+                            'a ' . $tasksNamespace . 'name> tag');
+                    }
+                    if (!preg_match('/[a-zA-Z0-9]+/',
+                          $subparam[$tasksNamespace . 'name'])) {
+                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                            $fileXml['name'] . '" parameter "' .
+                            $subparam[$tasksNamespace . 'name'] .
+                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
+                            $param[$tasksNamespace . 'id'] .
+                            '" is not a valid name.  Must contain only alphanumeric characters');
+                    }
+                    if (!isset($subparam[$tasksNamespace . 'prompt'])) {
+                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                            $fileXml['name'] . '" parameter "' .
+                            $subparam[$tasksNamespace . 'name'] .
+                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
+                            $param[$tasksNamespace . 'id'] .
+                            '" must have a ' . $tasksNamespace . 'prompt> tag');
+                    }
+                    if (!isset($subparam[$tasksNamespace . 'type'])) {
+                        return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' .
+                            $fileXml['name'] . '" parameter "' .
+                            $subparam[$tasksNamespace . 'name'] .
+                            '" for ' . $tasksNamespace . 'paramgroup> id "' .
+                            $param[$tasksNamespace . 'id'] .
+                            '" must have a ' . $tasksNamespace . 'type> tag');
+                    }
+                    $definedparams[] = $param[$tasksNamespace . 'id'] . '::' .
+                    $subparam[$tasksNamespace . 'name'];
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Initialize a task instance with the parameters
+     * @param array raw, parsed xml
+     * @param array attributes from the <file> tag containing this task
+     * @param string|null last installed version of this package, if any (useful for upgrades)
+     */
+    function init($xml, $fileattribs, $lastversion)
+    {
+        $this->_class = str_replace('/', '_', $fileattribs['name']);
+        $this->_filename = $fileattribs['name'];
+        $this->_class = str_replace ('.php', '', $this->_class) . '_postinstall';
+        $this->_params = $xml;
+        $this->_lastversion = $lastversion;
+    }
+
+    /**
+     * Strip the tasks: namespace from internal params
+     *
+     * @access private
+     */
+    function _stripNamespace($params = null)
+    {
+        if ($params === null) {
+            $params = array();
+            if (!is_array($this->_params)) {
+                return;
+            }
+            foreach ($this->_params as $i => $param) {
+                if (is_array($param)) {
+                    $param = $this->_stripNamespace($param);
+                }
+                $params[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param;
+            }
+            $this->_params = $params;
+        } else {
+            $newparams = array();
+            foreach ($params as $i => $param) {
+                if (is_array($param)) {
+                    $param = $this->_stripNamespace($param);
+                }
+                $newparams[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param;
+            }
+            return $newparams;
+        }
+    }
+
+    /**
+     * Unlike other tasks, the installed file name is passed in instead of the file contents,
+     * because this task is handled post-installation
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @param string file name
+     * @return bool|PEAR_Error false to skip this file, PEAR_Error to fail
+     *         (use $this->throwError)
+     */
+    function startSession($pkg, $contents)
+    {
+        if ($this->installphase != PEAR_TASK_INSTALL) {
+            return false;
+        }
+        // remove the tasks: namespace if present
+        $this->_pkg = $pkg;
+        $this->_stripNamespace();
+        $this->logger->log(0, 'Including external post-installation script "' .
+            $contents . '" - any errors are in this script');
+        include_once $contents;
+        if (class_exists($this->_class)) {
+            $this->logger->log(0, 'Inclusion succeeded');
+        } else {
+            return $this->throwError('init of post-install script class "' . $this->_class
+                . '" failed');
+        }
+        $this->_obj = new $this->_class;
+        $this->logger->log(1, 'running post-install script "' . $this->_class . '->init()"');
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+        $res = $this->_obj->init($this->config, $pkg, $this->_lastversion);
+        PEAR::popErrorHandling();
+        if ($res) {
+            $this->logger->log(0, 'init succeeded');
+        } else {
+            return $this->throwError('init of post-install script "' . $this->_class .
+                '->init()" failed');
+        }
+        $this->_contents = $contents;
+        return true;
+    }
+
+    /**
+     * No longer used
+     * @see PEAR_PackageFile_v2::runPostinstallScripts()
+     * @param array an array of tasks
+     * @param string install or upgrade
+     * @access protected
+     * @static
+     */
+    function run()
+    {
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Task/Unixeol.php
===================================================================
--- trunk/library/PEAR/PEAR/Task/Unixeol.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Task/Unixeol.php	(revision 5146)
@@ -0,0 +1,77 @@
+<?php
+/**
+ * <tasks:unixeol>
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Unixeol.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**
+ * Base class
+ */
+require_once 'PEAR/Task/Common.php';
+/**
+ * Implements the unix line endings file task.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Task_Unixeol extends PEAR_Task_Common
+{
+    var $type = 'simple';
+    var $phase = PEAR_TASK_PACKAGE;
+    var $_replacements;
+
+    /**
+     * Validate the raw xml at parsing-time.
+     * @param PEAR_PackageFile_v2
+     * @param array raw, parsed xml
+     * @param PEAR_Config
+     * @static
+     */
+    function validateXml($pkg, $xml, $config, $fileXml)
+    {
+        if ($xml != '') {
+            return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
+        }
+        return true;
+    }
+
+    /**
+     * Initialize a task instance with the parameters
+     * @param array raw, parsed xml
+     * @param unused
+     */
+    function init($xml, $attribs)
+    {
+    }
+
+    /**
+     * Replace all line endings with line endings customized for the current OS
+     *
+     * See validateXml() source for the complete list of allowed fields
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @param string file contents
+     * @param string the eventual final file location (informational only)
+     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
+     *         (use $this->throwError), otherwise return the new contents
+     */
+    function startSession($pkg, $contents, $dest)
+    {
+        $this->logger->log(3, "replacing all line endings with \\n in $dest");
+        return preg_replace("/\r\n|\n\r|\r|\n/", "\n", $contents);
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Task/Windowseol/rw.php
===================================================================
--- trunk/library/PEAR/PEAR/Task/Windowseol/rw.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Task/Windowseol/rw.php	(revision 5146)
@@ -0,0 +1,56 @@
+<?php
+/**
+ * <tasks:windowseol> - read/write version
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a10
+ */
+/**
+ * Base class
+ */
+require_once 'PEAR/Task/Windowseol.php';
+/**
+ * Abstracts the windowseol task xml.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a10
+ */
+class PEAR_Task_Windowseol_rw extends PEAR_Task_Windowseol
+{
+    function PEAR_Task_Windowseol_rw(&$pkg, &$config, &$logger, $fileXml)
+    {
+        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
+        $this->_contents = $fileXml;
+        $this->_pkg = &$pkg;
+        $this->_params = array();
+    }
+
+    function validate()
+    {
+        return true;
+    }
+
+    function getName()
+    {
+        return 'windowseol';
+    }
+
+    function getXml()
+    {
+        return '';
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Task/Replace/rw.php
===================================================================
--- trunk/library/PEAR/PEAR/Task/Replace/rw.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Task/Replace/rw.php	(revision 5146)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * <tasks:replace> - read/write version
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a10
+ */
+/**
+ * Base class
+ */
+require_once 'PEAR/Task/Replace.php';
+/**
+ * Abstracts the replace task xml.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a10
+ */
+class PEAR_Task_Replace_rw extends PEAR_Task_Replace
+{
+    function PEAR_Task_Replace_rw(&$pkg, &$config, &$logger, $fileXml)
+    {
+        parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE);
+        $this->_contents = $fileXml;
+        $this->_pkg = &$pkg;
+        $this->_params = array();
+    }
+
+    function validate()
+    {
+        return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
+    }
+
+    function setInfo($from, $to, $type)
+    {
+        $this->_params = array('attribs' => array('from' => $from, 'to' => $to, 'type' => $type));
+    }
+
+    function getName()
+    {
+        return 'replace';
+    }
+
+    function getXml()
+    {
+        return $this->_params;
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Task/Common.php
===================================================================
--- trunk/library/PEAR/PEAR/Task/Common.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Task/Common.php	(revision 5146)
@@ -0,0 +1,202 @@
+<?php
+/**
+ * PEAR_Task_Common, base class for installer tasks
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+/**#@+
+ * Error codes for task validation routines
+ */
+define('PEAR_TASK_ERROR_NOATTRIBS', 1);
+define('PEAR_TASK_ERROR_MISSING_ATTRIB', 2);
+define('PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE', 3);
+define('PEAR_TASK_ERROR_INVALID', 4);
+/**#@-*/
+define('PEAR_TASK_PACKAGE', 1);
+define('PEAR_TASK_INSTALL', 2);
+define('PEAR_TASK_PACKAGEANDINSTALL', 3);
+/**
+ * A task is an operation that manipulates the contents of a file.
+ *
+ * Simple tasks operate on 1 file.  Multiple tasks are executed after all files have been
+ * processed and installed, and are designed to operate on all files containing the task.
+ * The Post-install script task simply takes advantage of the fact that it will be run
+ * after installation, replace is a simple task.
+ *
+ * Combining tasks is possible, but ordering is significant.
+ *
+ * <file name="test.php" role="php">
+ *  <tasks:replace from="@data-dir@" to="data_dir" type="pear-config"/>
+ *  <tasks:postinstallscript/>
+ * </file>
+ *
+ * This will first replace any instance of @data-dir@ in the test.php file
+ * with the path to the current data directory.  Then, it will include the
+ * test.php file and run the script it contains to configure the package post-installation.
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ * @abstract
+ */
+class PEAR_Task_Common
+{
+    /**
+     * Valid types for this version are 'simple' and 'multiple'
+     *
+     * - simple tasks operate on the contents of a file and write out changes to disk
+     * - multiple tasks operate on the contents of many files and write out the
+     *   changes directly to disk
+     *
+     * Child task classes must override this property.
+     * @access protected
+     */
+    var $type = 'simple';
+    /**
+     * Determines which install phase this task is executed under
+     */
+    var $phase = PEAR_TASK_INSTALL;
+    /**
+     * @access protected
+     */
+    var $config;
+    /**
+     * @access protected
+     */
+    var $registry;
+    /**
+     * @access protected
+     */
+    var $logger;
+    /**
+     * @access protected
+     */
+    var $installphase;
+    /**
+     * @param PEAR_Config
+     * @param PEAR_Common
+     */
+    function PEAR_Task_Common(&$config, &$logger, $phase)
+    {
+        $this->config = &$config;
+        $this->registry = &$config->getRegistry();
+        $this->logger = &$logger;
+        $this->installphase = $phase;
+        if ($this->type == 'multiple') {
+            $GLOBALS['_PEAR_TASK_POSTINSTANCES'][get_class($this)][] = &$this;
+        }
+    }
+
+    /**
+     * Validate the basic contents of a task tag.
+     * @param PEAR_PackageFile_v2
+     * @param array
+     * @param PEAR_Config
+     * @param array the entire parsed <file> tag
+     * @return true|array On error, return an array in format:
+     *    array(PEAR_TASK_ERROR_???[, param1][, param2][, ...])
+     *
+     *    For PEAR_TASK_ERROR_MISSING_ATTRIB, pass the attribute name in
+     *    For PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, pass the attribute name and an array
+     *    of legal values in
+     * @static
+     * @abstract
+     */
+    function validateXml($pkg, $xml, $config, $fileXml)
+    {
+    }
+
+    /**
+     * Initialize a task instance with the parameters
+     * @param array raw, parsed xml
+     * @param array attributes from the <file> tag containing this task
+     * @param string|null last installed version of this package
+     * @abstract
+     */
+    function init($xml, $fileAttributes, $lastVersion)
+    {
+    }
+
+    /**
+     * Begin a task processing session.  All multiple tasks will be processed after each file
+     * has been successfully installed, all simple tasks should perform their task here and
+     * return any errors using the custom throwError() method to allow forward compatibility
+     *
+     * This method MUST NOT write out any changes to disk
+     * @param PEAR_PackageFile_v2
+     * @param string file contents
+     * @param string the eventual final file location (informational only)
+     * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
+     *         (use $this->throwError), otherwise return the new contents
+     * @abstract
+     */
+    function startSession($pkg, $contents, $dest)
+    {
+    }
+
+    /**
+     * This method is used to process each of the tasks for a particular multiple class
+     * type.  Simple tasks need not implement this method.
+     * @param array an array of tasks
+     * @access protected
+     * @static
+     * @abstract
+     */
+    function run($tasks)
+    {
+    }
+
+    /**
+     * @static
+     * @final
+     */
+    function hasPostinstallTasks()
+    {
+        return isset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
+    }
+
+    /**
+     * @static
+     * @final
+     */
+     function runPostinstallTasks()
+     {
+         foreach ($GLOBALS['_PEAR_TASK_POSTINSTANCES'] as $class => $tasks) {
+             $err = call_user_func(array($class, 'run'),
+                  $GLOBALS['_PEAR_TASK_POSTINSTANCES'][$class]);
+             if ($err) {
+                 return PEAR_Task_Common::throwError($err);
+             }
+         }
+         unset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
+    }
+
+    /**
+     * Determines whether a role is a script
+     * @return bool
+     */
+    function isScript()
+    {
+        return $this->type == 'script';
+    }
+
+    function throwError($msg, $code = -1)
+    {
+        include_once 'PEAR.php';
+        return PEAR::raiseError($msg, $code);
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/Packager.php
===================================================================
--- trunk/library/PEAR/PEAR/Packager.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Packager.php	(revision 5146)
@@ -0,0 +1,201 @@
+<?php
+/**
+ * PEAR_Packager for generating releases
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Packager.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Common.php';
+require_once 'PEAR/PackageFile.php';
+require_once 'System.php';
+
+/**
+ * Administration class used to make a PEAR release tarball.
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Packager extends PEAR_Common
+{
+    /**
+     * @var PEAR_Registry
+     */
+    var $_registry;
+
+    function package($pkgfile = null, $compress = true, $pkg2 = null)
+    {
+        // {{{ validate supplied package.xml file
+        if (empty($pkgfile)) {
+            $pkgfile = 'package.xml';
+        }
+
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $pkg  = &new PEAR_PackageFile($this->config, $this->debug);
+        $pf   = &$pkg->fromPackageFile($pkgfile, PEAR_VALIDATE_NORMAL);
+        $main = &$pf;
+        PEAR::staticPopErrorHandling();
+        if (PEAR::isError($pf)) {
+            if (is_array($pf->getUserInfo())) {
+                foreach ($pf->getUserInfo() as $error) {
+                    $this->log(0, 'Error: ' . $error['message']);
+                }
+            }
+
+            $this->log(0, $pf->getMessage());
+            return $this->raiseError("Cannot package, errors in package file");
+        }
+
+        foreach ($pf->getValidationWarnings() as $warning) {
+            $this->log(1, 'Warning: ' . $warning['message']);
+        }
+
+        // }}}
+        if ($pkg2) {
+            $this->log(0, 'Attempting to process the second package file');
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $pf2 = &$pkg->fromPackageFile($pkg2, PEAR_VALIDATE_NORMAL);
+            PEAR::staticPopErrorHandling();
+            if (PEAR::isError($pf2)) {
+                if (is_array($pf2->getUserInfo())) {
+                    foreach ($pf2->getUserInfo() as $error) {
+                        $this->log(0, 'Error: ' . $error['message']);
+                    }
+                }
+                $this->log(0, $pf2->getMessage());
+                return $this->raiseError("Cannot package, errors in second package file");
+            }
+
+            foreach ($pf2->getValidationWarnings() as $warning) {
+                $this->log(1, 'Warning: ' . $warning['message']);
+            }
+
+            if ($pf2->getPackagexmlVersion() == '2.0' ||
+                  $pf2->getPackagexmlVersion() == '2.1'
+            ) {
+                $main  = &$pf2;
+                $other = &$pf;
+            } else {
+                $main  = &$pf;
+                $other = &$pf2;
+            }
+
+            if ($main->getPackagexmlVersion() != '2.0' &&
+                  $main->getPackagexmlVersion() != '2.1') {
+                return PEAR::raiseError('Error: cannot package two package.xml version 1.0, can ' .
+                    'only package together a package.xml 1.0 and package.xml 2.0');
+            }
+
+            if ($other->getPackagexmlVersion() != '1.0') {
+                return PEAR::raiseError('Error: cannot package two package.xml version 2.0, can ' .
+                    'only package together a package.xml 1.0 and package.xml 2.0');
+            }
+        }
+
+        $main->setLogger($this);
+        if (!$main->validate(PEAR_VALIDATE_PACKAGING)) {
+            foreach ($main->getValidationWarnings() as $warning) {
+                $this->log(0, 'Error: ' . $warning['message']);
+            }
+            return $this->raiseError("Cannot package, errors in package");
+        }
+
+        foreach ($main->getValidationWarnings() as $warning) {
+            $this->log(1, 'Warning: ' . $warning['message']);
+        }
+
+        if ($pkg2) {
+            $other->setLogger($this);
+            $a = false;
+            if (!$other->validate(PEAR_VALIDATE_NORMAL) || $a = !$main->isEquivalent($other)) {
+                foreach ($other->getValidationWarnings() as $warning) {
+                    $this->log(0, 'Error: ' . $warning['message']);
+                }
+
+                foreach ($main->getValidationWarnings() as $warning) {
+                    $this->log(0, 'Error: ' . $warning['message']);
+                }
+
+                if ($a) {
+                    return $this->raiseError('The two package.xml files are not equivalent!');
+                }
+
+                return $this->raiseError("Cannot package, errors in package");
+            }
+
+            foreach ($other->getValidationWarnings() as $warning) {
+                $this->log(1, 'Warning: ' . $warning['message']);
+            }
+
+            $gen = &$main->getDefaultGenerator();
+            $tgzfile = $gen->toTgz2($this, $other, $compress);
+            if (PEAR::isError($tgzfile)) {
+                return $tgzfile;
+            }
+
+            $dest_package = basename($tgzfile);
+            $pkgdir       = dirname($pkgfile);
+
+            // TAR the Package -------------------------------------------
+            $this->log(1, "Package $dest_package done");
+            if (file_exists("$pkgdir/CVS/Root")) {
+                $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion());
+                $cvstag = "RELEASE_$cvsversion";
+                $this->log(1, 'Tag the released code with "pear cvstag ' .
+                    $main->getPackageFile() . '"');
+                $this->log(1, "(or set the CVS tag $cvstag by hand)");
+            } elseif (file_exists("$pkgdir/.svn")) {
+                $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion());
+                $svntag = $pf->getName() . "-$svnversion";
+                $this->log(1, 'Tag the released code with "pear svntag ' .
+                    $main->getPackageFile() . '"');
+                $this->log(1, "(or set the SVN tag $svntag by hand)");
+            }
+        } else { // this branch is executed for single packagefile packaging
+            $gen = &$pf->getDefaultGenerator();
+            $tgzfile = $gen->toTgz($this, $compress);
+            if (PEAR::isError($tgzfile)) {
+                $this->log(0, $tgzfile->getMessage());
+                return $this->raiseError("Cannot package, errors in package");
+            }
+
+            $dest_package = basename($tgzfile);
+            $pkgdir       = dirname($pkgfile);
+
+            // TAR the Package -------------------------------------------
+            $this->log(1, "Package $dest_package done");
+            if (file_exists("$pkgdir/CVS/Root")) {
+                $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion());
+                $cvstag = "RELEASE_$cvsversion";
+                $this->log(1, "Tag the released code with `pear cvstag $pkgfile'");
+                $this->log(1, "(or set the CVS tag $cvstag by hand)");
+            } elseif (file_exists("$pkgdir/.svn")) {
+                $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion());
+                $svntag = $pf->getName() . "-$svnversion";
+                $this->log(1, "Tag the released code with `pear svntag $pkgfile'");
+                $this->log(1, "(or set the SVN tag $svntag by hand)");
+            }
+        }
+
+        return $dest_package;
+    }
+}
Index: trunk/library/PEAR/PEAR/REST.php
===================================================================
--- trunk/library/PEAR/PEAR/REST.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/REST.php	(revision 5146)
@@ -0,0 +1,483 @@
+<?php
+/**
+ * PEAR_REST
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: REST.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * For downloading xml files
+ */
+require_once 'PEAR.php';
+require_once 'PEAR/XMLParser.php';
+
+/**
+ * Intelligently retrieve data, following hyperlinks if necessary, and re-directing
+ * as well
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_REST
+{
+    var $config;
+    var $_options;
+
+    function PEAR_REST(&$config, $options = array())
+    {
+        $this->config   = &$config;
+        $this->_options = $options;
+    }
+
+    /**
+     * Retrieve REST data, but always retrieve the local cache if it is available.
+     *
+     * This is useful for elements that should never change, such as information on a particular
+     * release
+     * @param string full URL to this resource
+     * @param array|false contents of the accept-encoding header
+     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
+     *                    parsed using PEAR_XMLParser
+     * @return string|array
+     */
+    function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false)
+    {
+        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
+            md5($url) . 'rest.cachefile';
+
+        if (file_exists($cachefile)) {
+            return unserialize(implode('', file($cachefile)));
+        }
+
+        return $this->retrieveData($url, $accept, $forcestring, $channel);
+    }
+
+    /**
+     * Retrieve a remote REST resource
+     * @param string full URL to this resource
+     * @param array|false contents of the accept-encoding header
+     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
+     *                    parsed using PEAR_XMLParser
+     * @return string|array
+     */
+    function retrieveData($url, $accept = false, $forcestring = false, $channel = false)
+    {
+        $cacheId = $this->getCacheId($url);
+        if ($ret = $this->useLocalCache($url, $cacheId)) {
+            return $ret;
+        }
+
+        $file = $trieddownload = false;
+        if (!isset($this->_options['offline'])) {
+            $trieddownload = true;
+            $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel);
+        }
+
+        if (PEAR::isError($file)) {
+            if ($file->getCode() !== -9276) {
+                return $file;
+            }
+
+            $trieddownload = false;
+            $file = false; // use local copy if available on socket connect error
+        }
+
+        if (!$file) {
+            $ret = $this->getCache($url);
+            if (!PEAR::isError($ret) && $trieddownload) {
+                // reset the age of the cache if the server says it was unmodified
+                $result = $this->saveCache($url, $ret, null, true, $cacheId);
+                if (PEAR::isError($result)) {
+                    return PEAR::raiseError($result->getMessage());
+                }
+            }
+
+            return $ret;
+        }
+
+        if (is_array($file)) {
+            $headers      = $file[2];
+            $lastmodified = $file[1];
+            $content      = $file[0];
+        } else {
+            $headers      = array();
+            $lastmodified = false;
+            $content      = $file;
+        }
+
+        if ($forcestring) {
+            $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId);
+            if (PEAR::isError($result)) {
+                return PEAR::raiseError($result->getMessage());
+            }
+
+            return $content;
+        }
+
+        if (isset($headers['content-type'])) {
+            switch ($headers['content-type']) {
+                case 'text/xml' :
+                case 'application/xml' :
+                case 'text/plain' :
+                    if ($headers['content-type'] === 'text/plain') {
+                        $check = substr($content, 0, 5);
+                        if ($check !== '<?xml') {
+                            break;
+                        }
+                    }
+
+                    $parser = new PEAR_XMLParser;
+                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+                    $err = $parser->parse($content);
+                    PEAR::popErrorHandling();
+                    if (PEAR::isError($err)) {
+                        return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
+                            $err->getMessage());
+                    }
+                    $content = $parser->getData();
+                case 'text/html' :
+                default :
+                    // use it as a string
+            }
+        } else {
+            // assume XML
+            $parser = new PEAR_XMLParser;
+            $parser->parse($content);
+            $content = $parser->getData();
+        }
+
+        $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId);
+        if (PEAR::isError($result)) {
+            return PEAR::raiseError($result->getMessage());
+        }
+
+        return $content;
+    }
+
+    function useLocalCache($url, $cacheid = null)
+    {
+        if ($cacheid === null) {
+            $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
+                md5($url) . 'rest.cacheid';
+            if (!file_exists($cacheidfile)) {
+                return false;
+            }
+
+            $cacheid = unserialize(implode('', file($cacheidfile)));
+        }
+
+        $cachettl = $this->config->get('cache_ttl');
+        // If cache is newer than $cachettl seconds, we use the cache!
+        if (time() - $cacheid['age'] < $cachettl) {
+            return $this->getCache($url);
+        }
+
+        return false;
+    }
+
+    function getCacheId($url)
+    {
+        $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
+            md5($url) . 'rest.cacheid';
+
+        if (!file_exists($cacheidfile)) {
+            return false;
+        }
+
+        $ret = unserialize(implode('', file($cacheidfile)));
+        return $ret;
+    }
+
+    function getCache($url)
+    {
+        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
+            md5($url) . 'rest.cachefile';
+
+        if (!file_exists($cachefile)) {
+            return PEAR::raiseError('No cached content available for "' . $url . '"');
+        }
+
+        return unserialize(implode('', file($cachefile)));
+    }
+
+    /**
+     * @param string full URL to REST resource
+     * @param string original contents of the REST resource
+     * @param array  HTTP Last-Modified and ETag headers
+     * @param bool   if true, then the cache id file should be regenerated to
+     *               trigger a new time-to-live value
+     */
+    function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null)
+    {
+        $cache_dir   = $this->config->get('cache_dir');
+        $d           = $cache_dir . DIRECTORY_SEPARATOR . md5($url);
+        $cacheidfile = $d . 'rest.cacheid';
+        $cachefile   = $d . 'rest.cachefile';
+
+        if (!is_dir($cache_dir)) {
+            if (System::mkdir(array('-p', $cache_dir)) === false) {
+              return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed.");
+            }
+        }
+
+        if ($cacheid === null && $nochange) {
+            $cacheid = unserialize(implode('', file($cacheidfile)));
+        }
+
+        $idData = serialize(array(
+            'age'        => time(),
+            'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified),
+        ));
+
+        $result = $this->saveCacheFile($cacheidfile, $idData);
+        if (PEAR::isError($result)) {
+            return $result;
+        } elseif ($nochange) {
+            return true;
+        }
+
+        $result = $this->saveCacheFile($cachefile, serialize($contents));
+        if (PEAR::isError($result)) {
+            if (file_exists($cacheidfile)) {
+              @unlink($cacheidfile);
+            }
+
+            return $result;
+        }
+
+        return true;
+    }
+
+    function saveCacheFile($file, $contents)
+    {
+        $len = strlen($contents);
+
+        $cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode
+        if ($cachefile_fp !== false) { // create file
+            if (fwrite($cachefile_fp, $contents, $len) < $len) {
+                fclose($cachefile_fp);
+                return PEAR::raiseError("Could not write $file.");
+            }
+        } else { // update file
+            $cachefile_lstat = lstat($file);
+            $cachefile_fp = @fopen($file, 'wb');
+            if (!$cachefile_fp) {
+                return PEAR::raiseError("Could not open $file for writing.");
+            }
+
+            $cachefile_fstat = fstat($cachefile_fp);
+            if (
+              $cachefile_lstat['mode'] == $cachefile_fstat['mode'] &&
+              $cachefile_lstat['ino']  == $cachefile_fstat['ino'] &&
+              $cachefile_lstat['dev']  == $cachefile_fstat['dev'] &&
+              $cachefile_fstat['nlink'] === 1
+            ) {
+                if (fwrite($cachefile_fp, $contents, $len) < $len) {
+                    fclose($cachefile_fp);
+                    return PEAR::raiseError("Could not write $file.");
+                }
+            } else {
+                fclose($cachefile_fp);
+                $link = function_exists('readlink') ? readlink($file) : $file;
+                return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack');
+            }
+        }
+
+        fclose($cachefile_fp);
+        return true;
+    }
+
+    /**
+     * Efficiently Download a file through HTTP.  Returns downloaded file as a string in-memory
+     * This is best used for small files
+     *
+     * If an HTTP proxy has been configured (http_proxy PEAR_Config
+     * setting), the proxy will be used.
+     *
+     * @param string  $url       the URL to download
+     * @param string  $save_dir  directory to save file in
+     * @param false|string|array $lastmodified header values to check against for caching
+     *                           use false to return the header values from this download
+     * @param false|array $accept Accept headers to send
+     * @return string|array  Returns the contents of the downloaded file or a PEAR
+     *                       error on failure.  If the error is caused by
+     *                       socket-related errors, the error object will
+     *                       have the fsockopen error code available through
+     *                       getCode().  If caching is requested, then return the header
+     *                       values.
+     *
+     * @access public
+     */
+    function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false)
+    {
+        static $redirect = 0;
+        // always reset , so we are clean case of error
+        $wasredirect = $redirect;
+        $redirect = 0;
+
+        $info = parse_url($url);
+        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
+            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
+        }
+
+        if (!isset($info['host'])) {
+            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
+        }
+
+        $host   = isset($info['host']) ? $info['host'] : null;
+        $port   = isset($info['port']) ? $info['port'] : null;
+        $path   = isset($info['path']) ? $info['path'] : null;
+        $schema = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
+
+        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+        if ($this->config->get('http_proxy')&&
+              $proxy = parse_url($this->config->get('http_proxy'))
+        ) {
+            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
+            if ($schema === 'https') {
+                $proxy_host = 'ssl://' . $proxy_host;
+            }
+
+            $proxy_port   = isset($proxy['port']) ? $proxy['port'] : 8080;
+            $proxy_user   = isset($proxy['user']) ? urldecode($proxy['user']) : null;
+            $proxy_pass   = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
+            $proxy_schema = (isset($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http';
+        }
+
+        if (empty($port)) {
+            $port = (isset($info['scheme']) && $info['scheme'] == 'https')  ? 443 : 80;
+        }
+
+        if (isset($proxy['host'])) {
+            $request = "GET $url HTTP/1.1\r\n";
+        } else {
+            $request = "GET $path HTTP/1.1\r\n";
+        }
+
+        $request .= "Host: $host\r\n";
+        $ifmodifiedsince = '';
+        if (is_array($lastmodified)) {
+            if (isset($lastmodified['Last-Modified'])) {
+                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
+            }
+
+            if (isset($lastmodified['ETag'])) {
+                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
+            }
+        } else {
+            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
+        }
+
+        $request .= $ifmodifiedsince .
+            "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n";
+
+        $username = $this->config->get('username', null, $channel);
+        $password = $this->config->get('password', null, $channel);
+
+        if ($username && $password) {
+            $tmp = base64_encode("$username:$password");
+            $request .= "Authorization: Basic $tmp\r\n";
+        }
+
+        if ($proxy_host != '' && $proxy_user != '') {
+            $request .= 'Proxy-Authorization: Basic ' .
+                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
+        }
+
+        if ($accept) {
+            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
+        }
+
+        $request .= "Accept-Encoding:\r\n";
+        $request .= "Connection: close\r\n";
+        $request .= "\r\n";
+
+        if ($proxy_host != '') {
+            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15);
+            if (!$fp) {
+                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", -9276);
+            }
+        } else {
+            if ($schema === 'https') {
+                $host = 'ssl://' . $host;
+            }
+
+            $fp = @fsockopen($host, $port, $errno, $errstr);
+            if (!$fp) {
+                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
+            }
+        }
+
+        fwrite($fp, $request);
+
+        $headers = array();
+        $reply   = 0;
+        while ($line = trim(fgets($fp, 1024))) {
+            if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
+                $headers[strtolower($matches[1])] = trim($matches[2]);
+            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
+                $reply = (int)$matches[1];
+                if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
+                    return false;
+                }
+
+                if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) {
+                    return PEAR::raiseError("File $schema://$host:$port$path not valid (received: $line)");
+                }
+            }
+        }
+
+        if ($reply != 200) {
+            if (!isset($headers['location'])) {
+                return PEAR::raiseError("File $schema://$host:$port$path not valid (redirected but no location)");
+            }
+
+            if ($wasredirect > 4) {
+                return PEAR::raiseError("File $schema://$host:$port$path not valid (redirection looped more than 5 times)");
+            }
+
+            $redirect = $wasredirect + 1;
+            return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel);
+        }
+
+        $length = isset($headers['content-length']) ? $headers['content-length'] : -1;
+
+        $data = '';
+        while ($chunk = @fread($fp, 8192)) {
+            $data .= $chunk;
+        }
+        fclose($fp);
+
+        if ($lastmodified === false || $lastmodified) {
+            if (isset($headers['etag'])) {
+                $lastmodified = array('ETag' => $headers['etag']);
+            }
+
+            if (isset($headers['last-modified'])) {
+                if (is_array($lastmodified)) {
+                    $lastmodified['Last-Modified'] = $headers['last-modified'];
+                } else {
+                    $lastmodified = $headers['last-modified'];
+                }
+            }
+
+            return array($data, $lastmodified, $headers);
+        }
+
+        return $data;
+    }
+}
Index: trunk/library/PEAR/PEAR/XMLParser.php
===================================================================
--- trunk/library/PEAR/PEAR/XMLParser.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/XMLParser.php	(revision 5146)
@@ -0,0 +1,253 @@
+<?php
+/**
+ * PEAR_XMLParser
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @author     Stephan Schmidt (original XML_Unserializer code)
+ * @copyright  1997-2009 The Authors
+ * @license   http://opensource.org/licenses/bsd-license New BSD License
+ * @version    CVS: $Id: XMLParser.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * Parser for any xml file
+ * @category  pear
+ * @package   PEAR
+ * @author    Greg Beaver <cellog@php.net>
+ * @author    Stephan Schmidt (original XML_Unserializer code)
+ * @copyright 1997-2009 The Authors
+ * @license   http://opensource.org/licenses/bsd-license New BSD License
+ * @version   Release: 1.9.4
+ * @link      http://pear.php.net/package/PEAR
+ * @since     Class available since Release 1.4.0a1
+ */
+class PEAR_XMLParser
+{
+    /**
+     * unserilialized data
+     * @var string $_serializedData
+     */
+    var $_unserializedData = null;
+
+    /**
+     * name of the root tag
+     * @var string $_root
+     */
+    var $_root = null;
+
+    /**
+     * stack for all data that is found
+     * @var array    $_dataStack
+     */
+    var $_dataStack = array();
+
+    /**
+     * stack for all values that are generated
+     * @var array    $_valStack
+     */
+    var $_valStack = array();
+
+    /**
+     * current tag depth
+     * @var int    $_depth
+     */
+    var $_depth = 0;
+
+    /**
+     * The XML encoding to use
+     * @var string $encoding
+     */
+    var $encoding = 'ISO-8859-1';
+
+    /**
+     * @return array
+     */
+    function getData()
+    {
+        return $this->_unserializedData;
+    }
+
+    /**
+     * @param string xml content
+     * @return true|PEAR_Error
+     */
+    function parse($data)
+    {
+        if (!extension_loaded('xml')) {
+            include_once 'PEAR.php';
+            return PEAR::raiseError("XML Extension not found", 1);
+        }
+        $this->_dataStack =  $this->_valStack = array();
+        $this->_depth = 0;
+
+        if (
+            strpos($data, 'encoding="UTF-8"')
+            || strpos($data, 'encoding="utf-8"')
+            || strpos($data, "encoding='UTF-8'")
+            || strpos($data, "encoding='utf-8'")
+        ) {
+            $this->encoding = 'UTF-8';
+        }
+
+        if (version_compare(phpversion(), '5.0.0', 'lt') && $this->encoding == 'UTF-8') {
+            $data = utf8_decode($data);
+            $this->encoding = 'ISO-8859-1';
+        }
+
+        $xp = xml_parser_create($this->encoding);
+        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0);
+        xml_set_object($xp, $this);
+        xml_set_element_handler($xp, 'startHandler', 'endHandler');
+        xml_set_character_data_handler($xp, 'cdataHandler');
+        if (!xml_parse($xp, $data)) {
+            $msg = xml_error_string(xml_get_error_code($xp));
+            $line = xml_get_current_line_number($xp);
+            xml_parser_free($xp);
+            include_once 'PEAR.php';
+            return PEAR::raiseError("XML Error: '$msg' on line '$line'", 2);
+        }
+        xml_parser_free($xp);
+        return true;
+    }
+
+    /**
+     * Start element handler for XML parser
+     *
+     * @access private
+     * @param  object $parser  XML parser object
+     * @param  string $element XML element
+     * @param  array  $attribs attributes of XML tag
+     * @return void
+     */
+    function startHandler($parser, $element, $attribs)
+    {
+        $this->_depth++;
+        $this->_dataStack[$this->_depth] = null;
+
+        $val = array(
+            'name'         => $element,
+            'value'        => null,
+            'type'         => 'string',
+            'childrenKeys' => array(),
+            'aggregKeys'   => array()
+       );
+
+        if (count($attribs) > 0) {
+            $val['children'] = array();
+            $val['type'] = 'array';
+            $val['children']['attribs'] = $attribs;
+        }
+
+        array_push($this->_valStack, $val);
+    }
+
+    /**
+     * post-process data
+     *
+     * @param string $data
+     * @param string $element element name
+     */
+    function postProcess($data, $element)
+    {
+        return trim($data);
+    }
+
+    /**
+     * End element handler for XML parser
+     *
+     * @access private
+     * @param  object XML parser object
+     * @param  string
+     * @return void
+     */
+    function endHandler($parser, $element)
+    {
+        $value = array_pop($this->_valStack);
+        $data  = $this->postProcess($this->_dataStack[$this->_depth], $element);
+
+        // adjust type of the value
+        switch (strtolower($value['type'])) {
+            // unserialize an array
+            case 'array':
+                if ($data !== '') {
+                    $value['children']['_content'] = $data;
+                }
+
+                $value['value'] = isset($value['children']) ? $value['children'] : array();
+                break;
+
+            /*
+             * unserialize a null value
+             */
+            case 'null':
+                $data = null;
+                break;
+
+            /*
+             * unserialize any scalar value
+             */
+            default:
+                settype($data, $value['type']);
+                $value['value'] = $data;
+                break;
+        }
+
+        $parent = array_pop($this->_valStack);
+        if ($parent === null) {
+            $this->_unserializedData = &$value['value'];
+            $this->_root = &$value['name'];
+            return true;
+        }
+
+        // parent has to be an array
+        if (!isset($parent['children']) || !is_array($parent['children'])) {
+            $parent['children'] = array();
+            if ($parent['type'] != 'array') {
+                $parent['type'] = 'array';
+            }
+        }
+
+        if (!empty($value['name'])) {
+            // there already has been a tag with this name
+            if (in_array($value['name'], $parent['childrenKeys'])) {
+                // no aggregate has been created for this tag
+                if (!in_array($value['name'], $parent['aggregKeys'])) {
+                    if (isset($parent['children'][$value['name']])) {
+                        $parent['children'][$value['name']] = array($parent['children'][$value['name']]);
+                    } else {
+                        $parent['children'][$value['name']] = array();
+                    }
+                    array_push($parent['aggregKeys'], $value['name']);
+                }
+                array_push($parent['children'][$value['name']], $value['value']);
+            } else {
+                $parent['children'][$value['name']] = &$value['value'];
+                array_push($parent['childrenKeys'], $value['name']);
+            }
+        } else {
+            array_push($parent['children'],$value['value']);
+        }
+        array_push($this->_valStack, $parent);
+
+        $this->_depth--;
+    }
+
+    /**
+     * Handler for character data
+     *
+     * @access private
+     * @param  object XML parser object
+     * @param  string CDATA
+     * @return void
+     */
+    function cdataHandler($parser, $cdata)
+    {
+        $this->_dataStack[$this->_depth] .= $cdata;
+    }
+}
Index: trunk/library/PEAR/PEAR/Command.php
===================================================================
--- trunk/library/PEAR/PEAR/Command.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command.php	(revision 5146)
@@ -0,0 +1,414 @@
+<?php
+/**
+ * PEAR_Command, command pattern class
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Command.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * Needed for error handling
+ */
+require_once 'PEAR.php';
+require_once 'PEAR/Frontend.php';
+require_once 'PEAR/XMLParser.php';
+
+/**
+ * List of commands and what classes they are implemented in.
+ * @var array command => implementing class
+ */
+$GLOBALS['_PEAR_Command_commandlist'] = array();
+
+/**
+ * List of commands and their descriptions
+ * @var array command => description
+ */
+$GLOBALS['_PEAR_Command_commanddesc'] = array();
+
+/**
+ * List of shortcuts to common commands.
+ * @var array shortcut => command
+ */
+$GLOBALS['_PEAR_Command_shortcuts'] = array();
+
+/**
+ * Array of command objects
+ * @var array class => object
+ */
+$GLOBALS['_PEAR_Command_objects'] = array();
+
+/**
+ * PEAR command class, a simple factory class for administrative
+ * commands.
+ *
+ * How to implement command classes:
+ *
+ * - The class must be called PEAR_Command_Nnn, installed in the
+ *   "PEAR/Common" subdir, with a method called getCommands() that
+ *   returns an array of the commands implemented by the class (see
+ *   PEAR/Command/Install.php for an example).
+ *
+ * - The class must implement a run() function that is called with three
+ *   params:
+ *
+ *    (string) command name
+ *    (array)  assoc array with options, freely defined by each
+ *             command, for example:
+ *             array('force' => true)
+ *    (array)  list of the other parameters
+ *
+ *   The run() function returns a PEAR_CommandResponse object.  Use
+ *   these methods to get information:
+ *
+ *    int getStatus()   Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL)
+ *                      *_PARTIAL means that you need to issue at least
+ *                      one more command to complete the operation
+ *                      (used for example for validation steps).
+ *
+ *    string getMessage()  Returns a message for the user.  Remember,
+ *                         no HTML or other interface-specific markup.
+ *
+ *   If something unexpected happens, run() returns a PEAR error.
+ *
+ * - DON'T OUTPUT ANYTHING! Return text for output instead.
+ *
+ * - DON'T USE HTML! The text you return will be used from both Gtk,
+ *   web and command-line interfaces, so for now, keep everything to
+ *   plain text.
+ *
+ * - DON'T USE EXIT OR DIE! Always use pear errors.  From static
+ *   classes do PEAR::raiseError(), from other classes do
+ *   $this->raiseError().
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Command
+{
+    // {{{ factory()
+
+    /**
+     * Get the right object for executing a command.
+     *
+     * @param string $command The name of the command
+     * @param object $config  Instance of PEAR_Config object
+     *
+     * @return object the command object or a PEAR error
+     *
+     * @access public
+     * @static
+     */
+    function &factory($command, &$config)
+    {
+        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+            PEAR_Command::registerCommands();
+        }
+        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
+            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
+        }
+        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
+            $a = PEAR::raiseError("unknown command `$command'");
+            return $a;
+        }
+        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
+        if (!class_exists($class)) {
+            require_once $GLOBALS['_PEAR_Command_objects'][$class];
+        }
+        if (!class_exists($class)) {
+            $a = PEAR::raiseError("unknown command `$command'");
+            return $a;
+        }
+        $ui =& PEAR_Command::getFrontendObject();
+        $obj = &new $class($ui, $config);
+        return $obj;
+    }
+
+    // }}}
+    // {{{ & getObject()
+    function &getObject($command)
+    {
+        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
+        if (!class_exists($class)) {
+            require_once $GLOBALS['_PEAR_Command_objects'][$class];
+        }
+        if (!class_exists($class)) {
+            return PEAR::raiseError("unknown command `$command'");
+        }
+        $ui =& PEAR_Command::getFrontendObject();
+        $config = &PEAR_Config::singleton();
+        $obj = &new $class($ui, $config);
+        return $obj;
+    }
+
+    // }}}
+    // {{{ & getFrontendObject()
+
+    /**
+     * Get instance of frontend object.
+     *
+     * @return object|PEAR_Error
+     * @static
+     */
+    function &getFrontendObject()
+    {
+        $a = &PEAR_Frontend::singleton();
+        return $a;
+    }
+
+    // }}}
+    // {{{ & setFrontendClass()
+
+    /**
+     * Load current frontend class.
+     *
+     * @param string $uiclass Name of class implementing the frontend
+     *
+     * @return object the frontend object, or a PEAR error
+     * @static
+     */
+    function &setFrontendClass($uiclass)
+    {
+        $a = &PEAR_Frontend::setFrontendClass($uiclass);
+        return $a;
+    }
+
+    // }}}
+    // {{{ setFrontendType()
+
+    /**
+     * Set current frontend.
+     *
+     * @param string $uitype Name of the frontend type (for example "CLI")
+     *
+     * @return object the frontend object, or a PEAR error
+     * @static
+     */
+    function setFrontendType($uitype)
+    {
+        $uiclass = 'PEAR_Frontend_' . $uitype;
+        return PEAR_Command::setFrontendClass($uiclass);
+    }
+
+    // }}}
+    // {{{ registerCommands()
+
+    /**
+     * Scan through the Command directory looking for classes
+     * and see what commands they implement.
+     *
+     * @param bool   (optional) if FALSE (default), the new list of
+     *               commands should replace the current one.  If TRUE,
+     *               new entries will be merged with old.
+     *
+     * @param string (optional) where (what directory) to look for
+     *               classes, defaults to the Command subdirectory of
+     *               the directory from where this file (__FILE__) is
+     *               included.
+     *
+     * @return bool TRUE on success, a PEAR error on failure
+     *
+     * @access public
+     * @static
+     */
+    function registerCommands($merge = false, $dir = null)
+    {
+        $parser = new PEAR_XMLParser;
+        if ($dir === null) {
+            $dir = dirname(__FILE__) . '/Command';
+        }
+        if (!is_dir($dir)) {
+            return PEAR::raiseError("registerCommands: opendir($dir) '$dir' does not exist or is not a directory");
+        }
+        $dp = @opendir($dir);
+        if (empty($dp)) {
+            return PEAR::raiseError("registerCommands: opendir($dir) failed");
+        }
+        if (!$merge) {
+            $GLOBALS['_PEAR_Command_commandlist'] = array();
+        }
+
+        while ($file = readdir($dp)) {
+            if ($file{0} == '.' || substr($file, -4) != '.xml') {
+                continue;
+            }
+
+            $f = substr($file, 0, -4);
+            $class = "PEAR_Command_" . $f;
+            // List of commands
+            if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
+                $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . $f . '.php';
+            }
+
+            $parser->parse(file_get_contents("$dir/$file"));
+            $implements = $parser->getData();
+            foreach ($implements as $command => $desc) {
+                if ($command == 'attribs') {
+                    continue;
+                }
+
+                if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
+                    return PEAR::raiseError('Command "' . $command . '" already registered in ' .
+                        'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
+                }
+
+                $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
+                $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary'];
+                if (isset($desc['shortcut'])) {
+                    $shortcut = $desc['shortcut'];
+                    if (isset($GLOBALS['_PEAR_Command_shortcuts'][$shortcut])) {
+                        return PEAR::raiseError('Command shortcut "' . $shortcut . '" already ' .
+                            'registered to command "' . $command . '" in class "' .
+                            $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
+                    }
+                    $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command;
+                }
+
+                if (isset($desc['options']) && $desc['options']) {
+                    foreach ($desc['options'] as $oname => $option) {
+                        if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) {
+                            return PEAR::raiseError('Option "' . $oname . '" short option "' .
+                                $option['shortopt'] . '" must be ' .
+                                'only 1 character in Command "' . $command . '" in class "' .
+                                $class . '"');
+                        }
+                    }
+                }
+            }
+        }
+
+        ksort($GLOBALS['_PEAR_Command_shortcuts']);
+        ksort($GLOBALS['_PEAR_Command_commandlist']);
+        @closedir($dp);
+        return true;
+    }
+
+    // }}}
+    // {{{ getCommands()
+
+    /**
+     * Get the list of currently supported commands, and what
+     * classes implement them.
+     *
+     * @return array command => implementing class
+     *
+     * @access public
+     * @static
+     */
+    function getCommands()
+    {
+        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+            PEAR_Command::registerCommands();
+        }
+        return $GLOBALS['_PEAR_Command_commandlist'];
+    }
+
+    // }}}
+    // {{{ getShortcuts()
+
+    /**
+     * Get the list of command shortcuts.
+     *
+     * @return array shortcut => command
+     *
+     * @access public
+     * @static
+     */
+    function getShortcuts()
+    {
+        if (empty($GLOBALS['_PEAR_Command_shortcuts'])) {
+            PEAR_Command::registerCommands();
+        }
+        return $GLOBALS['_PEAR_Command_shortcuts'];
+    }
+
+    // }}}
+    // {{{ getGetoptArgs()
+
+    /**
+     * Compiles arguments for getopt.
+     *
+     * @param string $command     command to get optstring for
+     * @param string $short_args  (reference) short getopt format
+     * @param array  $long_args   (reference) long getopt format
+     *
+     * @return void
+     *
+     * @access public
+     * @static
+     */
+    function getGetoptArgs($command, &$short_args, &$long_args)
+    {
+        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+            PEAR_Command::registerCommands();
+        }
+        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
+            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
+        }
+        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
+            return null;
+        }
+        $obj = &PEAR_Command::getObject($command);
+        return $obj->getGetoptArgs($command, $short_args, $long_args);
+    }
+
+    // }}}
+    // {{{ getDescription()
+
+    /**
+     * Get description for a command.
+     *
+     * @param  string $command Name of the command
+     *
+     * @return string command description
+     *
+     * @access public
+     * @static
+     */
+    function getDescription($command)
+    {
+        if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) {
+            return null;
+        }
+        return $GLOBALS['_PEAR_Command_commanddesc'][$command];
+    }
+
+    // }}}
+    // {{{ getHelp()
+
+    /**
+     * Get help for command.
+     *
+     * @param string $command Name of the command to return help for
+     *
+     * @access public
+     * @static
+     */
+    function getHelp($command)
+    {
+        $cmds = PEAR_Command::getCommands();
+        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
+            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
+        }
+        if (isset($cmds[$command])) {
+            $obj = &PEAR_Command::getObject($command);
+            return $obj->getHelp($command);
+        }
+        return false;
+    }
+    // }}}
+}
Index: trunk/library/PEAR/PEAR/ErrorStack.php
===================================================================
--- trunk/library/PEAR/PEAR/ErrorStack.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/ErrorStack.php	(revision 5146)
@@ -0,0 +1,985 @@
+<?php
+/**
+ * Error Stack Implementation
+ * 
+ * This is an incredibly simple implementation of a very complex error handling
+ * facility.  It contains the ability
+ * to track multiple errors from multiple packages simultaneously.  In addition,
+ * it can track errors of many levels, save data along with the error, context
+ * information such as the exact file, line number, class and function that
+ * generated the error, and if necessary, it can raise a traditional PEAR_Error.
+ * It has built-in support for PEAR::Log, to log errors as they occur
+ * 
+ * Since version 0.2alpha, it is also possible to selectively ignore errors,
+ * through the use of an error callback, see {@link pushCallback()}
+ * 
+ * Since version 0.3alpha, it is possible to specify the exception class
+ * returned from {@link push()}
+ *
+ * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class.  This can
+ * still be done quite handily in an error callback or by manipulating the returned array
+ * @category   Debugging
+ * @package    PEAR_ErrorStack
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  2004-2008 Greg Beaver
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: ErrorStack.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR_ErrorStack
+ */
+
+/**
+ * Singleton storage
+ * 
+ * Format:
+ * <pre>
+ * array(
+ *  'package1' => PEAR_ErrorStack object,
+ *  'package2' => PEAR_ErrorStack object,
+ *  ...
+ * )
+ * </pre>
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
+
+/**
+ * Global error callback (default)
+ * 
+ * This is only used if set to non-false.  * is the default callback for
+ * all packages, whereas specific packages may set a default callback
+ * for all instances, regardless of whether they are a singleton or not.
+ *
+ * To exclude non-singletons, only set the local callback for the singleton
+ * @see PEAR_ErrorStack::setDefaultCallback()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
+    '*' => false,
+);
+
+/**
+ * Global Log object (default)
+ * 
+ * This is only used if set to non-false.  Use to set a default log object for
+ * all stacks, regardless of instantiation order or location
+ * @see PEAR_ErrorStack::setDefaultLogger()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
+
+/**
+ * Global Overriding Callback
+ * 
+ * This callback will override any error callbacks that specific loggers have set.
+ * Use with EXTREME caution
+ * @see PEAR_ErrorStack::staticPushCallback()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
+
+/**#@+
+ * One of four possible return values from the error Callback
+ * @see PEAR_ErrorStack::_errorCallback()
+ */
+/**
+ * If this is returned, then the error will be both pushed onto the stack
+ * and logged.
+ */
+define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
+/**
+ * If this is returned, then the error will only be pushed onto the stack,
+ * and not logged.
+ */
+define('PEAR_ERRORSTACK_PUSH', 2);
+/**
+ * If this is returned, then the error will only be logged, but not pushed
+ * onto the error stack.
+ */
+define('PEAR_ERRORSTACK_LOG', 3);
+/**
+ * If this is returned, then the error is completely ignored.
+ */
+define('PEAR_ERRORSTACK_IGNORE', 4);
+/**
+ * If this is returned, then the error is logged and die() is called.
+ */
+define('PEAR_ERRORSTACK_DIE', 5);
+/**#@-*/
+
+/**
+ * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
+ * the singleton method.
+ */
+define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
+
+/**
+ * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
+ * that has no __toString() method
+ */
+define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
+/**
+ * Error Stack Implementation
+ *
+ * Usage:
+ * <code>
+ * // global error stack
+ * $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
+ * // local error stack
+ * $local_stack = new PEAR_ErrorStack('MyPackage');
+ * </code>
+ * @author     Greg Beaver <cellog@php.net>
+ * @version    1.9.4
+ * @package    PEAR_ErrorStack
+ * @category   Debugging
+ * @copyright  2004-2008 Greg Beaver
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: ErrorStack.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR_ErrorStack
+ */
+class PEAR_ErrorStack {
+    /**
+     * Errors are stored in the order that they are pushed on the stack.
+     * @since 0.4alpha Errors are no longer organized by error level.
+     * This renders pop() nearly unusable, and levels could be more easily
+     * handled in a callback anyway
+     * @var array
+     * @access private
+     */
+    var $_errors = array();
+
+    /**
+     * Storage of errors by level.
+     *
+     * Allows easy retrieval and deletion of only errors from a particular level
+     * @since PEAR 1.4.0dev
+     * @var array
+     * @access private
+     */
+    var $_errorsByLevel = array();
+
+    /**
+     * Package name this error stack represents
+     * @var string
+     * @access protected
+     */
+    var $_package;
+    
+    /**
+     * Determines whether a PEAR_Error is thrown upon every error addition
+     * @var boolean
+     * @access private
+     */
+    var $_compat = false;
+    
+    /**
+     * If set to a valid callback, this will be used to generate the error
+     * message from the error code, otherwise the message passed in will be
+     * used
+     * @var false|string|array
+     * @access private
+     */
+    var $_msgCallback = false;
+    
+    /**
+     * If set to a valid callback, this will be used to generate the error
+     * context for an error.  For PHP-related errors, this will be a file
+     * and line number as retrieved from debug_backtrace(), but can be
+     * customized for other purposes.  The error might actually be in a separate
+     * configuration file, or in a database query.
+     * @var false|string|array
+     * @access protected
+     */
+    var $_contextCallback = false;
+    
+    /**
+     * If set to a valid callback, this will be called every time an error
+     * is pushed onto the stack.  The return value will be used to determine
+     * whether to allow an error to be pushed or logged.
+     * 
+     * The return value must be one an PEAR_ERRORSTACK_* constant
+     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+     * @var false|string|array
+     * @access protected
+     */
+    var $_errorCallback = array();
+    
+    /**
+     * PEAR::Log object for logging errors
+     * @var false|Log
+     * @access protected
+     */
+    var $_logger = false;
+    
+    /**
+     * Error messages - designed to be overridden
+     * @var array
+     * @abstract
+     */
+    var $_errorMsgs = array();
+    
+    /**
+     * Set up a new error stack
+     * 
+     * @param string   $package name of the package this error stack represents
+     * @param callback $msgCallback callback used for error message generation
+     * @param callback $contextCallback callback used for context generation,
+     *                 defaults to {@link getFileLine()}
+     * @param boolean  $throwPEAR_Error
+     */
+    function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false,
+                         $throwPEAR_Error = false)
+    {
+        $this->_package = $package;
+        $this->setMessageCallback($msgCallback);
+        $this->setContextCallback($contextCallback);
+        $this->_compat = $throwPEAR_Error;
+    }
+    
+    /**
+     * Return a single error stack for this package.
+     * 
+     * Note that all parameters are ignored if the stack for package $package
+     * has already been instantiated
+     * @param string   $package name of the package this error stack represents
+     * @param callback $msgCallback callback used for error message generation
+     * @param callback $contextCallback callback used for context generation,
+     *                 defaults to {@link getFileLine()}
+     * @param boolean  $throwPEAR_Error
+     * @param string   $stackClass class to instantiate
+     * @static
+     * @return PEAR_ErrorStack
+     */
+    function &singleton($package, $msgCallback = false, $contextCallback = false,
+                         $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack')
+    {
+        if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
+            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
+        }
+        if (!class_exists($stackClass)) {
+            if (function_exists('debug_backtrace')) {
+                $trace = debug_backtrace();
+            }
+            PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
+                'exception', array('stackclass' => $stackClass),
+                'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
+                false, $trace);
+        }
+        $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
+            new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);
+
+        return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
+    }
+
+    /**
+     * Internal error handler for PEAR_ErrorStack class
+     * 
+     * Dies if the error is an exception (and would have died anyway)
+     * @access private
+     */
+    function _handleError($err)
+    {
+        if ($err['level'] == 'exception') {
+            $message = $err['message'];
+            if (isset($_SERVER['REQUEST_URI'])) {
+                echo '<br />';
+            } else {
+                echo "\n";
+            }
+            var_dump($err['context']);
+            die($message);
+        }
+    }
+    
+    /**
+     * Set up a PEAR::Log object for all error stacks that don't have one
+     * @param Log $log 
+     * @static
+     */
+    function setDefaultLogger(&$log)
+    {
+        if (is_object($log) && method_exists($log, 'log') ) {
+            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
+        } elseif (is_callable($log)) {
+            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
+	}
+    }
+    
+    /**
+     * Set up a PEAR::Log object for this error stack
+     * @param Log $log 
+     */
+    function setLogger(&$log)
+    {
+        if (is_object($log) && method_exists($log, 'log') ) {
+            $this->_logger = &$log;
+        } elseif (is_callable($log)) {
+            $this->_logger = &$log;
+        }
+    }
+    
+    /**
+     * Set an error code => error message mapping callback
+     * 
+     * This method sets the callback that can be used to generate error
+     * messages for any instance
+     * @param array|string Callback function/method
+     */
+    function setMessageCallback($msgCallback)
+    {
+        if (!$msgCallback) {
+            $this->_msgCallback = array(&$this, 'getErrorMessage');
+        } else {
+            if (is_callable($msgCallback)) {
+                $this->_msgCallback = $msgCallback;
+            }
+        }
+    }
+    
+    /**
+     * Get an error code => error message mapping callback
+     * 
+     * This method returns the current callback that can be used to generate error
+     * messages
+     * @return array|string|false Callback function/method or false if none
+     */
+    function getMessageCallback()
+    {
+        return $this->_msgCallback;
+    }
+    
+    /**
+     * Sets a default callback to be used by all error stacks
+     * 
+     * This method sets the callback that can be used to generate error
+     * messages for a singleton
+     * @param array|string Callback function/method
+     * @param string Package name, or false for all packages
+     * @static
+     */
+    function setDefaultCallback($callback = false, $package = false)
+    {
+        if (!is_callable($callback)) {
+            $callback = false;
+        }
+        $package = $package ? $package : '*';
+        $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
+    }
+    
+    /**
+     * Set a callback that generates context information (location of error) for an error stack
+     * 
+     * This method sets the callback that can be used to generate context
+     * information for an error.  Passing in NULL will disable context generation
+     * and remove the expensive call to debug_backtrace()
+     * @param array|string|null Callback function/method
+     */
+    function setContextCallback($contextCallback)
+    {
+        if ($contextCallback === null) {
+            return $this->_contextCallback = false;
+        }
+        if (!$contextCallback) {
+            $this->_contextCallback = array(&$this, 'getFileLine');
+        } else {
+            if (is_callable($contextCallback)) {
+                $this->_contextCallback = $contextCallback;
+            }
+        }
+    }
+    
+    /**
+     * Set an error Callback
+     * If set to a valid callback, this will be called every time an error
+     * is pushed onto the stack.  The return value will be used to determine
+     * whether to allow an error to be pushed or logged.
+     * 
+     * The return value must be one of the ERRORSTACK_* constants.
+     * 
+     * This functionality can be used to emulate PEAR's pushErrorHandling, and
+     * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
+     * the error stack or logging
+     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+     * @see popCallback()
+     * @param string|array $cb
+     */
+    function pushCallback($cb)
+    {
+        array_push($this->_errorCallback, $cb);
+    }
+    
+    /**
+     * Remove a callback from the error callback stack
+     * @see pushCallback()
+     * @return array|string|false
+     */
+    function popCallback()
+    {
+        if (!count($this->_errorCallback)) {
+            return false;
+        }
+        return array_pop($this->_errorCallback);
+    }
+    
+    /**
+     * Set a temporary overriding error callback for every package error stack
+     *
+     * Use this to temporarily disable all existing callbacks (can be used
+     * to emulate the @ operator, for instance)
+     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+     * @see staticPopCallback(), pushCallback()
+     * @param string|array $cb
+     * @static
+     */
+    function staticPushCallback($cb)
+    {
+        array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
+    }
+    
+    /**
+     * Remove a temporary overriding error callback
+     * @see staticPushCallback()
+     * @return array|string|false
+     * @static
+     */
+    function staticPopCallback()
+    {
+        $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
+        if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
+            $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
+        }
+        return $ret;
+    }
+    
+    /**
+     * Add an error to the stack
+     * 
+     * If the message generator exists, it is called with 2 parameters.
+     *  - the current Error Stack object
+     *  - an array that is in the same format as an error.  Available indices
+     *    are 'code', 'package', 'time', 'params', 'level', and 'context'
+     * 
+     * Next, if the error should contain context information, this is
+     * handled by the context grabbing method.
+     * Finally, the error is pushed onto the proper error stack
+     * @param int    $code      Package-specific error code
+     * @param string $level     Error level.  This is NOT spell-checked
+     * @param array  $params    associative array of error parameters
+     * @param string $msg       Error message, or a portion of it if the message
+     *                          is to be generated
+     * @param array  $repackage If this error re-packages an error pushed by
+     *                          another package, place the array returned from
+     *                          {@link pop()} in this parameter
+     * @param array  $backtrace Protected parameter: use this to pass in the
+     *                          {@link debug_backtrace()} that should be used
+     *                          to find error context
+     * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
+     * thrown.  If a PEAR_Error is returned, the userinfo
+     * property is set to the following array:
+     * 
+     * <code>
+     * array(
+     *    'code' => $code,
+     *    'params' => $params,
+     *    'package' => $this->_package,
+     *    'level' => $level,
+     *    'time' => time(),
+     *    'context' => $context,
+     *    'message' => $msg,
+     * //['repackage' => $err] repackaged error array/Exception class
+     * );
+     * </code>
+     * 
+     * Normally, the previous array is returned.
+     */
+    function push($code, $level = 'error', $params = array(), $msg = false,
+                  $repackage = false, $backtrace = false)
+    {
+        $context = false;
+        // grab error context
+        if ($this->_contextCallback) {
+            if (!$backtrace) {
+                $backtrace = debug_backtrace();
+            }
+            $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
+        }
+        
+        // save error
+        $time = explode(' ', microtime());
+        $time = $time[1] + $time[0];
+        $err = array(
+                'code' => $code,
+                'params' => $params,
+                'package' => $this->_package,
+                'level' => $level,
+                'time' => $time,
+                'context' => $context,
+                'message' => $msg,
+               );
+
+        if ($repackage) {
+            $err['repackage'] = $repackage;
+        }
+
+        // set up the error message, if necessary
+        if ($this->_msgCallback) {
+            $msg = call_user_func_array($this->_msgCallback,
+                                        array(&$this, $err));
+            $err['message'] = $msg;
+        }        
+        $push = $log = true;
+        $die = false;
+        // try the overriding callback first
+        $callback = $this->staticPopCallback();
+        if ($callback) {
+            $this->staticPushCallback($callback);
+        }
+        if (!is_callable($callback)) {
+            // try the local callback next
+            $callback = $this->popCallback();
+            if (is_callable($callback)) {
+                $this->pushCallback($callback);
+            } else {
+                // try the default callback
+                $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
+                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
+                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
+            }
+        }
+        if (is_callable($callback)) {
+            switch(call_user_func($callback, $err)){
+            	case PEAR_ERRORSTACK_IGNORE: 
+            		return $err;
+        		break;
+            	case PEAR_ERRORSTACK_PUSH: 
+            		$log = false;
+        		break;
+            	case PEAR_ERRORSTACK_LOG: 
+            		$push = false;
+        		break;
+            	case PEAR_ERRORSTACK_DIE: 
+            		$die = true;
+        		break;
+                // anything else returned has the same effect as pushandlog
+            }
+        }
+        if ($push) {
+            array_unshift($this->_errors, $err);
+            if (!isset($this->_errorsByLevel[$err['level']])) {
+                $this->_errorsByLevel[$err['level']] = array();
+            }
+            $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
+        }
+        if ($log) {
+            if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
+                $this->_log($err);
+            }
+        }
+        if ($die) {
+            die();
+        }
+        if ($this->_compat && $push) {
+            return $this->raiseError($msg, $code, null, null, $err);
+        }
+        return $err;
+    }
+    
+    /**
+     * Static version of {@link push()}
+     * 
+     * @param string $package   Package name this error belongs to
+     * @param int    $code      Package-specific error code
+     * @param string $level     Error level.  This is NOT spell-checked
+     * @param array  $params    associative array of error parameters
+     * @param string $msg       Error message, or a portion of it if the message
+     *                          is to be generated
+     * @param array  $repackage If this error re-packages an error pushed by
+     *                          another package, place the array returned from
+     *                          {@link pop()} in this parameter
+     * @param array  $backtrace Protected parameter: use this to pass in the
+     *                          {@link debug_backtrace()} that should be used
+     *                          to find error context
+     * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
+     *                          thrown.  see docs for {@link push()}
+     * @static
+     */
+    function staticPush($package, $code, $level = 'error', $params = array(),
+                        $msg = false, $repackage = false, $backtrace = false)
+    {
+        $s = &PEAR_ErrorStack::singleton($package);
+        if ($s->_contextCallback) {
+            if (!$backtrace) {
+                if (function_exists('debug_backtrace')) {
+                    $backtrace = debug_backtrace();
+                }
+            }
+        }
+        return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
+    }
+    
+    /**
+     * Log an error using PEAR::Log
+     * @param array $err Error array
+     * @param array $levels Error level => Log constant map
+     * @access protected
+     */
+    function _log($err)
+    {
+        if ($this->_logger) {
+            $logger = &$this->_logger;
+        } else {
+            $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
+        }
+        if (is_a($logger, 'Log')) {
+            $levels = array(
+                'exception' => PEAR_LOG_CRIT,
+                'alert' => PEAR_LOG_ALERT,
+                'critical' => PEAR_LOG_CRIT,
+                'error' => PEAR_LOG_ERR,
+                'warning' => PEAR_LOG_WARNING,
+                'notice' => PEAR_LOG_NOTICE,
+                'info' => PEAR_LOG_INFO,
+                'debug' => PEAR_LOG_DEBUG);
+            if (isset($levels[$err['level']])) {
+                $level = $levels[$err['level']];
+            } else {
+                $level = PEAR_LOG_INFO;
+            }
+            $logger->log($err['message'], $level, $err);
+        } else { // support non-standard logs
+            call_user_func($logger, $err);
+        }
+    }
+
+    
+    /**
+     * Pop an error off of the error stack
+     * 
+     * @return false|array
+     * @since 0.4alpha it is no longer possible to specify a specific error
+     * level to return - the last error pushed will be returned, instead
+     */
+    function pop()
+    {
+        $err = @array_shift($this->_errors);
+        if (!is_null($err)) {
+            @array_pop($this->_errorsByLevel[$err['level']]);
+            if (!count($this->_errorsByLevel[$err['level']])) {
+                unset($this->_errorsByLevel[$err['level']]);
+            }
+        }
+        return $err;
+    }
+
+    /**
+     * Pop an error off of the error stack, static method
+     *
+     * @param string package name
+     * @return boolean
+     * @since PEAR1.5.0a1
+     */
+    function staticPop($package)
+    {
+        if ($package) {
+            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
+                return false;
+            }
+            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop();
+        }
+    }
+
+    /**
+     * Determine whether there are any errors on the stack
+     * @param string|array Level name.  Use to determine if any errors
+     * of level (string), or levels (array) have been pushed
+     * @return boolean
+     */
+    function hasErrors($level = false)
+    {
+        if ($level) {
+            return isset($this->_errorsByLevel[$level]);
+        }
+        return count($this->_errors);
+    }
+    
+    /**
+     * Retrieve all errors since last purge
+     * 
+     * @param boolean set in order to empty the error stack
+     * @param string level name, to return only errors of a particular severity
+     * @return array
+     */
+    function getErrors($purge = false, $level = false)
+    {
+        if (!$purge) {
+            if ($level) {
+                if (!isset($this->_errorsByLevel[$level])) {
+                    return array();
+                } else {
+                    return $this->_errorsByLevel[$level];
+                }
+            } else {
+                return $this->_errors;
+            }
+        }
+        if ($level) {
+            $ret = $this->_errorsByLevel[$level];
+            foreach ($this->_errorsByLevel[$level] as $i => $unused) {
+                // entries are references to the $_errors array
+                $this->_errorsByLevel[$level][$i] = false;
+            }
+            // array_filter removes all entries === false
+            $this->_errors = array_filter($this->_errors);
+            unset($this->_errorsByLevel[$level]);
+            return $ret;
+        }
+        $ret = $this->_errors;
+        $this->_errors = array();
+        $this->_errorsByLevel = array();
+        return $ret;
+    }
+    
+    /**
+     * Determine whether there are any errors on a single error stack, or on any error stack
+     *
+     * The optional parameter can be used to test the existence of any errors without the need of
+     * singleton instantiation
+     * @param string|false Package name to check for errors
+     * @param string Level name to check for a particular severity
+     * @return boolean
+     * @static
+     */
+    function staticHasErrors($package = false, $level = false)
+    {
+        if ($package) {
+            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
+                return false;
+            }
+            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
+        }
+        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
+            if ($obj->hasErrors($level)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Get a list of all errors since last purge, organized by package
+     * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
+     * @param boolean $purge Set to purge the error stack of existing errors
+     * @param string  $level Set to a level name in order to retrieve only errors of a particular level
+     * @param boolean $merge Set to return a flat array, not organized by package
+     * @param array   $sortfunc Function used to sort a merged array - default
+     *        sorts by time, and should be good for most cases
+     * @static
+     * @return array 
+     */
+    function staticGetErrors($purge = false, $level = false, $merge = false,
+                             $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
+    {
+        $ret = array();
+        if (!is_callable($sortfunc)) {
+            $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
+        }
+        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
+            $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
+            if ($test) {
+                if ($merge) {
+                    $ret = array_merge($ret, $test);
+                } else {
+                    $ret[$package] = $test;
+                }
+            }
+        }
+        if ($merge) {
+            usort($ret, $sortfunc);
+        }
+        return $ret;
+    }
+    
+    /**
+     * Error sorting function, sorts by time
+     * @access private
+     */
+    function _sortErrors($a, $b)
+    {
+        if ($a['time'] == $b['time']) {
+            return 0;
+        }
+        if ($a['time'] < $b['time']) {
+            return 1;
+        }
+        return -1;
+    }
+
+    /**
+     * Standard file/line number/function/class context callback
+     *
+     * This function uses a backtrace generated from {@link debug_backtrace()}
+     * and so will not work at all in PHP < 4.3.0.  The frame should
+     * reference the frame that contains the source of the error.
+     * @return array|false either array('file' => file, 'line' => line,
+     *         'function' => function name, 'class' => class name) or
+     *         if this doesn't work, then false
+     * @param unused
+     * @param integer backtrace frame.
+     * @param array Results of debug_backtrace()
+     * @static
+     */
+    function getFileLine($code, $params, $backtrace = null)
+    {
+        if ($backtrace === null) {
+            return false;
+        }
+        $frame = 0;
+        $functionframe = 1;
+        if (!isset($backtrace[1])) {
+            $functionframe = 0;
+        } else {
+            while (isset($backtrace[$functionframe]['function']) &&
+                  $backtrace[$functionframe]['function'] == 'eval' &&
+                  isset($backtrace[$functionframe + 1])) {
+                $functionframe++;
+            }
+        }
+        if (isset($backtrace[$frame])) {
+            if (!isset($backtrace[$frame]['file'])) {
+                $frame++;
+            }
+            $funcbacktrace = $backtrace[$functionframe];
+            $filebacktrace = $backtrace[$frame];
+            $ret = array('file' => $filebacktrace['file'],
+                         'line' => $filebacktrace['line']);
+            // rearrange for eval'd code or create function errors
+            if (strpos($filebacktrace['file'], '(') && 
+            	  preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'],
+                  $matches)) {
+                $ret['file'] = $matches[1];
+                $ret['line'] = $matches[2] + 0;
+            }
+            if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
+                if ($funcbacktrace['function'] != 'eval') {
+                    if ($funcbacktrace['function'] == '__lambda_func') {
+                        $ret['function'] = 'create_function() code';
+                    } else {
+                        $ret['function'] = $funcbacktrace['function'];
+                    }
+                }
+            }
+            if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
+                $ret['class'] = $funcbacktrace['class'];
+            }
+            return $ret;
+        }
+        return false;
+    }
+    
+    /**
+     * Standard error message generation callback
+     * 
+     * This method may also be called by a custom error message generator
+     * to fill in template values from the params array, simply
+     * set the third parameter to the error message template string to use
+     * 
+     * The special variable %__msg% is reserved: use it only to specify
+     * where a message passed in by the user should be placed in the template,
+     * like so:
+     * 
+     * Error message: %msg% - internal error
+     * 
+     * If the message passed like so:
+     * 
+     * <code>
+     * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
+     * </code>
+     * 
+     * The returned error message will be "Error message: server error 500 -
+     * internal error"
+     * @param PEAR_ErrorStack
+     * @param array
+     * @param string|false Pre-generated error message template
+     * @static
+     * @return string
+     */
+    function getErrorMessage(&$stack, $err, $template = false)
+    {
+        if ($template) {
+            $mainmsg = $template;
+        } else {
+            $mainmsg = $stack->getErrorMessageTemplate($err['code']);
+        }
+        $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
+        if (is_array($err['params']) && count($err['params'])) {
+            foreach ($err['params'] as $name => $val) {
+                if (is_array($val)) {
+                    // @ is needed in case $val is a multi-dimensional array
+                    $val = @implode(', ', $val);
+                }
+                if (is_object($val)) {
+                    if (method_exists($val, '__toString')) {
+                        $val = $val->__toString();
+                    } else {
+                        PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
+                            'warning', array('obj' => get_class($val)),
+                            'object %obj% passed into getErrorMessage, but has no __toString() method');
+                        $val = 'Object';
+                    }
+                }
+                $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
+            }
+        }
+        return $mainmsg;
+    }
+    
+    /**
+     * Standard Error Message Template generator from code
+     * @return string
+     */
+    function getErrorMessageTemplate($code)
+    {
+        if (!isset($this->_errorMsgs[$code])) {
+            return '%__msg%';
+        }
+        return $this->_errorMsgs[$code];
+    }
+    
+    /**
+     * Set the Error Message Template array
+     * 
+     * The array format must be:
+     * <pre>
+     * array(error code => 'message template',...)
+     * </pre>
+     * 
+     * Error message parameters passed into {@link push()} will be used as input
+     * for the error message.  If the template is 'message %foo% was %bar%', and the
+     * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
+     * be 'message one was six'
+     * @return string
+     */
+    function setErrorMessageTemplate($template)
+    {
+        $this->_errorMsgs = $template;
+    }
+    
+    
+    /**
+     * emulate PEAR::raiseError()
+     * 
+     * @return PEAR_Error
+     */
+    function raiseError()
+    {
+        require_once 'PEAR.php';
+        $args = func_get_args();
+        return call_user_func_array(array('PEAR', 'raiseError'), $args);
+    }
+}
+$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
+$stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
+?>
Index: trunk/library/PEAR/PEAR/Frontend.php
===================================================================
--- trunk/library/PEAR/PEAR/Frontend.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Frontend.php	(revision 5146)
@@ -0,0 +1,228 @@
+<?php
+/**
+ * PEAR_Frontend, the singleton-based frontend for user input/output
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Frontend.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * Include error handling
+ */
+//require_once 'PEAR.php';
+
+/**
+ * Which user interface class is being used.
+ * @var string class name
+ */
+$GLOBALS['_PEAR_FRONTEND_CLASS'] = 'PEAR_Frontend_CLI';
+
+/**
+ * Instance of $_PEAR_Command_uiclass.
+ * @var object
+ */
+$GLOBALS['_PEAR_FRONTEND_SINGLETON'] = null;
+
+/**
+ * Singleton-based frontend for PEAR user input/output
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Frontend extends PEAR
+{
+    /**
+     * Retrieve the frontend object
+     * @return PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk
+     * @static
+     */
+    function &singleton($type = null)
+    {
+        if ($type === null) {
+            if (!isset($GLOBALS['_PEAR_FRONTEND_SINGLETON'])) {
+                $a = false;
+                return $a;
+            }
+            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
+        }
+
+        $a = PEAR_Frontend::setFrontendClass($type);
+        return $a;
+    }
+
+    /**
+     * Set the frontend class that will be used by calls to {@link singleton()}
+     *
+     * Frontends are expected to conform to the PEAR naming standard of
+     * _ => DIRECTORY_SEPARATOR (PEAR_Frontend_CLI is in PEAR/Frontend/CLI.php)
+     * @param string $uiclass full class name
+     * @return PEAR_Frontend
+     * @static
+     */
+    function &setFrontendClass($uiclass)
+    {
+        if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
+              is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) {
+            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
+        }
+
+        if (!class_exists($uiclass)) {
+            $file = str_replace('_', '/', $uiclass) . '.php';
+            if (PEAR_Frontend::isIncludeable($file)) {
+                include_once $file;
+            }
+        }
+
+        if (class_exists($uiclass)) {
+            $obj = &new $uiclass;
+            // quick test to see if this class implements a few of the most
+            // important frontend methods
+            if (is_a($obj, 'PEAR_Frontend')) {
+                $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj;
+                $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass;
+                return $obj;
+            }
+
+            $err = PEAR::raiseError("not a frontend class: $uiclass");
+            return $err;
+        }
+
+        $err = PEAR::raiseError("no such class: $uiclass");
+        return $err;
+    }
+
+    /**
+     * Set the frontend class that will be used by calls to {@link singleton()}
+     *
+     * Frontends are expected to be a descendant of PEAR_Frontend
+     * @param PEAR_Frontend
+     * @return PEAR_Frontend
+     * @static
+     */
+    function &setFrontendObject($uiobject)
+    {
+        if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
+              is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) {
+            return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
+        }
+
+        if (!is_a($uiobject, 'PEAR_Frontend')) {
+            $err = PEAR::raiseError('not a valid frontend class: (' .
+                get_class($uiobject) . ')');
+            return $err;
+        }
+
+        $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject;
+        $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject);
+        return $uiobject;
+    }
+
+    /**
+     * @param string $path relative or absolute include path
+     * @return boolean
+     * @static
+     */
+    function isIncludeable($path)
+    {
+        if (file_exists($path) && is_readable($path)) {
+            return true;
+        }
+
+        $fp = @fopen($path, 'r', true);
+        if ($fp) {
+            fclose($fp);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @param PEAR_Config
+     */
+    function setConfig(&$config)
+    {
+    }
+
+    /**
+     * This can be overridden to allow session-based temporary file management
+     *
+     * By default, all files are deleted at the end of a session.  The web installer
+     * needs to be able to sustain a list over many sessions in order to support
+     * user interaction with install scripts
+     */
+    function addTempFile($file)
+    {
+        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
+    }
+
+    /**
+     * Log an action
+     *
+     * @param string $msg the message to log
+     * @param boolean $append_crlf
+     * @return boolean true
+     * @abstract
+     */
+    function log($msg, $append_crlf = true)
+    {
+    }
+
+    /**
+     * Run a post-installation script
+     *
+     * @param array $scripts array of post-install scripts
+     * @abstract
+     */
+    function runPostinstallScripts(&$scripts)
+    {
+    }
+
+    /**
+     * Display human-friendly output formatted depending on the
+     * $command parameter.
+     *
+     * This should be able to handle basic output data with no command
+     * @param mixed  $data    data structure containing the information to display
+     * @param string $command command from which this method was called
+     * @abstract
+     */
+    function outputData($data, $command = '_default')
+    {
+    }
+
+    /**
+     * Display a modal form dialog and return the given input
+     *
+     * A frontend that requires multiple requests to retrieve and process
+     * data must take these needs into account, and implement the request
+     * handling code.
+     * @param string $command  command from which this method was called
+     * @param array  $prompts  associative array. keys are the input field names
+     *                         and values are the description
+     * @param array  $types    array of input field types (text, password,
+     *                         etc.) keys have to be the same like in $prompts
+     * @param array  $defaults array of default values. again keys have
+     *                         to be the same like in $prompts.  Do not depend
+     *                         on a default value being set.
+     * @return array input sent by the user
+     * @abstract
+     */
+    function userDialog($command, $prompts, $types = array(), $defaults = array())
+    {
+    }
+}
Index: trunk/library/PEAR/PEAR/ChannelFile/Parser.php
===================================================================
--- trunk/library/PEAR/PEAR/ChannelFile/Parser.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/ChannelFile/Parser.php	(revision 5146)
@@ -0,0 +1,68 @@
+<?php
+/**
+ * PEAR_ChannelFile_Parser for parsing channel.xml
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Parser.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * base xml parser class
+ */
+require_once 'PEAR/XMLParser.php';
+require_once 'PEAR/ChannelFile.php';
+/**
+ * Parser for channel.xml
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_ChannelFile_Parser extends PEAR_XMLParser
+{
+    var $_config;
+    var $_logger;
+    var $_registry;
+
+    function setConfig(&$c)
+    {
+        $this->_config = &$c;
+        $this->_registry = &$c->getRegistry();
+    }
+
+    function setLogger(&$l)
+    {
+        $this->_logger = &$l;
+    }
+
+    function parse($data, $file)
+    {
+        if (PEAR::isError($err = parent::parse($data, $file))) {
+            return $err;
+        }
+
+        $ret = new PEAR_ChannelFile;
+        $ret->setConfig($this->_config);
+        if (isset($this->_logger)) {
+            $ret->setLogger($this->_logger);
+        }
+
+        $ret->fromArray($this->_unserializedData);
+        // make sure the filelist is in the easy to read format needed
+        $ret->flattenFilelist();
+        $ret->setPackagefile($file, $archive);
+        return $ret;
+    }
+}
Index: trunk/library/PEAR/PEAR/DependencyDB.php
===================================================================
--- trunk/library/PEAR/PEAR/DependencyDB.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/DependencyDB.php	(revision 5146)
@@ -0,0 +1,769 @@
+<?php
+/**
+ * PEAR_DependencyDB, advanced installed packages dependency database
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: DependencyDB.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * Needed for error handling
+ */
+require_once 'PEAR.php';
+require_once 'PEAR/Config.php';
+
+$GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array();
+/**
+ * Track dependency relationships between installed packages
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @author     Tomas V.V.Cox <cox@idec.net.com>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_DependencyDB
+{
+    // {{{ properties
+
+    /**
+     * This is initialized by {@link setConfig()}
+     * @var PEAR_Config
+     * @access private
+     */
+    var $_config;
+    /**
+     * This is initialized by {@link setConfig()}
+     * @var PEAR_Registry
+     * @access private
+     */
+    var $_registry;
+    /**
+     * Filename of the dependency DB (usually .depdb)
+     * @var string
+     * @access private
+     */
+    var $_depdb = false;
+    /**
+     * File name of the lockfile (usually .depdblock)
+     * @var string
+     * @access private
+     */
+    var $_lockfile = false;
+    /**
+     * Open file resource for locking the lockfile
+     * @var resource|false
+     * @access private
+     */
+    var $_lockFp = false;
+    /**
+     * API version of this class, used to validate a file on-disk
+     * @var string
+     * @access private
+     */
+    var $_version = '1.0';
+    /**
+     * Cached dependency database file
+     * @var array|null
+     * @access private
+     */
+    var $_cache;
+
+    // }}}
+    // {{{ & singleton()
+
+    /**
+     * Get a raw dependency database.  Calls setConfig() and assertDepsDB()
+     * @param PEAR_Config
+     * @param string|false full path to the dependency database, or false to use default
+     * @return PEAR_DependencyDB|PEAR_Error
+     * @static
+     */
+    function &singleton(&$config, $depdb = false)
+    {
+        $phpdir = $config->get('php_dir', null, 'pear.php.net');
+        if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) {
+            $a = new PEAR_DependencyDB;
+            $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a;
+            $a->setConfig($config, $depdb);
+            $e = $a->assertDepsDB();
+            if (PEAR::isError($e)) {
+                return $e;
+            }
+        }
+
+        return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir];
+    }
+
+    /**
+     * Set up the registry/location of dependency DB
+     * @param PEAR_Config|false
+     * @param string|false full path to the dependency database, or false to use default
+     */
+    function setConfig(&$config, $depdb = false)
+    {
+        if (!$config) {
+            $this->_config = &PEAR_Config::singleton();
+        } else {
+            $this->_config = &$config;
+        }
+
+        $this->_registry = &$this->_config->getRegistry();
+        if (!$depdb) {
+            $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') .
+                DIRECTORY_SEPARATOR . '.depdb';
+        } else {
+            $this->_depdb = $depdb;
+        }
+
+        $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock';
+    }
+    // }}}
+
+    function hasWriteAccess()
+    {
+        if (!file_exists($this->_depdb)) {
+            $dir = $this->_depdb;
+            while ($dir && $dir != '.') {
+                $dir = dirname($dir); // cd ..
+                if ($dir != '.' && file_exists($dir)) {
+                    if (is_writeable($dir)) {
+                        return true;
+                    }
+
+                    return false;
+                }
+            }
+
+            return false;
+        }
+
+        return is_writeable($this->_depdb);
+    }
+
+    // {{{ assertDepsDB()
+
+    /**
+     * Create the dependency database, if it doesn't exist.  Error if the database is
+     * newer than the code reading it.
+     * @return void|PEAR_Error
+     */
+    function assertDepsDB()
+    {
+        if (!is_file($this->_depdb)) {
+            $this->rebuildDB();
+            return;
+        }
+
+        $depdb = $this->_getDepDB();
+        // Datatype format has been changed, rebuild the Deps DB
+        if ($depdb['_version'] < $this->_version) {
+            $this->rebuildDB();
+        }
+
+        if ($depdb['_version']{0} > $this->_version{0}) {
+            return PEAR::raiseError('Dependency database is version ' .
+                $depdb['_version'] . ', and we are version ' .
+                $this->_version . ', cannot continue');
+        }
+    }
+
+    /**
+     * Get a list of installed packages that depend on this package
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
+     * @return array|false
+     */
+    function getDependentPackages(&$pkg)
+    {
+        $data = $this->_getDepDB();
+        if (is_object($pkg)) {
+            $channel = strtolower($pkg->getChannel());
+            $package = strtolower($pkg->getPackage());
+        } else {
+            $channel = strtolower($pkg['channel']);
+            $package = strtolower($pkg['package']);
+        }
+
+        if (isset($data['packages'][$channel][$package])) {
+            return $data['packages'][$channel][$package];
+        }
+
+        return false;
+    }
+
+    /**
+     * Get a list of the actual dependencies of installed packages that depend on
+     * a package.
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
+     * @return array|false
+     */
+    function getDependentPackageDependencies(&$pkg)
+    {
+        $data = $this->_getDepDB();
+        if (is_object($pkg)) {
+            $channel = strtolower($pkg->getChannel());
+            $package = strtolower($pkg->getPackage());
+        } else {
+            $channel = strtolower($pkg['channel']);
+            $package = strtolower($pkg['package']);
+        }
+
+        $depend = $this->getDependentPackages($pkg);
+        if (!$depend) {
+            return false;
+        }
+
+        $dependencies = array();
+        foreach ($depend as $info) {
+            $temp = $this->getDependencies($info);
+            foreach ($temp as $dep) {
+                if (
+                    isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) &&
+                    strtolower($dep['dep']['channel']) == $channel &&
+                    strtolower($dep['dep']['name']) == $package
+                ) {
+                    if (!isset($dependencies[$info['channel']])) {
+                        $dependencies[$info['channel']] = array();
+                    }
+
+                    if (!isset($dependencies[$info['channel']][$info['package']])) {
+                        $dependencies[$info['channel']][$info['package']] = array();
+                    }
+                    $dependencies[$info['channel']][$info['package']][] = $dep;
+                }
+            }
+        }
+
+        return $dependencies;
+    }
+
+    /**
+     * Get a list of dependencies of this installed package
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
+     * @return array|false
+     */
+    function getDependencies(&$pkg)
+    {
+        if (is_object($pkg)) {
+            $channel = strtolower($pkg->getChannel());
+            $package = strtolower($pkg->getPackage());
+        } else {
+            $channel = strtolower($pkg['channel']);
+            $package = strtolower($pkg['package']);
+        }
+
+        $data = $this->_getDepDB();
+        if (isset($data['dependencies'][$channel][$package])) {
+            return $data['dependencies'][$channel][$package];
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine whether $parent depends on $child, near or deep
+     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
+     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
+     */
+    function dependsOn($parent, $child)
+    {
+        $c = array();
+        $this->_getDepDB();
+        return $this->_dependsOn($parent, $child, $c);
+    }
+
+    function _dependsOn($parent, $child, &$checked)
+    {
+        if (is_object($parent)) {
+            $channel = strtolower($parent->getChannel());
+            $package = strtolower($parent->getPackage());
+        } else {
+            $channel = strtolower($parent['channel']);
+            $package = strtolower($parent['package']);
+        }
+
+        if (is_object($child)) {
+            $depchannel = strtolower($child->getChannel());
+            $deppackage = strtolower($child->getPackage());
+        } else {
+            $depchannel = strtolower($child['channel']);
+            $deppackage = strtolower($child['package']);
+        }
+
+        if (isset($checked[$channel][$package][$depchannel][$deppackage])) {
+            return false; // avoid endless recursion
+        }
+
+        $checked[$channel][$package][$depchannel][$deppackage] = true;
+        if (!isset($this->_cache['dependencies'][$channel][$package])) {
+            return false;
+        }
+
+        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
+            if (isset($info['dep']['uri'])) {
+                if (is_object($child)) {
+                    if ($info['dep']['uri'] == $child->getURI()) {
+                        return true;
+                    }
+                } elseif (isset($child['uri'])) {
+                    if ($info['dep']['uri'] == $child['uri']) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            if (strtolower($info['dep']['channel']) == $depchannel &&
+                  strtolower($info['dep']['name']) == $deppackage) {
+                return true;
+            }
+        }
+
+        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
+            if (isset($info['dep']['uri'])) {
+                if ($this->_dependsOn(array(
+                        'uri' => $info['dep']['uri'],
+                        'package' => $info['dep']['name']), $child, $checked)) {
+                    return true;
+                }
+            } else {
+                if ($this->_dependsOn(array(
+                        'channel' => $info['dep']['channel'],
+                        'package' => $info['dep']['name']), $child, $checked)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Register dependencies of a package that is being installed or upgraded
+     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2
+     */
+    function installPackage(&$package)
+    {
+        $data = $this->_getDepDB();
+        unset($this->_cache);
+        $this->_setPackageDeps($data, $package);
+        $this->_writeDepDB($data);
+    }
+
+    /**
+     * Remove dependencies of a package that is being uninstalled, or upgraded.
+     *
+     * Upgraded packages first uninstall, then install
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have
+     *        indices 'channel' and 'package'
+     */
+    function uninstallPackage(&$pkg)
+    {
+        $data = $this->_getDepDB();
+        unset($this->_cache);
+        if (is_object($pkg)) {
+            $channel = strtolower($pkg->getChannel());
+            $package = strtolower($pkg->getPackage());
+        } else {
+            $channel = strtolower($pkg['channel']);
+            $package = strtolower($pkg['package']);
+        }
+
+        if (!isset($data['dependencies'][$channel][$package])) {
+            return true;
+        }
+
+        foreach ($data['dependencies'][$channel][$package] as $dep) {
+            $found      = false;
+            $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']);
+            $depname    = strtolower($dep['dep']['name']);
+            if (isset($data['packages'][$depchannel][$depname])) {
+                foreach ($data['packages'][$depchannel][$depname] as $i => $info) {
+                    if ($info['channel'] == $channel && $info['package'] == $package) {
+                        $found = true;
+                        break;
+                    }
+                }
+            }
+
+            if ($found) {
+                unset($data['packages'][$depchannel][$depname][$i]);
+                if (!count($data['packages'][$depchannel][$depname])) {
+                    unset($data['packages'][$depchannel][$depname]);
+                    if (!count($data['packages'][$depchannel])) {
+                        unset($data['packages'][$depchannel]);
+                    }
+                } else {
+                    $data['packages'][$depchannel][$depname] =
+                        array_values($data['packages'][$depchannel][$depname]);
+                }
+            }
+        }
+
+        unset($data['dependencies'][$channel][$package]);
+        if (!count($data['dependencies'][$channel])) {
+            unset($data['dependencies'][$channel]);
+        }
+
+        if (!count($data['dependencies'])) {
+            unset($data['dependencies']);
+        }
+
+        if (!count($data['packages'])) {
+            unset($data['packages']);
+        }
+
+        $this->_writeDepDB($data);
+    }
+
+    /**
+     * Rebuild the dependency DB by reading registry entries.
+     * @return true|PEAR_Error
+     */
+    function rebuildDB()
+    {
+        $depdb = array('_version' => $this->_version);
+        if (!$this->hasWriteAccess()) {
+            // allow startup for read-only with older Registry
+            return $depdb;
+        }
+
+        $packages = $this->_registry->listAllPackages();
+        if (PEAR::isError($packages)) {
+            return $packages;
+        }
+
+        foreach ($packages as $channel => $ps) {
+            foreach ($ps as $package) {
+                $package = $this->_registry->getPackage($package, $channel);
+                if (PEAR::isError($package)) {
+                    return $package;
+                }
+                $this->_setPackageDeps($depdb, $package);
+            }
+        }
+
+        $error = $this->_writeDepDB($depdb);
+        if (PEAR::isError($error)) {
+            return $error;
+        }
+
+        $this->_cache = $depdb;
+        return true;
+    }
+
+    /**
+     * Register usage of the dependency DB to prevent race conditions
+     * @param int one of the LOCK_* constants
+     * @return true|PEAR_Error
+     * @access private
+     */
+    function _lock($mode = LOCK_EX)
+    {
+        if (stristr(php_uname(), 'Windows 9')) {
+            return true;
+        }
+
+        if ($mode != LOCK_UN && is_resource($this->_lockFp)) {
+            // XXX does not check type of lock (LOCK_SH/LOCK_EX)
+            return true;
+        }
+
+        $open_mode = 'w';
+        // XXX People reported problems with LOCK_SH and 'w'
+        if ($mode === LOCK_SH) {
+            if (!file_exists($this->_lockfile)) {
+                touch($this->_lockfile);
+            } elseif (!is_file($this->_lockfile)) {
+                return PEAR::raiseError('could not create Dependency lock file, ' .
+                    'it exists and is not a regular file');
+            }
+            $open_mode = 'r';
+        }
+
+        if (!is_resource($this->_lockFp)) {
+            $this->_lockFp = @fopen($this->_lockfile, $open_mode);
+        }
+
+        if (!is_resource($this->_lockFp)) {
+            return PEAR::raiseError("could not create Dependency lock file" .
+                                     (isset($php_errormsg) ? ": " . $php_errormsg : ""));
+        }
+
+        if (!(int)flock($this->_lockFp, $mode)) {
+            switch ($mode) {
+                case LOCK_SH: $str = 'shared';    break;
+                case LOCK_EX: $str = 'exclusive'; break;
+                case LOCK_UN: $str = 'unlock';    break;
+                default:      $str = 'unknown';   break;
+            }
+
+            return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)");
+        }
+
+        return true;
+    }
+
+    /**
+     * Release usage of dependency DB
+     * @return true|PEAR_Error
+     * @access private
+     */
+    function _unlock()
+    {
+        $ret = $this->_lock(LOCK_UN);
+        if (is_resource($this->_lockFp)) {
+            fclose($this->_lockFp);
+        }
+        $this->_lockFp = null;
+        return $ret;
+    }
+
+    /**
+     * Load the dependency database from disk, or return the cache
+     * @return array|PEAR_Error
+     */
+    function _getDepDB()
+    {
+        if (!$this->hasWriteAccess()) {
+            return array('_version' => $this->_version);
+        }
+
+        if (isset($this->_cache)) {
+            return $this->_cache;
+        }
+
+        if (!$fp = fopen($this->_depdb, 'r')) {
+            $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'");
+            return $err;
+        }
+
+        $rt = get_magic_quotes_runtime();
+        set_magic_quotes_runtime(0);
+        clearstatcache();
+        fclose($fp);
+        $data = unserialize(file_get_contents($this->_depdb));
+        set_magic_quotes_runtime($rt);
+        $this->_cache = $data;
+        return $data;
+    }
+
+    /**
+     * Write out the dependency database to disk
+     * @param array the database
+     * @return true|PEAR_Error
+     * @access private
+     */
+    function _writeDepDB(&$deps)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+
+        if (!$fp = fopen($this->_depdb, 'wb')) {
+            $this->_unlock();
+            return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing");
+        }
+
+        $rt = get_magic_quotes_runtime();
+        set_magic_quotes_runtime(0);
+        fwrite($fp, serialize($deps));
+        set_magic_quotes_runtime($rt);
+        fclose($fp);
+        $this->_unlock();
+        $this->_cache = $deps;
+        return true;
+    }
+
+    /**
+     * Register all dependencies from a package in the dependencies database, in essence
+     * "installing" the package's dependency information
+     * @param array the database
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @access private
+     */
+    function _setPackageDeps(&$data, &$pkg)
+    {
+        $pkg->setConfig($this->_config);
+        if ($pkg->getPackagexmlVersion() == '1.0') {
+            $gen = &$pkg->getDefaultGenerator();
+            $deps = $gen->dependenciesToV2();
+        } else {
+            $deps = $pkg->getDeps(true);
+        }
+
+        if (!$deps) {
+            return;
+        }
+
+        if (!is_array($data)) {
+            $data = array();
+        }
+
+        if (!isset($data['dependencies'])) {
+            $data['dependencies'] = array();
+        }
+
+        $channel = strtolower($pkg->getChannel());
+        $package = strtolower($pkg->getPackage());
+
+        if (!isset($data['dependencies'][$channel])) {
+            $data['dependencies'][$channel] = array();
+        }
+
+        $data['dependencies'][$channel][$package] = array();
+        if (isset($deps['required']['package'])) {
+            if (!isset($deps['required']['package'][0])) {
+                $deps['required']['package'] = array($deps['required']['package']);
+            }
+
+            foreach ($deps['required']['package'] as $dep) {
+                $this->_registerDep($data, $pkg, $dep, 'required');
+            }
+        }
+
+        if (isset($deps['optional']['package'])) {
+            if (!isset($deps['optional']['package'][0])) {
+                $deps['optional']['package'] = array($deps['optional']['package']);
+            }
+
+            foreach ($deps['optional']['package'] as $dep) {
+                $this->_registerDep($data, $pkg, $dep, 'optional');
+            }
+        }
+
+        if (isset($deps['required']['subpackage'])) {
+            if (!isset($deps['required']['subpackage'][0])) {
+                $deps['required']['subpackage'] = array($deps['required']['subpackage']);
+            }
+
+            foreach ($deps['required']['subpackage'] as $dep) {
+                $this->_registerDep($data, $pkg, $dep, 'required');
+            }
+        }
+
+        if (isset($deps['optional']['subpackage'])) {
+            if (!isset($deps['optional']['subpackage'][0])) {
+                $deps['optional']['subpackage'] = array($deps['optional']['subpackage']);
+            }
+
+            foreach ($deps['optional']['subpackage'] as $dep) {
+                $this->_registerDep($data, $pkg, $dep, 'optional');
+            }
+        }
+
+        if (isset($deps['group'])) {
+            if (!isset($deps['group'][0])) {
+                $deps['group'] = array($deps['group']);
+            }
+
+            foreach ($deps['group'] as $group) {
+                if (isset($group['package'])) {
+                    if (!isset($group['package'][0])) {
+                        $group['package'] = array($group['package']);
+                    }
+
+                    foreach ($group['package'] as $dep) {
+                        $this->_registerDep($data, $pkg, $dep, 'optional',
+                            $group['attribs']['name']);
+                    }
+                }
+
+                if (isset($group['subpackage'])) {
+                    if (!isset($group['subpackage'][0])) {
+                        $group['subpackage'] = array($group['subpackage']);
+                    }
+
+                    foreach ($group['subpackage'] as $dep) {
+                        $this->_registerDep($data, $pkg, $dep, 'optional',
+                            $group['attribs']['name']);
+                    }
+                }
+            }
+        }
+
+        if ($data['dependencies'][$channel][$package] == array()) {
+            unset($data['dependencies'][$channel][$package]);
+            if (!count($data['dependencies'][$channel])) {
+                unset($data['dependencies'][$channel]);
+            }
+        }
+    }
+
+    /**
+     * @param array the database
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @param array the specific dependency
+     * @param required|optional whether this is a required or an optional dep
+     * @param string|false dependency group this dependency is from, or false for ordinary dep
+     */
+    function _registerDep(&$data, &$pkg, $dep, $type, $group = false)
+    {
+        $info = array(
+            'dep'   => $dep,
+            'type'  => $type,
+            'group' => $group
+        );
+
+        $dep  = array_map('strtolower', $dep);
+        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
+        if (!isset($data['dependencies'])) {
+            $data['dependencies'] = array();
+        }
+
+        $channel = strtolower($pkg->getChannel());
+        $package = strtolower($pkg->getPackage());
+
+        if (!isset($data['dependencies'][$channel])) {
+            $data['dependencies'][$channel] = array();
+        }
+
+        if (!isset($data['dependencies'][$channel][$package])) {
+            $data['dependencies'][$channel][$package] = array();
+        }
+
+        $data['dependencies'][$channel][$package][] = $info;
+        if (isset($data['packages'][$depchannel][$dep['name']])) {
+            $found = false;
+            foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) {
+                if ($p['channel'] == $channel && $p['package'] == $package) {
+                    $found = true;
+                    break;
+                }
+            }
+        } else {
+            if (!isset($data['packages'])) {
+                $data['packages'] = array();
+            }
+
+            if (!isset($data['packages'][$depchannel])) {
+                $data['packages'][$depchannel] = array();
+            }
+
+            if (!isset($data['packages'][$depchannel][$dep['name']])) {
+                $data['packages'][$depchannel][$dep['name']] = array();
+            }
+
+            $found = false;
+        }
+
+        if (!$found) {
+            $data['packages'][$depchannel][$dep['name']][] = array(
+                'channel' => $channel,
+                'package' => $package
+            );
+        }
+    }
+}
Index: trunk/library/PEAR/PEAR/Builder.php
===================================================================
--- trunk/library/PEAR/PEAR/Builder.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Builder.php	(revision 5146)
@@ -0,0 +1,489 @@
+<?php
+/**
+ * PEAR_Builder for building PHP extensions (PECL packages)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Builder.php 313024 2011-07-06 19:51:24Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ *
+ * TODO: log output parameters in PECL command line
+ * TODO: msdev path in configuration
+ */
+
+/**
+ * Needed for extending PEAR_Builder
+ */
+require_once 'PEAR/Common.php';
+require_once 'PEAR/PackageFile.php';
+
+/**
+ * Class to handle building (compiling) extensions.
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since PHP 4.0.2
+ * @see        http://pear.php.net/manual/en/core.ppm.pear-builder.php
+ */
+class PEAR_Builder extends PEAR_Common
+{
+    var $php_api_version = 0;
+    var $zend_module_api_no = 0;
+    var $zend_extension_api_no = 0;
+
+    var $extensions_built = array();
+
+    /**
+     * @var string Used for reporting when it is not possible to pass function
+     *             via extra parameter, e.g. log, msdevCallback
+     */
+    var $current_callback = null;
+
+    // used for msdev builds
+    var $_lastline = null;
+    var $_firstline = null;
+
+    /**
+     * PEAR_Builder constructor.
+     *
+     * @param object $ui user interface object (instance of PEAR_Frontend_*)
+     *
+     * @access public
+     */
+    function PEAR_Builder(&$ui)
+    {
+        parent::PEAR_Common();
+        $this->setFrontendObject($ui);
+    }
+
+    /**
+     * Build an extension from source on windows.
+     * requires msdev
+     */
+    function _build_win32($descfile, $callback = null)
+    {
+        if (is_object($descfile)) {
+            $pkg = $descfile;
+            $descfile = $pkg->getPackageFile();
+        } else {
+            $pf = &new PEAR_PackageFile($this->config, $this->debug);
+            $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
+            if (PEAR::isError($pkg)) {
+                return $pkg;
+            }
+        }
+        $dir = dirname($descfile);
+        $old_cwd = getcwd();
+
+        if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
+            return $this->raiseError("could not chdir to $dir");
+        }
+
+        // packages that were in a .tar have the packagefile in this directory
+        $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
+        if (file_exists($dir) && is_dir($vdir)) {
+            if (!chdir($vdir)) {
+                return $this->raiseError("could not chdir to " . realpath($vdir));
+            }
+
+            $dir = getcwd();
+        }
+
+        $this->log(2, "building in $dir");
+
+        $dsp = $pkg->getPackage().'.dsp';
+        if (!file_exists("$dir/$dsp")) {
+            return $this->raiseError("The DSP $dsp does not exist.");
+        }
+        // XXX TODO: make release build type configurable
+        $command = 'msdev '.$dsp.' /MAKE "'.$pkg->getPackage(). ' - Release"';
+
+        $err = $this->_runCommand($command, array(&$this, 'msdevCallback'));
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+
+        // figure out the build platform and type
+        $platform = 'Win32';
+        $buildtype = 'Release';
+        if (preg_match('/.*?'.$pkg->getPackage().'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) {
+            $platform = $matches[1];
+            $buildtype = $matches[2];
+        }
+
+        if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/', $this->_lastline, $matches)) {
+            if ($matches[2]) {
+                // there were errors in the build
+                return $this->raiseError("There were errors during compilation.");
+            }
+            $out = $matches[1];
+        } else {
+            return $this->raiseError("Did not understand the completion status returned from msdev.exe.");
+        }
+
+        // msdev doesn't tell us the output directory :/
+        // open the dsp, find /out and use that directory
+        $dsptext = join(file($dsp),'');
+
+        // this regex depends on the build platform and type having been
+        // correctly identified above.
+        $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'.
+                    $pkg->getPackage().'\s-\s'.
+                    $platform.'\s'.
+                    $buildtype.'").*?'.
+                    '\/out:"(.*?)"/is';
+
+        if ($dsptext && preg_match($regex, $dsptext, $matches)) {
+            // what we get back is a relative path to the output file itself.
+            $outfile = realpath($matches[2]);
+        } else {
+            return $this->raiseError("Could not retrieve output information from $dsp.");
+        }
+        // realpath returns false if the file doesn't exist
+        if ($outfile && copy($outfile, "$dir/$out")) {
+            $outfile = "$dir/$out";
+        }
+
+        $built_files[] = array(
+            'file' => "$outfile",
+            'php_api' => $this->php_api_version,
+            'zend_mod_api' => $this->zend_module_api_no,
+            'zend_ext_api' => $this->zend_extension_api_no,
+            );
+
+        return $built_files;
+    }
+    // }}}
+
+    // {{{ msdevCallback()
+    function msdevCallback($what, $data)
+    {
+        if (!$this->_firstline)
+            $this->_firstline = $data;
+        $this->_lastline = $data;
+        call_user_func($this->current_callback, $what, $data);
+    }
+
+    /**
+     * @param string
+     * @param string
+     * @param array
+     * @access private
+     */
+    function _harvestInstDir($dest_prefix, $dirname, &$built_files)
+    {
+        $d = opendir($dirname);
+        if (!$d)
+            return false;
+
+        $ret = true;
+        while (($ent = readdir($d)) !== false) {
+            if ($ent{0} == '.')
+                continue;
+
+            $full = $dirname . DIRECTORY_SEPARATOR . $ent;
+            if (is_dir($full)) {
+                if (!$this->_harvestInstDir(
+                        $dest_prefix . DIRECTORY_SEPARATOR . $ent,
+                        $full, $built_files)) {
+                    $ret = false;
+                    break;
+                }
+            } else {
+                $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent;
+                $built_files[] = array(
+                        'file' => $full,
+                        'dest' => $dest,
+                        'php_api' => $this->php_api_version,
+                        'zend_mod_api' => $this->zend_module_api_no,
+                        'zend_ext_api' => $this->zend_extension_api_no,
+                        );
+            }
+        }
+        closedir($d);
+        return $ret;
+    }
+
+    /**
+     * Build an extension from source.  Runs "phpize" in the source
+     * directory, but compiles in a temporary directory
+     * (TMPDIR/pear-build-USER/PACKAGE-VERSION).
+     *
+     * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or
+     *               a PEAR_PackageFile object
+     *
+     * @param mixed $callback callback function used to report output,
+     * see PEAR_Builder::_runCommand for details
+     *
+     * @return array an array of associative arrays with built files,
+     * format:
+     * array( array( 'file' => '/path/to/ext.so',
+     *               'php_api' => YYYYMMDD,
+     *               'zend_mod_api' => YYYYMMDD,
+     *               'zend_ext_api' => YYYYMMDD ),
+     *        ... )
+     *
+     * @access public
+     *
+     * @see PEAR_Builder::_runCommand
+     */
+    function build($descfile, $callback = null)
+    {
+        if (preg_match('/(\\/|\\\\|^)([^\\/\\\\]+)?php(.+)?$/',
+                       $this->config->get('php_bin'), $matches)) {
+            if (isset($matches[2]) && strlen($matches[2]) &&
+                trim($matches[2]) != trim($this->config->get('php_prefix'))) {
+                $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') .
+                           ' appears to have a prefix ' . $matches[2] . ', but' .
+                           ' config variable php_prefix does not match');
+            }
+
+            if (isset($matches[3]) && strlen($matches[3]) &&
+                trim($matches[3]) != trim($this->config->get('php_suffix'))) {
+                $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') .
+                           ' appears to have a suffix ' . $matches[3] . ', but' .
+                           ' config variable php_suffix does not match');
+            }
+        }
+
+        $this->current_callback = $callback;
+        if (PEAR_OS == "Windows") {
+            return $this->_build_win32($descfile, $callback);
+        }
+
+        if (PEAR_OS != 'Unix') {
+            return $this->raiseError("building extensions not supported on this platform");
+        }
+
+        if (is_object($descfile)) {
+            $pkg = $descfile;
+            $descfile = $pkg->getPackageFile();
+            if (is_a($pkg, 'PEAR_PackageFile_v1')) {
+                $dir = dirname($descfile);
+            } else {
+                $dir = $pkg->_config->get('temp_dir') . '/' . $pkg->getName();
+                // automatically delete at session end
+                $this->addTempFile($dir);
+            }
+        } else {
+            $pf = &new PEAR_PackageFile($this->config);
+            $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
+            if (PEAR::isError($pkg)) {
+                return $pkg;
+            }
+            $dir = dirname($descfile);
+        }
+
+        // Find config. outside of normal path - e.g. config.m4
+        foreach (array_keys($pkg->getInstallationFileList()) as $item) {
+          if (stristr(basename($item), 'config.m4') && dirname($item) != '.') {
+            $dir .= DIRECTORY_SEPARATOR . dirname($item);
+            break;
+          }
+        }
+
+        $old_cwd = getcwd();
+        if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
+            return $this->raiseError("could not chdir to $dir");
+        }
+
+        $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
+        if (is_dir($vdir)) {
+            chdir($vdir);
+        }
+
+        $dir = getcwd();
+        $this->log(2, "building in $dir");
+        putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH'));
+        $err = $this->_runCommand($this->config->get('php_prefix')
+                                . "phpize" .
+                                $this->config->get('php_suffix'),
+                                array(&$this, 'phpizeCallback'));
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+
+        if (!$err) {
+            return $this->raiseError("`phpize' failed");
+        }
+
+        // {{{ start of interactive part
+        $configure_command = "$dir/configure";
+        $configure_options = $pkg->getConfigureOptions();
+        if ($configure_options) {
+            foreach ($configure_options as $o) {
+                $default = array_key_exists('default', $o) ? $o['default'] : null;
+                list($r) = $this->ui->userDialog('build',
+                                                 array($o['prompt']),
+                                                 array('text'),
+                                                 array($default));
+                if (substr($o['name'], 0, 5) == 'with-' &&
+                    ($r == 'yes' || $r == 'autodetect')) {
+                    $configure_command .= " --$o[name]";
+                } else {
+                    $configure_command .= " --$o[name]=".trim($r);
+                }
+            }
+        }
+        // }}} end of interactive part
+
+        // FIXME make configurable
+        if (!$user=getenv('USER')) {
+            $user='defaultuser';
+        }
+
+        $tmpdir = $this->config->get('temp_dir');
+        $build_basedir = System::mktemp(' -t "' . $tmpdir . '" -d "pear-build-' . $user . '"');
+        $build_dir = "$build_basedir/$vdir";
+        $inst_dir = "$build_basedir/install-$vdir";
+        $this->log(1, "building in $build_dir");
+        if (is_dir($build_dir)) {
+            System::rm(array('-rf', $build_dir));
+        }
+
+        if (!System::mkDir(array('-p', $build_dir))) {
+            return $this->raiseError("could not create build dir: $build_dir");
+        }
+
+        $this->addTempFile($build_dir);
+        if (!System::mkDir(array('-p', $inst_dir))) {
+            return $this->raiseError("could not create temporary install dir: $inst_dir");
+        }
+        $this->addTempFile($inst_dir);
+
+        $make_command = getenv('MAKE') ? getenv('MAKE') : 'make';
+
+        $to_run = array(
+            $configure_command,
+            $make_command,
+            "$make_command INSTALL_ROOT=\"$inst_dir\" install",
+            "find \"$inst_dir\" | xargs ls -dils"
+            );
+        if (!file_exists($build_dir) || !is_dir($build_dir) || !chdir($build_dir)) {
+            return $this->raiseError("could not chdir to $build_dir");
+        }
+        putenv('PHP_PEAR_VERSION=1.9.4');
+        foreach ($to_run as $cmd) {
+            $err = $this->_runCommand($cmd, $callback);
+            if (PEAR::isError($err)) {
+                chdir($old_cwd);
+                return $err;
+            }
+            if (!$err) {
+                chdir($old_cwd);
+                return $this->raiseError("`$cmd' failed");
+            }
+        }
+        if (!($dp = opendir("modules"))) {
+            chdir($old_cwd);
+            return $this->raiseError("no `modules' directory found");
+        }
+        $built_files = array();
+        $prefix = exec($this->config->get('php_prefix')
+                        . "php-config" .
+                       $this->config->get('php_suffix') . " --prefix");
+        $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files);
+        chdir($old_cwd);
+        return $built_files;
+    }
+
+    /**
+     * Message callback function used when running the "phpize"
+     * program.  Extracts the API numbers used.  Ignores other message
+     * types than "cmdoutput".
+     *
+     * @param string $what the type of message
+     * @param mixed $data the message
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function phpizeCallback($what, $data)
+    {
+        if ($what != 'cmdoutput') {
+            return;
+        }
+        $this->log(1, rtrim($data));
+        if (preg_match('/You should update your .aclocal.m4/', $data)) {
+            return;
+        }
+        $matches = array();
+        if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) {
+            $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1]));
+            $apino = (int)$matches[2];
+            if (isset($this->$member)) {
+                $this->$member = $apino;
+                //$msg = sprintf("%-22s : %d", $matches[1], $apino);
+                //$this->log(1, $msg);
+            }
+        }
+    }
+
+    /**
+     * Run an external command, using a message callback to report
+     * output.  The command will be run through popen and output is
+     * reported for every line with a "cmdoutput" message with the
+     * line string, including newlines, as payload.
+     *
+     * @param string $command the command to run
+     *
+     * @param mixed $callback (optional) function to use as message
+     * callback
+     *
+     * @return bool whether the command was successful (exit code 0
+     * means success, any other means failure)
+     *
+     * @access private
+     */
+    function _runCommand($command, $callback = null)
+    {
+        $this->log(1, "running: $command");
+        $pp = popen("$command 2>&1", "r");
+        if (!$pp) {
+            return $this->raiseError("failed to run `$command'");
+        }
+        if ($callback && $callback[0]->debug == 1) {
+            $olddbg = $callback[0]->debug;
+            $callback[0]->debug = 2;
+        }
+
+        while ($line = fgets($pp, 1024)) {
+            if ($callback) {
+                call_user_func($callback, 'cmdoutput', $line);
+            } else {
+                $this->log(2, rtrim($line));
+            }
+        }
+        if ($callback && isset($olddbg)) {
+            $callback[0]->debug = $olddbg;
+        }
+
+        $exitcode = is_resource($pp) ? pclose($pp) : -1;
+        return ($exitcode == 0);
+    }
+
+    function log($level, $msg)
+    {
+        if ($this->current_callback) {
+            if ($this->debug >= $level) {
+                call_user_func($this->current_callback, 'output', $msg);
+            }
+            return;
+        }
+        return PEAR_Common::log($level, $msg);
+    }
+}
Index: trunk/library/PEAR/PEAR/REST/10.php
===================================================================
--- trunk/library/PEAR/PEAR/REST/10.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/REST/10.php	(revision 5146)
@@ -0,0 +1,871 @@
+<?php
+/**
+ * PEAR_REST_10
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: 10.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a12
+ */
+
+/**
+ * For downloading REST xml/txt files
+ */
+require_once 'PEAR/REST.php';
+
+/**
+ * Implement REST 1.0
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a12
+ */
+class PEAR_REST_10
+{
+    /**
+     * @var PEAR_REST
+     */
+    var $_rest;
+    function PEAR_REST_10($config, $options = array())
+    {
+        $this->_rest = &new PEAR_REST($config, $options);
+    }
+
+    /**
+     * Retrieve information about a remote package to be downloaded from a REST server
+     *
+     * @param string $base The uri to prepend to all REST calls
+     * @param array $packageinfo an array of format:
+     * <pre>
+     *  array(
+     *   'package' => 'packagename',
+     *   'channel' => 'channelname',
+     *  ['state' => 'alpha' (or valid state),]
+     *  -or-
+     *  ['version' => '1.whatever']
+     * </pre>
+     * @param string $prefstate Current preferred_state config variable value
+     * @param bool $installed the installed version of this package to compare against
+     * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
+     */
+    function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
+    {
+        $states = $this->betterStates($prefstate, true);
+        if (!$states) {
+            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
+        }
+
+        $channel  = $packageinfo['channel'];
+        $package  = $packageinfo['package'];
+        $state    = isset($packageinfo['state'])   ? $packageinfo['state']   : null;
+        $version  = isset($packageinfo['version']) ? $packageinfo['version'] : null;
+        $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';
+
+        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
+        if (PEAR::isError($info)) {
+            return PEAR::raiseError('No releases available for package "' .
+                $channel . '/' . $package . '"');
+        }
+
+        if (!isset($info['r'])) {
+            return false;
+        }
+
+        $release = $found = false;
+        if (!is_array($info['r']) || !isset($info['r'][0])) {
+            $info['r'] = array($info['r']);
+        }
+
+        foreach ($info['r'] as $release) {
+            if (!isset($this->_rest->_options['force']) && ($installed &&
+                  version_compare($release['v'], $installed, '<'))) {
+                continue;
+            }
+
+            if (isset($state)) {
+                // try our preferred state first
+                if ($release['s'] == $state) {
+                    $found = true;
+                    break;
+                }
+                // see if there is something newer and more stable
+                // bug #7221
+                if (in_array($release['s'], $this->betterStates($state), true)) {
+                    $found = true;
+                    break;
+                }
+            } elseif (isset($version)) {
+                if ($release['v'] == $version) {
+                    $found = true;
+                    break;
+                }
+            } else {
+                if (in_array($release['s'], $states)) {
+                    $found = true;
+                    break;
+                }
+            }
+        }
+
+        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
+    }
+
+    function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
+                               $prefstate = 'stable', $installed = false, $channel = false)
+    {
+        $states = $this->betterStates($prefstate, true);
+        if (!$states) {
+            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
+        }
+
+        $channel  = $dependency['channel'];
+        $package  = $dependency['name'];
+        $state    = isset($dependency['state'])   ? $dependency['state']   : null;
+        $version  = isset($dependency['version']) ? $dependency['version'] : null;
+        $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';
+
+        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
+        if (PEAR::isError($info)) {
+            return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
+                . '" dependency "' . $channel . '/' . $package . '" has no releases');
+        }
+
+        if (!is_array($info) || !isset($info['r'])) {
+            return false;
+        }
+
+        $exclude = array();
+        $min = $max = $recommended = false;
+        if ($xsdversion == '1.0') {
+            switch ($dependency['rel']) {
+                case 'ge' :
+                    $min = $dependency['version'];
+                break;
+                case 'gt' :
+                    $min = $dependency['version'];
+                    $exclude = array($dependency['version']);
+                break;
+                case 'eq' :
+                    $recommended = $dependency['version'];
+                break;
+                case 'lt' :
+                    $max = $dependency['version'];
+                    $exclude = array($dependency['version']);
+                break;
+                case 'le' :
+                    $max = $dependency['version'];
+                break;
+                case 'ne' :
+                    $exclude = array($dependency['version']);
+                break;
+            }
+        } else {
+            $min = isset($dependency['min']) ? $dependency['min'] : false;
+            $max = isset($dependency['max']) ? $dependency['max'] : false;
+            $recommended = isset($dependency['recommended']) ?
+                $dependency['recommended'] : false;
+            if (isset($dependency['exclude'])) {
+                if (!isset($dependency['exclude'][0])) {
+                    $exclude = array($dependency['exclude']);
+                }
+            }
+        }
+        $release = $found = false;
+        if (!is_array($info['r']) || !isset($info['r'][0])) {
+            $info['r'] = array($info['r']);
+        }
+        foreach ($info['r'] as $release) {
+            if (!isset($this->_rest->_options['force']) && ($installed &&
+                  version_compare($release['v'], $installed, '<'))) {
+                continue;
+            }
+            if (in_array($release['v'], $exclude)) { // skip excluded versions
+                continue;
+            }
+            // allow newer releases to say "I'm OK with the dependent package"
+            if ($xsdversion == '2.0' && isset($release['co'])) {
+                if (!is_array($release['co']) || !isset($release['co'][0])) {
+                    $release['co'] = array($release['co']);
+                }
+                foreach ($release['co'] as $entry) {
+                    if (isset($entry['x']) && !is_array($entry['x'])) {
+                        $entry['x'] = array($entry['x']);
+                    } elseif (!isset($entry['x'])) {
+                        $entry['x'] = array();
+                    }
+                    if ($entry['c'] == $deppackage['channel'] &&
+                          strtolower($entry['p']) == strtolower($deppackage['package']) &&
+                          version_compare($deppackage['version'], $entry['min'], '>=') &&
+                          version_compare($deppackage['version'], $entry['max'], '<=') &&
+                          !in_array($release['v'], $entry['x'])) {
+                        $recommended = $release['v'];
+                        break;
+                    }
+                }
+            }
+            if ($recommended) {
+                if ($release['v'] != $recommended) { // if we want a specific
+                    // version, then skip all others
+                    continue;
+                } else {
+                    if (!in_array($release['s'], $states)) {
+                        // the stability is too low, but we must return the
+                        // recommended version if possible
+                        return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
+                    }
+                }
+            }
+            if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
+                continue;
+            }
+            if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
+                continue;
+            }
+            if ($installed && version_compare($release['v'], $installed, '<')) {
+                continue;
+            }
+            if (in_array($release['s'], $states)) { // if in the preferred state...
+                $found = true; // ... then use it
+                break;
+            }
+        }
+        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
+    }
+
+    /**
+     * Take raw data and return the array needed for processing a download URL
+     *
+     * @param string $base REST base uri
+     * @param string $package Package name
+     * @param array $release an array of format array('v' => version, 's' => state)
+     *                       describing the release to download
+     * @param array $info list of all releases as defined by allreleases.xml
+     * @param bool|null $found determines whether the release was found or this is the next
+     *                    best alternative.  If null, then versions were skipped because
+     *                    of PHP dependency
+     * @return array|PEAR_Error
+     * @access private
+     */
+    function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false)
+    {
+        if (!$found) {
+            $release = $info['r'][0];
+        }
+
+        $packageLower = strtolower($package);
+        $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . $packageLower . '/' .
+            'info.xml', false, false, $channel);
+        if (PEAR::isError($pinfo)) {
+            return PEAR::raiseError('Package "' . $package .
+                '" does not have REST info xml available');
+        }
+
+        $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
+            $release['v'] . '.xml', false, false, $channel);
+        if (PEAR::isError($releaseinfo)) {
+            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
+                '" does not have REST xml available');
+        }
+
+        $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
+            'deps.' . $release['v'] . '.txt', false, true, $channel);
+        if (PEAR::isError($packagexml)) {
+            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
+                '" does not have REST dependency information available');
+        }
+
+        $packagexml = unserialize($packagexml);
+        if (!$packagexml) {
+            $packagexml = array();
+        }
+
+        $allinfo = $this->_rest->retrieveData($base . 'r/' . $packageLower .
+            '/allreleases.xml', false, false, $channel);
+        if (PEAR::isError($allinfo)) {
+            return $allinfo;
+        }
+
+        if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) {
+            $allinfo['r'] = array($allinfo['r']);
+        }
+
+        $compatible = false;
+        foreach ($allinfo['r'] as $release) {
+            if ($release['v'] != $releaseinfo['v']) {
+                continue;
+            }
+
+            if (!isset($release['co'])) {
+                break;
+            }
+
+            $compatible = array();
+            if (!is_array($release['co']) || !isset($release['co'][0])) {
+                $release['co'] = array($release['co']);
+            }
+
+            foreach ($release['co'] as $entry) {
+                $comp = array();
+                $comp['name']    = $entry['p'];
+                $comp['channel'] = $entry['c'];
+                $comp['min']     = $entry['min'];
+                $comp['max']     = $entry['max'];
+                if (isset($entry['x']) && !is_array($entry['x'])) {
+                    $comp['exclude'] = $entry['x'];
+                }
+
+                $compatible[] = $comp;
+            }
+
+            if (count($compatible) == 1) {
+                $compatible = $compatible[0];
+            }
+
+            break;
+        }
+
+        $deprecated = false;
+        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
+            if (is_array($pinfo['dp'])) {
+                $deprecated = array('channel' => (string) $pinfo['dc'],
+                                    'package' => trim($pinfo['dp']['_content']));
+            } else {
+                $deprecated = array('channel' => (string) $pinfo['dc'],
+                                    'package' => trim($pinfo['dp']));
+            }
+        }
+
+        $return = array(
+            'version'    => $releaseinfo['v'],
+            'info'       => $packagexml,
+            'package'    => $releaseinfo['p']['_content'],
+            'stability'  => $releaseinfo['st'],
+            'compatible' => $compatible,
+            'deprecated' => $deprecated,
+        );
+
+        if ($found) {
+            $return['url'] = $releaseinfo['g'];
+            return $return;
+        }
+
+        $return['php'] = $phpversion;
+        return $return;
+    }
+
+    function listPackages($base, $channel = false)
+    {
+        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
+        if (PEAR::isError($packagelist)) {
+            return $packagelist;
+        }
+
+        if (!is_array($packagelist) || !isset($packagelist['p'])) {
+            return array();
+        }
+
+        if (!is_array($packagelist['p'])) {
+            $packagelist['p'] = array($packagelist['p']);
+        }
+
+        return $packagelist['p'];
+    }
+
+    /**
+     * List all categories of a REST server
+     *
+     * @param string $base base URL of the server
+     * @return array of categorynames
+     */
+    function listCategories($base, $channel = false)
+    {
+        $categories = array();
+
+        // c/categories.xml does not exist;
+        // check for every package its category manually
+        // This is SLOOOWWWW : ///
+        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
+        if (PEAR::isError($packagelist)) {
+            return $packagelist;
+        }
+
+        if (!is_array($packagelist) || !isset($packagelist['p'])) {
+            $ret = array();
+            return $ret;
+        }
+
+        if (!is_array($packagelist['p'])) {
+            $packagelist['p'] = array($packagelist['p']);
+        }
+
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+        foreach ($packagelist['p'] as $package) {
+                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
+                if (PEAR::isError($inf)) {
+                    PEAR::popErrorHandling();
+                    return $inf;
+                }
+                $cat = $inf['ca']['_content'];
+                if (!isset($categories[$cat])) {
+                    $categories[$cat] = $inf['ca'];
+                }
+        }
+
+        return array_values($categories);
+    }
+
+    /**
+     * List a category of a REST server
+     *
+     * @param string $base base URL of the server
+     * @param string $category name of the category
+     * @param boolean $info also download full package info
+     * @return array of packagenames
+     */
+    function listCategory($base, $category, $info = false, $channel = false)
+    {
+        // gives '404 Not Found' error when category doesn't exist
+        $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel);
+        if (PEAR::isError($packagelist)) {
+            return $packagelist;
+        }
+
+        if (!is_array($packagelist) || !isset($packagelist['p'])) {
+            return array();
+        }
+
+        if (!is_array($packagelist['p']) ||
+            !isset($packagelist['p'][0])) { // only 1 pkg
+            $packagelist = array($packagelist['p']);
+        } else {
+            $packagelist = $packagelist['p'];
+        }
+
+        if ($info == true) {
+            // get individual package info
+            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+            foreach ($packagelist as $i => $packageitem) {
+                $url = sprintf('%s'.'r/%s/latest.txt',
+                        $base,
+                        strtolower($packageitem['_content']));
+                $version = $this->_rest->retrieveData($url, false, false, $channel);
+                if (PEAR::isError($version)) {
+                    break; // skipit
+                }
+                $url = sprintf('%s'.'r/%s/%s.xml',
+                        $base,
+                        strtolower($packageitem['_content']),
+                        $version);
+                $info = $this->_rest->retrieveData($url, false, false, $channel);
+                if (PEAR::isError($info)) {
+                    break; // skipit
+                }
+                $packagelist[$i]['info'] = $info;
+            }
+            PEAR::popErrorHandling();
+        }
+
+        return $packagelist;
+    }
+
+
+    function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
+    {
+        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
+        if (PEAR::isError($packagelist)) {
+            return $packagelist;
+        }
+        if ($this->_rest->config->get('verbose') > 0) {
+            $ui = &PEAR_Frontend::singleton();
+            $ui->log('Retrieving data...0%', true);
+        }
+        $ret = array();
+        if (!is_array($packagelist) || !isset($packagelist['p'])) {
+            return $ret;
+        }
+        if (!is_array($packagelist['p'])) {
+            $packagelist['p'] = array($packagelist['p']);
+        }
+
+        // only search-packagename = quicksearch !
+        if ($searchpackage && (!$searchsummary || empty($searchpackage))) {
+            $newpackagelist = array();
+            foreach ($packagelist['p'] as $package) {
+                if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) {
+                    $newpackagelist[] = $package;
+                }
+            }
+            $packagelist['p'] = $newpackagelist;
+        }
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+        $next = .1;
+        foreach ($packagelist['p'] as $progress => $package) {
+            if ($this->_rest->config->get('verbose') > 0) {
+                if ($progress / count($packagelist['p']) >= $next) {
+                    if ($next == .5) {
+                        $ui->log('50%', false);
+                    } else {
+                        $ui->log('.', false);
+                    }
+                    $next += .1;
+                }
+            }
+
+            if ($basic) { // remote-list command
+                if ($dostable) {
+                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
+                        '/stable.txt', false, false, $channel);
+                } else {
+                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
+                        '/latest.txt', false, false, $channel);
+                }
+                if (PEAR::isError($latest)) {
+                    $latest = false;
+                }
+                $info = array('stable' => $latest);
+            } else { // list-all command
+                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
+                if (PEAR::isError($inf)) {
+                    PEAR::popErrorHandling();
+                    return $inf;
+                }
+                if ($searchpackage) {
+                    $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false);
+                    if (!$found && !(isset($searchsummary) && !empty($searchsummary)
+                        && (stristr($inf['s'], $searchsummary) !== false
+                            || stristr($inf['d'], $searchsummary) !== false)))
+                    {
+                        continue;
+                    };
+                }
+                $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
+                    '/allreleases.xml', false, false, $channel);
+                if (PEAR::isError($releases)) {
+                    continue;
+                }
+                if (!isset($releases['r'][0])) {
+                    $releases['r'] = array($releases['r']);
+                }
+                unset($latest);
+                unset($unstable);
+                unset($stable);
+                unset($state);
+                foreach ($releases['r'] as $release) {
+                    if (!isset($latest)) {
+                        if ($dostable && $release['s'] == 'stable') {
+                            $latest = $release['v'];
+                            $state = 'stable';
+                        }
+                        if (!$dostable) {
+                            $latest = $release['v'];
+                            $state = $release['s'];
+                        }
+                    }
+                    if (!isset($stable) && $release['s'] == 'stable') {
+                        $stable = $release['v'];
+                        if (!isset($unstable)) {
+                            $unstable = $stable;
+                        }
+                    }
+                    if (!isset($unstable) && $release['s'] != 'stable') {
+                        $latest = $unstable = $release['v'];
+                        $state = $release['s'];
+                    }
+                    if (isset($latest) && !isset($state)) {
+                        $state = $release['s'];
+                    }
+                    if (isset($latest) && isset($stable) && isset($unstable)) {
+                        break;
+                    }
+                }
+                $deps = array();
+                if (!isset($unstable)) {
+                    $unstable = false;
+                    $state = 'stable';
+                    if (isset($stable)) {
+                        $latest = $unstable = $stable;
+                    }
+                } else {
+                    $latest = $unstable;
+                }
+                if (!isset($latest)) {
+                    $latest = false;
+                }
+                if ($latest) {
+                    $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
+                        $latest . '.txt', false, false, $channel);
+                    if (!PEAR::isError($d)) {
+                        $d = unserialize($d);
+                        if ($d) {
+                            if (isset($d['required'])) {
+                                if (!class_exists('PEAR_PackageFile_v2')) {
+                                    require_once 'PEAR/PackageFile/v2.php';
+                                }
+                                if (!isset($pf)) {
+                                    $pf = new PEAR_PackageFile_v2;
+                                }
+                                $pf->setDeps($d);
+                                $tdeps = $pf->getDeps();
+                            } else {
+                                $tdeps = $d;
+                            }
+                            foreach ($tdeps as $dep) {
+                                if ($dep['type'] !== 'pkg') {
+                                    continue;
+                                }
+                                $deps[] = $dep;
+                            }
+                        }
+                    }
+                }
+                if (!isset($stable)) {
+                    $stable = '-n/a-';
+                }
+                if (!$searchpackage) {
+                    $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' =>
+                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
+                        'unstable' => $unstable, 'state' => $state);
+                } else {
+                    $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' =>
+                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
+                        'unstable' => $unstable, 'state' => $state);
+                }
+            }
+            $ret[$package] = $info;
+        }
+        PEAR::popErrorHandling();
+        return $ret;
+    }
+
+    function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg)
+    {
+        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
+        if (PEAR::isError($packagelist)) {
+            return $packagelist;
+        }
+
+        $ret = array();
+        if (!is_array($packagelist) || !isset($packagelist['p'])) {
+            return $ret;
+        }
+
+        if (!is_array($packagelist['p'])) {
+            $packagelist['p'] = array($packagelist['p']);
+        }
+
+        foreach ($packagelist['p'] as $package) {
+            if (!isset($installed[strtolower($package)])) {
+                continue;
+            }
+
+            $inst_version = $reg->packageInfo($package, 'version', $channel);
+            $inst_state   = $reg->packageInfo($package, 'release_state', $channel);
+            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+            $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
+                '/allreleases.xml', false, false, $channel);
+            PEAR::popErrorHandling();
+            if (PEAR::isError($info)) {
+                continue; // no remote releases
+            }
+
+            if (!isset($info['r'])) {
+                continue;
+            }
+
+            $release = $found = false;
+            if (!is_array($info['r']) || !isset($info['r'][0])) {
+                $info['r'] = array($info['r']);
+            }
+
+            // $info['r'] is sorted by version number
+            usort($info['r'], array($this, '_sortReleasesByVersionNumber'));
+            foreach ($info['r'] as $release) {
+                if ($inst_version && version_compare($release['v'], $inst_version, '<=')) {
+                    // not newer than the one installed
+                    break;
+                }
+
+                // new version > installed version
+                if (!$pref_state) {
+                    // every state is a good state
+                    $found = true;
+                    break;
+                } else {
+                    $new_state = $release['s'];
+                    // if new state >= installed state: go
+                    if (in_array($new_state, $this->betterStates($inst_state, true))) {
+                        $found = true;
+                        break;
+                    } else {
+                        // only allow to lower the state of package,
+                        // if new state >= preferred state: go
+                        if (in_array($new_state, $this->betterStates($pref_state, true))) {
+                            $found = true;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if (!$found) {
+                continue;
+            }
+
+            $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
+                $release['v'] . '.xml', false, false, $channel);
+            if (PEAR::isError($relinfo)) {
+                return $relinfo;
+            }
+
+            $ret[$package] = array(
+                'version'  => $release['v'],
+                'state'    => $release['s'],
+                'filesize' => $relinfo['f'],
+            );
+        }
+
+        return $ret;
+    }
+
+    function packageInfo($base, $package, $channel = false)
+    {
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+        $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
+        if (PEAR::isError($pinfo)) {
+            PEAR::popErrorHandling();
+            return PEAR::raiseError('Unknown package: "' . $package . '" in channel "' . $channel . '"' . "\n". 'Debug: ' .
+                $pinfo->getMessage());
+        }
+
+        $releases = array();
+        $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
+            '/allreleases.xml', false, false, $channel);
+        if (!PEAR::isError($allreleases)) {
+            if (!class_exists('PEAR_PackageFile_v2')) {
+                require_once 'PEAR/PackageFile/v2.php';
+            }
+
+            if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) {
+                $allreleases['r'] = array($allreleases['r']);
+            }
+
+            $pf = new PEAR_PackageFile_v2;
+            foreach ($allreleases['r'] as $release) {
+                $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
+                    $release['v'] . '.txt', false, false, $channel);
+                if (PEAR::isError($ds)) {
+                    continue;
+                }
+
+                if (!isset($latest)) {
+                    $latest = $release['v'];
+                }
+
+                $pf->setDeps(unserialize($ds));
+                $ds = $pf->getDeps();
+                $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package)
+                    . '/' . $release['v'] . '.xml', false, false, $channel);
+
+                if (PEAR::isError($info)) {
+                    continue;
+                }
+
+                $releases[$release['v']] = array(
+                    'doneby' => $info['m'],
+                    'license' => $info['l'],
+                    'summary' => $info['s'],
+                    'description' => $info['d'],
+                    'releasedate' => $info['da'],
+                    'releasenotes' => $info['n'],
+                    'state' => $release['s'],
+                    'deps' => $ds ? $ds : array(),
+                );
+            }
+        } else {
+            $latest = '';
+        }
+
+        PEAR::popErrorHandling();
+        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
+            if (is_array($pinfo['dp'])) {
+                $deprecated = array('channel' => (string) $pinfo['dc'],
+                                    'package' => trim($pinfo['dp']['_content']));
+            } else {
+                $deprecated = array('channel' => (string) $pinfo['dc'],
+                                    'package' => trim($pinfo['dp']));
+            }
+        } else {
+            $deprecated = false;
+        }
+
+        if (!isset($latest)) {
+            $latest = '';
+        }
+
+        return array(
+            'name' => $pinfo['n'],
+            'channel' => $pinfo['c'],
+            'category' => $pinfo['ca']['_content'],
+            'stable' => $latest,
+            'license' => $pinfo['l'],
+            'summary' => $pinfo['s'],
+            'description' => $pinfo['d'],
+            'releases' => $releases,
+            'deprecated' => $deprecated,
+            );
+    }
+
+    /**
+     * Return an array containing all of the states that are more stable than
+     * or equal to the passed in state
+     *
+     * @param string Release state
+     * @param boolean Determines whether to include $state in the list
+     * @return false|array False if $state is not a valid release state
+     */
+    function betterStates($state, $include = false)
+    {
+        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
+        $i = array_search($state, $states);
+        if ($i === false) {
+            return false;
+        }
+
+        if ($include) {
+            $i--;
+        }
+
+        return array_slice($states, $i + 1);
+    }
+
+    /**
+     * Sort releases by version number
+     *
+     * @access private
+     */
+    function _sortReleasesByVersionNumber($a, $b)
+    {
+        if (version_compare($a['v'], $b['v'], '=')) {
+            return 0;
+        }
+
+        if (version_compare($a['v'], $b['v'], '>')) {
+            return -1;
+        }
+
+        if (version_compare($a['v'], $b['v'], '<')) {
+            return 1;
+        }
+    }
+}
Index: trunk/library/PEAR/PEAR/REST/11.php
===================================================================
--- trunk/library/PEAR/PEAR/REST/11.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/REST/11.php	(revision 5146)
@@ -0,0 +1,341 @@
+<?php
+/**
+ * PEAR_REST_11 - implement faster list-all/remote-list command
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: 11.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.3
+ */
+
+/**
+ * For downloading REST xml/txt files
+ */
+require_once 'PEAR/REST.php';
+
+/**
+ * Implement REST 1.1
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.3
+ */
+class PEAR_REST_11
+{
+    /**
+     * @var PEAR_REST
+     */
+    var $_rest;
+
+    function PEAR_REST_11($config, $options = array())
+    {
+        $this->_rest = &new PEAR_REST($config, $options);
+    }
+
+    function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
+    {
+        $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel);
+        if (PEAR::isError($categorylist)) {
+            return $categorylist;
+        }
+
+        $ret = array();
+        if (!is_array($categorylist['c']) || !isset($categorylist['c'][0])) {
+            $categorylist['c'] = array($categorylist['c']);
+        }
+
+        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+
+        foreach ($categorylist['c'] as $progress => $category) {
+            $category = $category['_content'];
+            $packagesinfo = $this->_rest->retrieveData($base .
+                'c/' . urlencode($category) . '/packagesinfo.xml', false, false, $channel);
+
+            if (PEAR::isError($packagesinfo)) {
+                continue;
+            }
+
+            if (!is_array($packagesinfo) || !isset($packagesinfo['pi'])) {
+                continue;
+            }
+
+            if (!is_array($packagesinfo['pi']) || !isset($packagesinfo['pi'][0])) {
+                $packagesinfo['pi'] = array($packagesinfo['pi']);
+            }
+
+            foreach ($packagesinfo['pi'] as $packageinfo) {
+                if (empty($packageinfo)) {
+                    continue;
+                }
+
+                $info     = $packageinfo['p'];
+                $package  = $info['n'];
+                $releases = isset($packageinfo['a']) ? $packageinfo['a'] : false;
+                unset($latest);
+                unset($unstable);
+                unset($stable);
+                unset($state);
+
+                if ($releases) {
+                    if (!isset($releases['r'][0])) {
+                        $releases['r'] = array($releases['r']);
+                    }
+
+                    foreach ($releases['r'] as $release) {
+                        if (!isset($latest)) {
+                            if ($dostable && $release['s'] == 'stable') {
+                                $latest = $release['v'];
+                                $state = 'stable';
+                            }
+                            if (!$dostable) {
+                                $latest = $release['v'];
+                                $state = $release['s'];
+                            }
+                        }
+
+                        if (!isset($stable) && $release['s'] == 'stable') {
+                            $stable = $release['v'];
+                            if (!isset($unstable)) {
+                                $unstable = $stable;
+                            }
+                        }
+
+                        if (!isset($unstable) && $release['s'] != 'stable') {
+                            $unstable = $release['v'];
+                            $state = $release['s'];
+                        }
+
+                        if (isset($latest) && !isset($state)) {
+                            $state = $release['s'];
+                        }
+
+                        if (isset($latest) && isset($stable) && isset($unstable)) {
+                            break;
+                        }
+                    }
+                }
+
+                if ($basic) { // remote-list command
+                    if (!isset($latest)) {
+                        $latest = false;
+                    }
+
+                    if ($dostable) {
+                        // $state is not set if there are no releases
+                        if (isset($state) && $state == 'stable') {
+                            $ret[$package] = array('stable' => $latest);
+                        } else {
+                            $ret[$package] = array('stable' => '-n/a-');
+                        }
+                    } else {
+                        $ret[$package] = array('stable' => $latest);
+                    }
+
+                    continue;
+                }
+
+                // list-all command
+                if (!isset($unstable)) {
+                    $unstable = false;
+                    $state = 'stable';
+                    if (isset($stable)) {
+                        $latest = $unstable = $stable;
+                    }
+                } else {
+                    $latest = $unstable;
+                }
+
+                if (!isset($latest)) {
+                    $latest = false;
+                }
+
+                $deps = array();
+                if ($latest && isset($packageinfo['deps'])) {
+                    if (!is_array($packageinfo['deps']) ||
+                          !isset($packageinfo['deps'][0])
+                    ) {
+                        $packageinfo['deps'] = array($packageinfo['deps']);
+                    }
+
+                    $d = false;
+                    foreach ($packageinfo['deps'] as $dep) {
+                        if ($dep['v'] == $latest) {
+                            $d = unserialize($dep['d']);
+                        }
+                    }
+
+                    if ($d) {
+                        if (isset($d['required'])) {
+                            if (!class_exists('PEAR_PackageFile_v2')) {
+                                require_once 'PEAR/PackageFile/v2.php';
+                            }
+
+                            if (!isset($pf)) {
+                                $pf = new PEAR_PackageFile_v2;
+                            }
+
+                            $pf->setDeps($d);
+                            $tdeps = $pf->getDeps();
+                        } else {
+                            $tdeps = $d;
+                        }
+
+                        foreach ($tdeps as $dep) {
+                            if ($dep['type'] !== 'pkg') {
+                                continue;
+                            }
+
+                            $deps[] = $dep;
+                        }
+                    }
+                }
+
+                $info = array(
+                    'stable'      => $latest,
+                    'summary'     => $info['s'],
+                    'description' => $info['d'],
+                    'deps'        => $deps,
+                    'category'    => $info['ca']['_content'],
+                    'unstable'    => $unstable,
+                    'state'       => $state
+                );
+                $ret[$package] = $info;
+            }
+        }
+
+        PEAR::popErrorHandling();
+        return $ret;
+    }
+
+    /**
+     * List all categories of a REST server
+     *
+     * @param string $base base URL of the server
+     * @return array of categorynames
+     */
+    function listCategories($base, $channel = false)
+    {
+        $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel);
+        if (PEAR::isError($categorylist)) {
+            return $categorylist;
+        }
+
+        if (!is_array($categorylist) || !isset($categorylist['c'])) {
+            return array();
+        }
+
+        if (isset($categorylist['c']['_content'])) {
+            // only 1 category
+            $categorylist['c'] = array($categorylist['c']);
+        }
+
+        return $categorylist['c'];
+    }
+
+    /**
+     * List packages in a category of a REST server
+     *
+     * @param string $base base URL of the server
+     * @param string $category name of the category
+     * @param boolean $info also download full package info
+     * @return array of packagenames
+     */
+    function listCategory($base, $category, $info = false, $channel = false)
+    {
+        if ($info == false) {
+            $url = '%s'.'c/%s/packages.xml';
+        } else {
+            $url = '%s'.'c/%s/packagesinfo.xml';
+        }
+        $url = sprintf($url,
+                    $base,
+                    urlencode($category));
+
+        // gives '404 Not Found' error when category doesn't exist
+        $packagelist = $this->_rest->retrieveData($url, false, false, $channel);
+        if (PEAR::isError($packagelist)) {
+            return $packagelist;
+        }
+        if (!is_array($packagelist)) {
+            return array();
+        }
+
+        if ($info == false) {
+            if (!isset($packagelist['p'])) {
+                return array();
+            }
+            if (!is_array($packagelist['p']) ||
+                !isset($packagelist['p'][0])) { // only 1 pkg
+                $packagelist = array($packagelist['p']);
+            } else {
+                $packagelist = $packagelist['p'];
+            }
+            return $packagelist;
+        }
+
+        // info == true
+        if (!isset($packagelist['pi'])) {
+            return array();
+        }
+
+        if (!is_array($packagelist['pi']) ||
+            !isset($packagelist['pi'][0])) { // only 1 pkg
+            $packagelist_pre = array($packagelist['pi']);
+        } else {
+            $packagelist_pre = $packagelist['pi'];
+        }
+
+        $packagelist = array();
+        foreach ($packagelist_pre as $i => $item) {
+            // compatibility with r/<latest.txt>.xml
+            if (isset($item['a']['r'][0])) {
+                // multiple releases
+                $item['p']['v'] = $item['a']['r'][0]['v'];
+                $item['p']['st'] = $item['a']['r'][0]['s'];
+            } elseif (isset($item['a'])) {
+                // first and only release
+                $item['p']['v'] = $item['a']['r']['v'];
+                $item['p']['st'] = $item['a']['r']['s'];
+            }
+
+            $packagelist[$i] = array('attribs' => $item['p']['r'],
+                                     '_content' => $item['p']['n'],
+                                     'info' => $item['p']);
+        }
+
+        return $packagelist;
+    }
+
+    /**
+     * Return an array containing all of the states that are more stable than
+     * or equal to the passed in state
+     *
+     * @param string Release state
+     * @param boolean Determines whether to include $state in the list
+     * @return false|array False if $state is not a valid release state
+     */
+    function betterStates($state, $include = false)
+    {
+        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
+        $i = array_search($state, $states);
+        if ($i === false) {
+            return false;
+        }
+        if ($include) {
+            $i--;
+        }
+        return array_slice($states, $i + 1);
+    }
+}
+?>
Index: trunk/library/PEAR/PEAR/REST/13.php
===================================================================
--- trunk/library/PEAR/PEAR/REST/13.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/REST/13.php	(revision 5146)
@@ -0,0 +1,299 @@
+<?php
+/**
+ * PEAR_REST_13
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: 13.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a12
+ */
+
+/**
+ * For downloading REST xml/txt files
+ */
+require_once 'PEAR/REST.php';
+require_once 'PEAR/REST/10.php';
+
+/**
+ * Implement REST 1.3
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a12
+ */
+class PEAR_REST_13 extends PEAR_REST_10
+{
+    /**
+     * Retrieve information about a remote package to be downloaded from a REST server
+     *
+     * This is smart enough to resolve the minimum PHP version dependency prior to download
+     * @param string $base The uri to prepend to all REST calls
+     * @param array $packageinfo an array of format:
+     * <pre>
+     *  array(
+     *   'package' => 'packagename',
+     *   'channel' => 'channelname',
+     *  ['state' => 'alpha' (or valid state),]
+     *  -or-
+     *  ['version' => '1.whatever']
+     * </pre>
+     * @param string $prefstate Current preferred_state config variable value
+     * @param bool $installed the installed version of this package to compare against
+     * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
+     */
+    function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
+    {
+        $states = $this->betterStates($prefstate, true);
+        if (!$states) {
+            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
+        }
+
+        $channel  = $packageinfo['channel'];
+        $package  = $packageinfo['package'];
+        $state    = isset($packageinfo['state'])   ? $packageinfo['state']   : null;
+        $version  = isset($packageinfo['version']) ? $packageinfo['version'] : null;
+        $restFile = $base . 'r/' . strtolower($package) . '/allreleases2.xml';
+
+        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
+        if (PEAR::isError($info)) {
+            return PEAR::raiseError('No releases available for package "' .
+                $channel . '/' . $package . '"');
+        }
+
+        if (!isset($info['r'])) {
+            return false;
+        }
+
+        $release = $found = false;
+        if (!is_array($info['r']) || !isset($info['r'][0])) {
+            $info['r'] = array($info['r']);
+        }
+
+        $skippedphp = false;
+        foreach ($info['r'] as $release) {
+            if (!isset($this->_rest->_options['force']) && ($installed &&
+                  version_compare($release['v'], $installed, '<'))) {
+                continue;
+            }
+
+            if (isset($state)) {
+                // try our preferred state first
+                if ($release['s'] == $state) {
+                    if (!isset($version) && version_compare($release['m'], phpversion(), '>')) {
+                        // skip releases that require a PHP version newer than our PHP version
+                        $skippedphp = $release;
+                        continue;
+                    }
+                    $found = true;
+                    break;
+                }
+
+                // see if there is something newer and more stable
+                // bug #7221
+                if (in_array($release['s'], $this->betterStates($state), true)) {
+                    if (!isset($version) && version_compare($release['m'], phpversion(), '>')) {
+                        // skip releases that require a PHP version newer than our PHP version
+                        $skippedphp = $release;
+                        continue;
+                    }
+                    $found = true;
+                    break;
+                }
+            } elseif (isset($version)) {
+                if ($release['v'] == $version) {
+                    if (!isset($this->_rest->_options['force']) &&
+                          !isset($version) &&
+                          version_compare($release['m'], phpversion(), '>')) {
+                        // skip releases that require a PHP version newer than our PHP version
+                        $skippedphp = $release;
+                        continue;
+                    }
+                    $found = true;
+                    break;
+                }
+            } else {
+                if (in_array($release['s'], $states)) {
+                    if (version_compare($release['m'], phpversion(), '>')) {
+                        // skip releases that require a PHP version newer than our PHP version
+                        $skippedphp = $release;
+                        continue;
+                    }
+                    $found = true;
+                    break;
+                }
+            }
+        }
+
+        if (!$found && $skippedphp) {
+            $found = null;
+        }
+
+        return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel);
+    }
+
+    function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
+                               $prefstate = 'stable', $installed = false, $channel = false)
+    {
+        $states = $this->betterStates($prefstate, true);
+        if (!$states) {
+            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
+        }
+
+        $channel  = $dependency['channel'];
+        $package  = $dependency['name'];
+        $state    = isset($dependency['state'])   ? $dependency['state']   : null;
+        $version  = isset($dependency['version']) ? $dependency['version'] : null;
+        $restFile = $base . 'r/' . strtolower($package) .'/allreleases2.xml';
+
+        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
+        if (PEAR::isError($info)) {
+            return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
+                . '" dependency "' . $channel . '/' . $package . '" has no releases');
+        }
+
+        if (!is_array($info) || !isset($info['r'])) {
+            return false;
+        }
+
+        $exclude = array();
+        $min = $max = $recommended = false;
+        if ($xsdversion == '1.0') {
+            $pinfo['package'] = $dependency['name'];
+            $pinfo['channel'] = 'pear.php.net'; // this is always true - don't change this
+            switch ($dependency['rel']) {
+                case 'ge' :
+                    $min = $dependency['version'];
+                break;
+                case 'gt' :
+                    $min = $dependency['version'];
+                    $exclude = array($dependency['version']);
+                break;
+                case 'eq' :
+                    $recommended = $dependency['version'];
+                break;
+                case 'lt' :
+                    $max = $dependency['version'];
+                    $exclude = array($dependency['version']);
+                break;
+                case 'le' :
+                    $max = $dependency['version'];
+                break;
+                case 'ne' :
+                    $exclude = array($dependency['version']);
+                break;
+            }
+        } else {
+            $pinfo['package'] = $dependency['name'];
+            $min = isset($dependency['min']) ? $dependency['min'] : false;
+            $max = isset($dependency['max']) ? $dependency['max'] : false;
+            $recommended = isset($dependency['recommended']) ?
+                $dependency['recommended'] : false;
+            if (isset($dependency['exclude'])) {
+                if (!isset($dependency['exclude'][0])) {
+                    $exclude = array($dependency['exclude']);
+                }
+            }
+        }
+
+        $skippedphp = $found = $release = false;
+        if (!is_array($info['r']) || !isset($info['r'][0])) {
+            $info['r'] = array($info['r']);
+        }
+
+        foreach ($info['r'] as $release) {
+            if (!isset($this->_rest->_options['force']) && ($installed &&
+                  version_compare($release['v'], $installed, '<'))) {
+                continue;
+            }
+
+            if (in_array($release['v'], $exclude)) { // skip excluded versions
+                continue;
+            }
+
+            // allow newer releases to say "I'm OK with the dependent package"
+            if ($xsdversion == '2.0' && isset($release['co'])) {
+                if (!is_array($release['co']) || !isset($release['co'][0])) {
+                    $release['co'] = array($release['co']);
+                }
+
+                foreach ($release['co'] as $entry) {
+                    if (isset($entry['x']) && !is_array($entry['x'])) {
+                        $entry['x'] = array($entry['x']);
+                    } elseif (!isset($entry['x'])) {
+                        $entry['x'] = array();
+                    }
+
+                    if ($entry['c'] == $deppackage['channel'] &&
+                          strtolower($entry['p']) == strtolower($deppackage['package']) &&
+                          version_compare($deppackage['version'], $entry['min'], '>=') &&
+                          version_compare($deppackage['version'], $entry['max'], '<=') &&
+                          !in_array($release['v'], $entry['x'])) {
+                        if (version_compare($release['m'], phpversion(), '>')) {
+                            // skip dependency releases that require a PHP version
+                            // newer than our PHP version
+                            $skippedphp = $release;
+                            continue;
+                        }
+
+                        $recommended = $release['v'];
+                        break;
+                    }
+                }
+            }
+
+            if ($recommended) {
+                if ($release['v'] != $recommended) { // if we want a specific
+                    // version, then skip all others
+                    continue;
+                }
+
+                if (!in_array($release['s'], $states)) {
+                    // the stability is too low, but we must return the
+                    // recommended version if possible
+                    return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
+                }
+            }
+
+            if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
+                continue;
+            }
+
+            if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
+                continue;
+            }
+
+            if ($installed && version_compare($release['v'], $installed, '<')) {
+                continue;
+            }
+
+            if (in_array($release['s'], $states)) { // if in the preferred state...
+                if (version_compare($release['m'], phpversion(), '>')) {
+                    // skip dependency releases that require a PHP version
+                    // newer than our PHP version
+                    $skippedphp = $release;
+                    continue;
+                }
+
+                $found = true; // ... then use it
+                break;
+            }
+        }
+
+        if (!$found && $skippedphp) {
+            $found = null;
+        }
+
+        return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel);
+    }
+}
Index: trunk/library/PEAR/PEAR/Common.php
===================================================================
--- trunk/library/PEAR/PEAR/Common.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Common.php	(revision 5146)
@@ -0,0 +1,837 @@
+<?php
+/**
+ * PEAR_Common, the base class for the PEAR Installer
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1.0
+ * @deprecated File deprecated since Release 1.4.0a1
+ */
+
+/**
+ * Include error handling
+ */
+require_once 'PEAR.php';
+
+/**
+ * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
+ */
+define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
+define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
+define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/');
+
+// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
+define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
+define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i');
+
+// XXX far from perfect :-)
+define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG .
+    ')(-([.0-9a-zA-Z]+))?');
+define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG .
+    '\\z/');
+
+define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+');
+define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/');
+
+// this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED
+define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*');
+define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i');
+
+define('_PEAR_CHANNELS_PACKAGE_PREG',  '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/('
+         . _PEAR_COMMON_PACKAGE_NAME_PREG . ')');
+define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i');
+
+define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::('
+    . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?');
+define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/');
+
+/**
+ * List of temporary files and directories registered by
+ * PEAR_Common::addTempFile().
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_tempfiles'] = array();
+
+/**
+ * Valid maintainer roles
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
+
+/**
+ * Valid release states
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
+
+/**
+ * Valid dependency types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
+
+/**
+ * Valid dependency relations
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');
+
+/**
+ * Valid file roles
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
+
+/**
+ * Valid replacement types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
+
+/**
+ * Valid "provide" types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
+
+/**
+ * Valid "provide" types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');
+
+/**
+ * Class providing common functionality for PEAR administration classes.
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ * @deprecated This class will disappear, and its components will be spread
+ *             into smaller classes, like the AT&T breakup, as of Release 1.4.0a1
+ */
+class PEAR_Common extends PEAR
+{
+    /**
+     * User Interface object (PEAR_Frontend_* class).  If null,
+     * the log() method uses print.
+     * @var object
+     */
+    var $ui = null;
+
+    /**
+     * Configuration object (PEAR_Config).
+     * @var PEAR_Config
+     */
+    var $config = null;
+
+    /** stack of elements, gives some sort of XML context */
+    var $element_stack = array();
+
+    /** name of currently parsed XML element */
+    var $current_element;
+
+    /** array of attributes of the currently parsed XML element */
+    var $current_attributes = array();
+
+    /** assoc with information about a package */
+    var $pkginfo = array();
+
+    var $current_path = null;
+
+    /**
+     * Flag variable used to mark a valid package file
+     * @var boolean
+     * @access private
+     */
+    var $_validPackageFile;
+
+    /**
+     * PEAR_Common constructor
+     *
+     * @access public
+     */
+    function PEAR_Common()
+    {
+        parent::PEAR();
+        $this->config = &PEAR_Config::singleton();
+        $this->debug = $this->config->get('verbose');
+    }
+
+    /**
+     * PEAR_Common destructor
+     *
+     * @access private
+     */
+    function _PEAR_Common()
+    {
+        // doesn't work due to bug #14744
+        //$tempfiles = $this->_tempfiles;
+        $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
+        while ($file = array_shift($tempfiles)) {
+            if (@is_dir($file)) {
+                if (!class_exists('System')) {
+                    require_once 'System.php';
+                }
+
+                System::rm(array('-rf', $file));
+            } elseif (file_exists($file)) {
+                unlink($file);
+            }
+        }
+    }
+
+    /**
+     * Register a temporary file or directory.  When the destructor is
+     * executed, all registered temporary files and directories are
+     * removed.
+     *
+     * @param string  $file  name of file or directory
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function addTempFile($file)
+    {
+        if (!class_exists('PEAR_Frontend')) {
+            require_once 'PEAR/Frontend.php';
+        }
+        PEAR_Frontend::addTempFile($file);
+    }
+
+    /**
+     * Wrapper to System::mkDir(), creates a directory as well as
+     * any necessary parent directories.
+     *
+     * @param string  $dir  directory name
+     *
+     * @return bool TRUE on success, or a PEAR error
+     *
+     * @access public
+     */
+    function mkDirHier($dir)
+    {
+        // Only used in Installer - move it there ?
+        $this->log(2, "+ create dir $dir");
+        if (!class_exists('System')) {
+            require_once 'System.php';
+        }
+        return System::mkDir(array('-p', $dir));
+    }
+
+    /**
+     * Logging method.
+     *
+     * @param int    $level  log level (0 is quiet, higher is noisier)
+     * @param string $msg    message to write to the log
+     *
+     * @return void
+     *
+     * @access public
+     * @static
+     */
+    function log($level, $msg, $append_crlf = true)
+    {
+        if ($this->debug >= $level) {
+            if (!class_exists('PEAR_Frontend')) {
+                require_once 'PEAR/Frontend.php';
+            }
+
+            $ui = &PEAR_Frontend::singleton();
+            if (is_a($ui, 'PEAR_Frontend')) {
+                $ui->log($msg, $append_crlf);
+            } else {
+                print "$msg\n";
+            }
+        }
+    }
+
+    /**
+     * Create and register a temporary directory.
+     *
+     * @param string $tmpdir (optional) Directory to use as tmpdir.
+     *                       Will use system defaults (for example
+     *                       /tmp or c:\windows\temp) if not specified
+     *
+     * @return string name of created directory
+     *
+     * @access public
+     */
+    function mkTempDir($tmpdir = '')
+    {
+        $topt = $tmpdir ? array('-t', $tmpdir) : array();
+        $topt = array_merge($topt, array('-d', 'pear'));
+        if (!class_exists('System')) {
+            require_once 'System.php';
+        }
+
+        if (!$tmpdir = System::mktemp($topt)) {
+            return false;
+        }
+
+        $this->addTempFile($tmpdir);
+        return $tmpdir;
+    }
+
+    /**
+     * Set object that represents the frontend to be used.
+     *
+     * @param  object Reference of the frontend object
+     * @return void
+     * @access public
+     */
+    function setFrontendObject(&$ui)
+    {
+        $this->ui = &$ui;
+    }
+
+    /**
+     * Return an array containing all of the states that are more stable than
+     * or equal to the passed in state
+     *
+     * @param string Release state
+     * @param boolean Determines whether to include $state in the list
+     * @return false|array False if $state is not a valid release state
+     */
+    function betterStates($state, $include = false)
+    {
+        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
+        $i = array_search($state, $states);
+        if ($i === false) {
+            return false;
+        }
+        if ($include) {
+            $i--;
+        }
+        return array_slice($states, $i + 1);
+    }
+
+    /**
+     * Get the valid roles for a PEAR package maintainer
+     *
+     * @return array
+     * @static
+     */
+    function getUserRoles()
+    {
+        return $GLOBALS['_PEAR_Common_maintainer_roles'];
+    }
+
+    /**
+     * Get the valid package release states of packages
+     *
+     * @return array
+     * @static
+     */
+    function getReleaseStates()
+    {
+        return $GLOBALS['_PEAR_Common_release_states'];
+    }
+
+    /**
+     * Get the implemented dependency types (php, ext, pkg etc.)
+     *
+     * @return array
+     * @static
+     */
+    function getDependencyTypes()
+    {
+        return $GLOBALS['_PEAR_Common_dependency_types'];
+    }
+
+    /**
+     * Get the implemented dependency relations (has, lt, ge etc.)
+     *
+     * @return array
+     * @static
+     */
+    function getDependencyRelations()
+    {
+        return $GLOBALS['_PEAR_Common_dependency_relations'];
+    }
+
+    /**
+     * Get the implemented file roles
+     *
+     * @return array
+     * @static
+     */
+    function getFileRoles()
+    {
+        return $GLOBALS['_PEAR_Common_file_roles'];
+    }
+
+    /**
+     * Get the implemented file replacement types in
+     *
+     * @return array
+     * @static
+     */
+    function getReplacementTypes()
+    {
+        return $GLOBALS['_PEAR_Common_replacement_types'];
+    }
+
+    /**
+     * Get the implemented file replacement types in
+     *
+     * @return array
+     * @static
+     */
+    function getProvideTypes()
+    {
+        return $GLOBALS['_PEAR_Common_provide_types'];
+    }
+
+    /**
+     * Get the implemented file replacement types in
+     *
+     * @return array
+     * @static
+     */
+    function getScriptPhases()
+    {
+        return $GLOBALS['_PEAR_Common_script_phases'];
+    }
+
+    /**
+     * Test whether a string contains a valid package name.
+     *
+     * @param string $name the package name to test
+     *
+     * @return bool
+     *
+     * @access public
+     */
+    function validPackageName($name)
+    {
+        return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
+    }
+
+    /**
+     * Test whether a string contains a valid package version.
+     *
+     * @param string $ver the package version to test
+     *
+     * @return bool
+     *
+     * @access public
+     */
+    function validPackageVersion($ver)
+    {
+        return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
+    }
+
+    /**
+     * @param string $path relative or absolute include path
+     * @return boolean
+     * @static
+     */
+    function isIncludeable($path)
+    {
+        if (file_exists($path) && is_readable($path)) {
+            return true;
+        }
+
+        $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
+        foreach ($ipath as $include) {
+            $test = realpath($include . DIRECTORY_SEPARATOR . $path);
+            if (file_exists($test) && is_readable($test)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    function _postProcessChecks($pf)
+    {
+        if (!PEAR::isError($pf)) {
+            return $this->_postProcessValidPackagexml($pf);
+        }
+
+        $errs = $pf->getUserinfo();
+        if (is_array($errs)) {
+            foreach ($errs as $error) {
+                $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
+            }
+        }
+
+        return $pf;
+    }
+
+    /**
+     * Returns information about a package file.  Expects the name of
+     * a gzipped tar file as input.
+     *
+     * @param string  $file  name of .tgz file
+     *
+     * @return array  array with package information
+     *
+     * @access public
+     * @deprecated use PEAR_PackageFile->fromTgzFile() instead
+     *
+     */
+    function infoFromTgzFile($file)
+    {
+        $packagefile = &new PEAR_PackageFile($this->config);
+        $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL);
+        return $this->_postProcessChecks($pf);
+    }
+
+    /**
+     * Returns information about a package file.  Expects the name of
+     * a package xml file as input.
+     *
+     * @param string  $descfile  name of package xml file
+     *
+     * @return array  array with package information
+     *
+     * @access public
+     * @deprecated use PEAR_PackageFile->fromPackageFile() instead
+     *
+     */
+    function infoFromDescriptionFile($descfile)
+    {
+        $packagefile = &new PEAR_PackageFile($this->config);
+        $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
+        return $this->_postProcessChecks($pf);
+    }
+
+    /**
+     * Returns information about a package file.  Expects the contents
+     * of a package xml file as input.
+     *
+     * @param string  $data  contents of package.xml file
+     *
+     * @return array   array with package information
+     *
+     * @access public
+     * @deprecated use PEAR_PackageFile->fromXmlstring() instead
+     *
+     */
+    function infoFromString($data)
+    {
+        $packagefile = &new PEAR_PackageFile($this->config);
+        $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false);
+        return $this->_postProcessChecks($pf);
+    }
+
+    /**
+     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
+     * @return array
+     */
+    function _postProcessValidPackagexml(&$pf)
+    {
+        if (!is_a($pf, 'PEAR_PackageFile_v2')) {
+            $this->pkginfo = $pf->toArray();
+            return $this->pkginfo;
+        }
+
+        // sort of make this into a package.xml 1.0-style array
+        // changelog is not converted to old format.
+        $arr = $pf->toArray(true);
+        $arr = array_merge($arr, $arr['old']);
+        unset($arr['old'], $arr['xsdversion'], $arr['contents'], $arr['compatible'],
+              $arr['channel'], $arr['uri'], $arr['dependencies'], $arr['phprelease'],
+              $arr['extsrcrelease'], $arr['zendextsrcrelease'], $arr['extbinrelease'],
+              $arr['zendextbinrelease'], $arr['bundle'], $arr['lead'], $arr['developer'],
+              $arr['helper'], $arr['contributor']);
+        $arr['filelist'] = $pf->getFilelist();
+        $this->pkginfo = $arr;
+        return $arr;
+    }
+
+    /**
+     * Returns package information from different sources
+     *
+     * This method is able to extract information about a package
+     * from a .tgz archive or from a XML package definition file.
+     *
+     * @access public
+     * @param  string Filename of the source ('package.xml', '<package>.tgz')
+     * @return string
+     * @deprecated use PEAR_PackageFile->fromAnyFile() instead
+     */
+    function infoFromAny($info)
+    {
+        if (is_string($info) && file_exists($info)) {
+            $packagefile = &new PEAR_PackageFile($this->config);
+            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
+            if (PEAR::isError($pf)) {
+                $errs = $pf->getUserinfo();
+                if (is_array($errs)) {
+                    foreach ($errs as $error) {
+                        $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
+                    }
+                }
+
+                return $pf;
+            }
+
+            return $this->_postProcessValidPackagexml($pf);
+        }
+
+        return $info;
+    }
+
+    /**
+     * Return an XML document based on the package info (as returned
+     * by the PEAR_Common::infoFrom* methods).
+     *
+     * @param array  $pkginfo  package info
+     *
+     * @return string XML data
+     *
+     * @access public
+     * @deprecated use a PEAR_PackageFile_v* object's generator instead
+     */
+    function xmlFromInfo($pkginfo)
+    {
+        $config      = &PEAR_Config::singleton();
+        $packagefile = &new PEAR_PackageFile($config);
+        $pf = &$packagefile->fromArray($pkginfo);
+        $gen = &$pf->getDefaultGenerator();
+        return $gen->toXml(PEAR_VALIDATE_PACKAGING);
+    }
+
+    /**
+     * Validate XML package definition file.
+     *
+     * @param  string $info Filename of the package archive or of the
+     *                package definition file
+     * @param  array $errors Array that will contain the errors
+     * @param  array $warnings Array that will contain the warnings
+     * @param  string $dir_prefix (optional) directory where source files
+     *                may be found, or empty if they are not available
+     * @access public
+     * @return boolean
+     * @deprecated use the validation of PEAR_PackageFile objects
+     */
+    function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
+    {
+        $config      = &PEAR_Config::singleton();
+        $packagefile = &new PEAR_PackageFile($config);
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        if (strpos($info, '<?xml') !== false) {
+            $pf = &$packagefile->fromXmlString($info, PEAR_VALIDATE_NORMAL, '');
+        } else {
+            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
+        }
+
+        PEAR::staticPopErrorHandling();
+        if (PEAR::isError($pf)) {
+            $errs = $pf->getUserinfo();
+            if (is_array($errs)) {
+                foreach ($errs as $error) {
+                    if ($error['level'] == 'error') {
+                        $errors[] = $error['message'];
+                    } else {
+                        $warnings[] = $error['message'];
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Build a "provides" array from data returned by
+     * analyzeSourceCode().  The format of the built array is like
+     * this:
+     *
+     *  array(
+     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
+     *    ...
+     *  )
+     *
+     *
+     * @param array $srcinfo array with information about a source file
+     * as returned by the analyzeSourceCode() method.
+     *
+     * @return void
+     *
+     * @access public
+     *
+     */
+    function buildProvidesArray($srcinfo)
+    {
+        $file = basename($srcinfo['source_file']);
+        $pn = '';
+        if (isset($this->_packageName)) {
+            $pn = $this->_packageName;
+        }
+
+        $pnl = strlen($pn);
+        foreach ($srcinfo['declared_classes'] as $class) {
+            $key = "class;$class";
+            if (isset($this->pkginfo['provides'][$key])) {
+                continue;
+            }
+
+            $this->pkginfo['provides'][$key] =
+                array('file'=> $file, 'type' => 'class', 'name' => $class);
+            if (isset($srcinfo['inheritance'][$class])) {
+                $this->pkginfo['provides'][$key]['extends'] =
+                    $srcinfo['inheritance'][$class];
+            }
+        }
+
+        foreach ($srcinfo['declared_methods'] as $class => $methods) {
+            foreach ($methods as $method) {
+                $function = "$class::$method";
+                $key = "function;$function";
+                if ($method{0} == '_' || !strcasecmp($method, $class) ||
+                    isset($this->pkginfo['provides'][$key])) {
+                    continue;
+                }
+
+                $this->pkginfo['provides'][$key] =
+                    array('file'=> $file, 'type' => 'function', 'name' => $function);
+            }
+        }
+
+        foreach ($srcinfo['declared_functions'] as $function) {
+            $key = "function;$function";
+            if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
+                continue;
+            }
+
+            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
+                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
+            }
+
+            $this->pkginfo['provides'][$key] =
+                array('file'=> $file, 'type' => 'function', 'name' => $function);
+        }
+    }
+
+    /**
+     * Analyze the source code of the given PHP file
+     *
+     * @param  string Filename of the PHP file
+     * @return mixed
+     * @access public
+     */
+    function analyzeSourceCode($file)
+    {
+        if (!class_exists('PEAR_PackageFile_v2_Validator')) {
+            require_once 'PEAR/PackageFile/v2/Validator.php';
+        }
+
+        $a = new PEAR_PackageFile_v2_Validator;
+        return $a->analyzeSourceCode($file);
+    }
+
+    function detectDependencies($any, $status_callback = null)
+    {
+        if (!function_exists("token_get_all")) {
+            return false;
+        }
+
+        if (PEAR::isError($info = $this->infoFromAny($any))) {
+            return $this->raiseError($info);
+        }
+
+        if (!is_array($info)) {
+            return false;
+        }
+
+        $deps = array();
+        $used_c = $decl_c = $decl_f = $decl_m = array();
+        foreach ($info['filelist'] as $file => $fa) {
+            $tmp = $this->analyzeSourceCode($file);
+            $used_c = @array_merge($used_c, $tmp['used_classes']);
+            $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
+            $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
+            $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
+            $inheri = @array_merge($inheri, $tmp['inheritance']);
+        }
+
+        $used_c = array_unique($used_c);
+        $decl_c = array_unique($decl_c);
+        $undecl_c = array_diff($used_c, $decl_c);
+
+        return array('used_classes' => $used_c,
+                     'declared_classes' => $decl_c,
+                     'declared_methods' => $decl_m,
+                     'declared_functions' => $decl_f,
+                     'undeclared_classes' => $undecl_c,
+                     'inheritance' => $inheri,
+                     );
+    }
+
+    /**
+     * Download a file through HTTP.  Considers suggested file name in
+     * Content-disposition: header and can run a callback function for
+     * different events.  The callback will be called with two
+     * parameters: the callback type, and parameters.  The implemented
+     * callback types are:
+     *
+     *  'setup'       called at the very beginning, parameter is a UI object
+     *                that should be used for all output
+     *  'message'     the parameter is a string with an informational message
+     *  'saveas'      may be used to save with a different file name, the
+     *                parameter is the filename that is about to be used.
+     *                If a 'saveas' callback returns a non-empty string,
+     *                that file name will be used as the filename instead.
+     *                Note that $save_dir will not be affected by this, only
+     *                the basename of the file.
+     *  'start'       download is starting, parameter is number of bytes
+     *                that are expected, or -1 if unknown
+     *  'bytesread'   parameter is the number of bytes read so far
+     *  'done'        download is complete, parameter is the total number
+     *                of bytes read
+     *  'connfailed'  if the TCP connection fails, this callback is called
+     *                with array(host,port,errno,errmsg)
+     *  'writefailed' if writing to disk fails, this callback is called
+     *                with array(destfile,errmsg)
+     *
+     * If an HTTP proxy has been configured (http_proxy PEAR_Config
+     * setting), the proxy will be used.
+     *
+     * @param string  $url       the URL to download
+     * @param object  $ui        PEAR_Frontend_* instance
+     * @param object  $config    PEAR_Config instance
+     * @param string  $save_dir  (optional) directory to save file in
+     * @param mixed   $callback  (optional) function/method to call for status
+     *                           updates
+     *
+     * @return string  Returns the full path of the downloaded file or a PEAR
+     *                 error on failure.  If the error is caused by
+     *                 socket-related errors, the error object will
+     *                 have the fsockopen error code available through
+     *                 getCode().
+     *
+     * @access public
+     * @deprecated in favor of PEAR_Downloader::downloadHttp()
+     */
+    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
+    {
+        if (!class_exists('PEAR_Downloader')) {
+            require_once 'PEAR/Downloader.php';
+        }
+        return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback);
+    }
+}
+
+require_once 'PEAR/Config.php';
+require_once 'PEAR/PackageFile.php';
Index: trunk/library/PEAR/PEAR/Command/Remote.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Remote.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Remote.php	(revision 5146)
@@ -0,0 +1,810 @@
+<?php
+/**
+ * PEAR_Command_Remote (remote-info, list-upgrades, remote-list, search, list-all, download,
+ * clear-cache commands)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Remote.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Common.php';
+require_once 'PEAR/REST.php';
+
+/**
+ * PEAR commands for remote server querying
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Command_Remote extends PEAR_Command_Common
+{
+    var $commands = array(
+        'remote-info' => array(
+            'summary' => 'Information About Remote Packages',
+            'function' => 'doRemoteInfo',
+            'shortcut' => 'ri',
+            'options' => array(),
+            'doc' => '<package>
+Get details on a package from the server.',
+            ),
+        'list-upgrades' => array(
+            'summary' => 'List Available Upgrades',
+            'function' => 'doListUpgrades',
+            'shortcut' => 'lu',
+            'options' => array(
+                'channelinfo' => array(
+                    'shortopt' => 'i',
+                    'doc' => 'output fully channel-aware data, even on failure',
+                    ),
+            ),
+            'doc' => '[preferred_state]
+List releases on the server of packages you have installed where
+a newer version is available with the same release state (stable etc.)
+or the state passed as the second parameter.'
+            ),
+        'remote-list' => array(
+            'summary' => 'List Remote Packages',
+            'function' => 'doRemoteList',
+            'shortcut' => 'rl',
+            'options' => array(
+                'channel' =>
+                    array(
+                    'shortopt' => 'c',
+                    'doc' => 'specify a channel other than the default channel',
+                    'arg' => 'CHAN',
+                    )
+                ),
+            'doc' => '
+Lists the packages available on the configured server along with the
+latest stable release of each package.',
+            ),
+        'search' => array(
+            'summary' => 'Search remote package database',
+            'function' => 'doSearch',
+            'shortcut' => 'sp',
+            'options' => array(
+                'channel' =>
+                    array(
+                    'shortopt' => 'c',
+                    'doc' => 'specify a channel other than the default channel',
+                    'arg' => 'CHAN',
+                    ),
+                'allchannels' => array(
+                    'shortopt' => 'a',
+                    'doc' => 'search packages from all known channels',
+                    ),
+                'channelinfo' => array(
+                    'shortopt' => 'i',
+                    'doc' => 'output fully channel-aware data, even on failure',
+                    ),
+                ),
+            'doc' => '[packagename] [packageinfo]
+Lists all packages which match the search parameters.  The first
+parameter is a fragment of a packagename.  The default channel
+will be used unless explicitly overridden.  The second parameter
+will be used to match any portion of the summary/description',
+            ),
+        'list-all' => array(
+            'summary' => 'List All Packages',
+            'function' => 'doListAll',
+            'shortcut' => 'la',
+            'options' => array(
+                'channel' =>
+                    array(
+                    'shortopt' => 'c',
+                    'doc' => 'specify a channel other than the default channel',
+                    'arg' => 'CHAN',
+                    ),
+                'channelinfo' => array(
+                    'shortopt' => 'i',
+                    'doc' => 'output fully channel-aware data, even on failure',
+                    ),
+                ),
+            'doc' => '
+Lists the packages available on the configured server along with the
+latest stable release of each package.',
+            ),
+        'download' => array(
+            'summary' => 'Download Package',
+            'function' => 'doDownload',
+            'shortcut' => 'd',
+            'options' => array(
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'download an uncompressed (.tar) file',
+                    ),
+                ),
+            'doc' => '<package>...
+Download package tarballs.  The files will be named as suggested by the
+server, for example if you download the DB package and the latest stable
+version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.',
+            ),
+        'clear-cache' => array(
+            'summary' => 'Clear Web Services Cache',
+            'function' => 'doClearCache',
+            'shortcut' => 'cc',
+            'options' => array(),
+            'doc' => '
+Clear the REST cache. See also the cache_ttl configuration
+parameter.
+',
+            ),
+        );
+
+    /**
+     * PEAR_Command_Remote constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Remote(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    function _checkChannelForStatus($channel, $chan)
+    {
+        if (PEAR::isError($chan)) {
+            $this->raiseError($chan);
+        }
+        if (!is_a($chan, 'PEAR_ChannelFile')) {
+            return $this->raiseError('Internal corruption error: invalid channel "' .
+                $channel . '"');
+        }
+        $rest = new PEAR_REST($this->config);
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $mirror = $this->config->get('preferred_mirror', null,
+                                     $channel);
+        $a = $rest->downloadHttp('http://' . $channel .
+            '/channel.xml', $chan->lastModified());
+        PEAR::staticPopErrorHandling();
+        if (!PEAR::isError($a) && $a) {
+            $this->ui->outputData('WARNING: channel "' . $channel . '" has ' .
+                'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $channel .
+                '" to update');
+        }
+    }
+
+    function doRemoteInfo($command, $options, $params)
+    {
+        if (sizeof($params) != 1) {
+            return $this->raiseError("$command expects one param: the remote package name");
+        }
+        $savechannel = $channel = $this->config->get('default_channel');
+        $reg = &$this->config->getRegistry();
+        $package = $params[0];
+        $parsed = $reg->parsePackageName($package, $channel);
+        if (PEAR::isError($parsed)) {
+            return $this->raiseError('Invalid package name "' . $package . '"');
+        }
+
+        $channel = $parsed['channel'];
+        $this->config->set('default_channel', $channel);
+        $chan = $reg->getChannel($channel);
+        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
+            return $e;
+        }
+
+        $mirror = $this->config->get('preferred_mirror');
+        if ($chan->supportsREST($mirror) && $base = $chan->getBaseURL('REST1.0', $mirror)) {
+            $rest = &$this->config->getREST('1.0', array());
+            $info = $rest->packageInfo($base, $parsed['package'], $channel);
+        }
+
+        if (!isset($info)) {
+            return $this->raiseError('No supported protocol was found');
+        }
+
+        if (PEAR::isError($info)) {
+            $this->config->set('default_channel', $savechannel);
+            return $this->raiseError($info);
+        }
+
+        if (!isset($info['name'])) {
+            return $this->raiseError('No remote package "' . $package . '" was found');
+        }
+
+        $installed = $reg->packageInfo($info['name'], null, $channel);
+        $info['installed'] = $installed['version'] ? $installed['version'] : '- no -';
+        if (is_array($info['installed'])) {
+            $info['installed'] = $info['installed']['release'];
+        }
+
+        $this->ui->outputData($info, $command);
+        $this->config->set('default_channel', $savechannel);
+
+        return true;
+    }
+
+    function doRemoteList($command, $options, $params)
+    {
+        $savechannel = $channel = $this->config->get('default_channel');
+        $reg = &$this->config->getRegistry();
+        if (isset($options['channel'])) {
+            $channel = $options['channel'];
+            if (!$reg->channelExists($channel)) {
+                return $this->raiseError('Channel "' . $channel . '" does not exist');
+            }
+
+            $this->config->set('default_channel', $channel);
+        }
+
+        $chan = $reg->getChannel($channel);
+        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
+            return $e;
+        }
+
+        $list_options = false;
+        if ($this->config->get('preferred_state') == 'stable') {
+            $list_options = true;
+        }
+
+        $available = array();
+        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
+              $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))
+        ) {
+            // use faster list-all if available
+            $rest = &$this->config->getREST('1.1', array());
+            $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName());
+        } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
+              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
+            $rest = &$this->config->getREST('1.0', array());
+            $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName());
+        }
+
+        if (PEAR::isError($available)) {
+            $this->config->set('default_channel', $savechannel);
+            return $this->raiseError($available);
+        }
+
+        $i = $j = 0;
+        $data = array(
+            'caption' => 'Channel ' . $channel . ' Available packages:',
+            'border' => true,
+            'headline' => array('Package', 'Version'),
+            'channel' => $channel
+            );
+
+        if (count($available) == 0) {
+            $data = '(no packages available yet)';
+        } else {
+            foreach ($available as $name => $info) {
+                $version = (isset($info['stable']) && $info['stable']) ? $info['stable'] : '-n/a-';
+                $data['data'][] = array($name, $version);
+            }
+        }
+        $this->ui->outputData($data, $command);
+        $this->config->set('default_channel', $savechannel);
+        return true;
+    }
+
+    function doListAll($command, $options, $params)
+    {
+        $savechannel = $channel = $this->config->get('default_channel');
+        $reg = &$this->config->getRegistry();
+        if (isset($options['channel'])) {
+            $channel = $options['channel'];
+            if (!$reg->channelExists($channel)) {
+                return $this->raiseError("Channel \"$channel\" does not exist");
+            }
+
+            $this->config->set('default_channel', $channel);
+        }
+
+        $list_options = false;
+        if ($this->config->get('preferred_state') == 'stable') {
+            $list_options = true;
+        }
+
+        $chan = $reg->getChannel($channel);
+        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
+            return $e;
+        }
+
+        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
+              $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) {
+            // use faster list-all if available
+            $rest = &$this->config->getREST('1.1', array());
+            $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName());
+        } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
+              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
+            $rest = &$this->config->getREST('1.0', array());
+            $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName());
+        }
+
+        if (PEAR::isError($available)) {
+            $this->config->set('default_channel', $savechannel);
+            return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "' . $available->getMessage() . '")');
+        }
+
+        $data = array(
+            'caption' => 'All packages [Channel ' . $channel . ']:',
+            'border' => true,
+            'headline' => array('Package', 'Latest', 'Local'),
+            'channel' => $channel,
+            );
+
+        if (isset($options['channelinfo'])) {
+            // add full channelinfo
+            $data['caption'] = 'Channel ' . $channel . ' All packages:';
+            $data['headline'] = array('Channel', 'Package', 'Latest', 'Local',
+                'Description', 'Dependencies');
+        }
+        $local_pkgs = $reg->listPackages($channel);
+
+        foreach ($available as $name => $info) {
+            $installed = $reg->packageInfo($name, null, $channel);
+            if (is_array($installed['version'])) {
+                $installed['version'] = $installed['version']['release'];
+            }
+            $desc = $info['summary'];
+            if (isset($params[$name])) {
+                $desc .= "\n\n".$info['description'];
+            }
+            if (isset($options['mode']))
+            {
+                if ($options['mode'] == 'installed' && !isset($installed['version'])) {
+                    continue;
+                }
+                if ($options['mode'] == 'notinstalled' && isset($installed['version'])) {
+                    continue;
+                }
+                if ($options['mode'] == 'upgrades'
+                      && (!isset($installed['version']) || version_compare($installed['version'],
+                      $info['stable'], '>='))) {
+                    continue;
+                }
+            }
+            $pos = array_search(strtolower($name), $local_pkgs);
+            if ($pos !== false) {
+                unset($local_pkgs[$pos]);
+            }
+
+            if (isset($info['stable']) && !$info['stable']) {
+                $info['stable'] = null;
+            }
+
+            if (isset($options['channelinfo'])) {
+                // add full channelinfo
+                if ($info['stable'] === $info['unstable']) {
+                    $state = $info['state'];
+                } else {
+                    $state = 'stable';
+                }
+                $latest = $info['stable'].' ('.$state.')';
+                $local = '';
+                if (isset($installed['version'])) {
+                    $inst_state = $reg->packageInfo($name, 'release_state', $channel);
+                    $local = $installed['version'].' ('.$inst_state.')';
+                }
+
+                $packageinfo = array(
+                    $channel,
+                    $name,
+                    $latest,
+                    $local,
+                    isset($desc) ? $desc : null,
+                    isset($info['deps']) ? $info['deps'] : null,
+                );
+            } else {
+                $packageinfo = array(
+                    $reg->channelAlias($channel) . '/' . $name,
+                    isset($info['stable']) ? $info['stable'] : null,
+                    isset($installed['version']) ? $installed['version'] : null,
+                    isset($desc) ? $desc : null,
+                    isset($info['deps']) ? $info['deps'] : null,
+                );
+            }
+            $data['data'][$info['category']][] = $packageinfo;
+        }
+
+        if (isset($options['mode']) && in_array($options['mode'], array('notinstalled', 'upgrades'))) {
+            $this->config->set('default_channel', $savechannel);
+            $this->ui->outputData($data, $command);
+            return true;
+        }
+
+        foreach ($local_pkgs as $name) {
+            $info = &$reg->getPackage($name, $channel);
+            $data['data']['Local'][] = array(
+                $reg->channelAlias($channel) . '/' . $info->getPackage(),
+                '',
+                $info->getVersion(),
+                $info->getSummary(),
+                $info->getDeps()
+                );
+        }
+
+        $this->config->set('default_channel', $savechannel);
+        $this->ui->outputData($data, $command);
+        return true;
+    }
+
+    function doSearch($command, $options, $params)
+    {
+        if ((!isset($params[0]) || empty($params[0]))
+            && (!isset($params[1]) || empty($params[1])))
+        {
+            return $this->raiseError('no valid search string supplied');
+        }
+
+        $channelinfo = isset($options['channelinfo']);
+        $reg = &$this->config->getRegistry();
+        if (isset($options['allchannels'])) {
+            // search all channels
+            unset($options['allchannels']);
+            $channels = $reg->getChannels();
+            $errors = array();
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            foreach ($channels as $channel) {
+                if ($channel->getName() != '__uri') {
+                    $options['channel'] = $channel->getName();
+                    $ret = $this->doSearch($command, $options, $params);
+                    if (PEAR::isError($ret)) {
+                        $errors[] = $ret;
+                    }
+                }
+            }
+
+            PEAR::staticPopErrorHandling();
+            if (count($errors) !== 0) {
+                // for now, only give first error
+                return PEAR::raiseError($errors[0]);
+            }
+
+            return true;
+        }
+
+        $savechannel = $channel = $this->config->get('default_channel');
+        $package = strtolower($params[0]);
+        $summary = isset($params[1]) ? $params[1] : false;
+        if (isset($options['channel'])) {
+            $reg = &$this->config->getRegistry();
+            $channel = $options['channel'];
+            if (!$reg->channelExists($channel)) {
+                return $this->raiseError('Channel "' . $channel . '" does not exist');
+            }
+
+            $this->config->set('default_channel', $channel);
+        }
+
+        $chan = $reg->getChannel($channel);
+        if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
+            return $e;
+        }
+
+        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
+              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
+            $rest = &$this->config->getREST('1.0', array());
+            $available = $rest->listAll($base, false, false, $package, $summary, $chan->getName());
+        }
+
+        if (PEAR::isError($available)) {
+            $this->config->set('default_channel', $savechannel);
+            return $this->raiseError($available);
+        }
+
+        if (!$available && !$channelinfo) {
+            // clean exit when not found, no error !
+            $data = 'no packages found that match pattern "' . $package . '", for channel '.$channel.'.';
+            $this->ui->outputData($data);
+            $this->config->set('default_channel', $channel);
+            return true;
+        }
+
+        if ($channelinfo) {
+            $data = array(
+                'caption' => 'Matched packages, channel ' . $channel . ':',
+                'border' => true,
+                'headline' => array('Channel', 'Package', 'Stable/(Latest)', 'Local'),
+                'channel' => $channel
+                );
+        } else {
+            $data = array(
+                'caption' => 'Matched packages, channel ' . $channel . ':',
+                'border' => true,
+                'headline' => array('Package', 'Stable/(Latest)', 'Local'),
+                'channel' => $channel
+                );
+        }
+
+        if (!$available && $channelinfo) {
+            unset($data['headline']);
+            $data['data'] = 'No packages found that match pattern "' . $package . '".';
+            $available = array();
+        }
+
+        foreach ($available as $name => $info) {
+            $installed = $reg->packageInfo($name, null, $channel);
+            $desc = $info['summary'];
+            if (isset($params[$name]))
+                $desc .= "\n\n".$info['description'];
+
+            if (!isset($info['stable']) || !$info['stable']) {
+                $version_remote = 'none';
+            } else {
+                if ($info['unstable']) {
+                    $version_remote = $info['unstable'];
+                } else {
+                    $version_remote = $info['stable'];
+                }
+                $version_remote .= ' ('.$info['state'].')';
+            }
+            $version = is_array($installed['version']) ? $installed['version']['release'] :
+                $installed['version'];
+            if ($channelinfo) {
+                $packageinfo = array(
+                    $channel,
+                    $name,
+                    $version_remote,
+                    $version,
+                    $desc,
+                );
+            } else {
+                $packageinfo = array(
+                    $name,
+                    $version_remote,
+                    $version,
+                    $desc,
+                );
+            }
+            $data['data'][$info['category']][] = $packageinfo;
+        }
+
+        $this->ui->outputData($data, $command);
+        $this->config->set('default_channel', $channel);
+        return true;
+    }
+
+    function &getDownloader($options)
+    {
+        if (!class_exists('PEAR_Downloader')) {
+            require_once 'PEAR/Downloader.php';
+        }
+        $a = &new PEAR_Downloader($this->ui, $options, $this->config);
+        return $a;
+    }
+
+    function doDownload($command, $options, $params)
+    {
+        // make certain that dependencies are ignored
+        $options['downloadonly'] = 1;
+
+        // eliminate error messages for preferred_state-related errors
+        /* TODO: Should be an option, but until now download does respect
+           prefered state */
+        /* $options['ignorepreferred_state'] = 1; */
+        // eliminate error messages for preferred_state-related errors
+
+        $downloader = &$this->getDownloader($options);
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $e = $downloader->setDownloadDir(getcwd());
+        PEAR::staticPopErrorHandling();
+        if (PEAR::isError($e)) {
+            return $this->raiseError('Current directory is not writeable, cannot download');
+        }
+
+        $errors = array();
+        $downloaded = array();
+        $err = $downloader->download($params);
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+
+        $errors = $downloader->getErrorMsgs();
+        if (count($errors)) {
+            foreach ($errors as $error) {
+                if ($error !== null) {
+                    $this->ui->outputData($error);
+                }
+            }
+
+            return $this->raiseError("$command failed");
+        }
+
+        $downloaded = $downloader->getDownloadedPackages();
+        foreach ($downloaded as $pkg) {
+            $this->ui->outputData("File $pkg[file] downloaded", $command);
+        }
+
+        return true;
+    }
+
+    function downloadCallback($msg, $params = null)
+    {
+        if ($msg == 'done') {
+            $this->bytes_downloaded = $params;
+        }
+    }
+
+    function doListUpgrades($command, $options, $params)
+    {
+        require_once 'PEAR/Common.php';
+        if (isset($params[0]) && !is_array(PEAR_Common::betterStates($params[0]))) {
+            return $this->raiseError($params[0] . ' is not a valid state (stable/beta/alpha/devel/etc.) try "pear help list-upgrades"');
+        }
+
+        $savechannel = $channel = $this->config->get('default_channel');
+        $reg = &$this->config->getRegistry();
+        foreach ($reg->listChannels() as $channel) {
+            $inst = array_flip($reg->listPackages($channel));
+            if (!count($inst)) {
+                continue;
+            }
+
+            if ($channel == '__uri') {
+                continue;
+            }
+
+            $this->config->set('default_channel', $channel);
+            $state = empty($params[0]) ? $this->config->get('preferred_state') : $params[0];
+
+            $caption = $channel . ' Available Upgrades';
+            $chan = $reg->getChannel($channel);
+            if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
+                return $e;
+            }
+
+            $latest = array();
+            $base2  = false;
+            $preferred_mirror = $this->config->get('preferred_mirror');
+            if ($chan->supportsREST($preferred_mirror) &&
+                (
+                   //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) ||
+                   ($base  = $chan->getBaseURL('REST1.0', $preferred_mirror))
+                )
+
+            ) {
+                if ($base2) {
+                    $rest = &$this->config->getREST('1.4', array());
+                    $base = $base2;
+                } else {
+                    $rest = &$this->config->getREST('1.0', array());
+                }
+
+                if (empty($state) || $state == 'any') {
+                    $state = false;
+                } else {
+                    $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')';
+                }
+
+                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                $latest = $rest->listLatestUpgrades($base, $state, $inst, $channel, $reg);
+                PEAR::staticPopErrorHandling();
+            }
+
+            if (PEAR::isError($latest)) {
+                $this->ui->outputData($latest->getMessage());
+                continue;
+            }
+
+            $caption .= ':';
+            if (PEAR::isError($latest)) {
+                $this->config->set('default_channel', $savechannel);
+                return $latest;
+            }
+
+            $data = array(
+                'caption' => $caption,
+                'border' => 1,
+                'headline' => array('Channel', 'Package', 'Local', 'Remote', 'Size'),
+                'channel' => $channel
+                );
+
+            foreach ((array)$latest as $pkg => $info) {
+                $package = strtolower($pkg);
+                if (!isset($inst[$package])) {
+                    // skip packages we don't have installed
+                    continue;
+                }
+
+                extract($info);
+                $inst_version = $reg->packageInfo($package, 'version', $channel);
+                $inst_state   = $reg->packageInfo($package, 'release_state', $channel);
+                if (version_compare("$version", "$inst_version", "le")) {
+                    // installed version is up-to-date
+                    continue;
+                }
+
+                if ($filesize >= 20480) {
+                    $filesize += 1024 - ($filesize % 1024);
+                    $fs = sprintf("%dkB", $filesize / 1024);
+                } elseif ($filesize > 0) {
+                    $filesize += 103 - ($filesize % 103);
+                    $fs = sprintf("%.1fkB", $filesize / 1024.0);
+                } else {
+                    $fs = "  -"; // XXX center instead
+                }
+
+                $data['data'][] = array($channel, $pkg, "$inst_version ($inst_state)", "$version ($state)", $fs);
+            }
+
+            if (isset($options['channelinfo'])) {
+                if (empty($data['data'])) {
+                    unset($data['headline']);
+                    if (count($inst) == 0) {
+                        $data['data'] = '(no packages installed)';
+                    } else {
+                        $data['data'] = '(no upgrades available)';
+                    }
+                }
+                $this->ui->outputData($data, $command);
+            } else {
+                if (empty($data['data'])) {
+                    $this->ui->outputData('Channel ' . $channel . ': No upgrades available');
+                } else {
+                    $this->ui->outputData($data, $command);
+                }
+            }
+        }
+
+        $this->config->set('default_channel', $savechannel);
+        return true;
+    }
+
+    function doClearCache($command, $options, $params)
+    {
+        $cache_dir = $this->config->get('cache_dir');
+        $verbose   = $this->config->get('verbose');
+        $output = '';
+        if (!file_exists($cache_dir) || !is_dir($cache_dir)) {
+            return $this->raiseError("$cache_dir does not exist or is not a directory");
+        }
+
+        if (!($dp = @opendir($cache_dir))) {
+            return $this->raiseError("opendir($cache_dir) failed: $php_errormsg");
+        }
+
+        if ($verbose >= 1) {
+            $output .= "reading directory $cache_dir\n";
+        }
+
+        $num = 0;
+        while ($ent = readdir($dp)) {
+            if (preg_match('/rest.cache(file|id)\\z/', $ent)) {
+                $path = $cache_dir . DIRECTORY_SEPARATOR . $ent;
+                if (file_exists($path)) {
+                    $ok = @unlink($path);
+                } else {
+                    $ok = false;
+                    $php_errormsg = '';
+                }
+
+                if ($ok) {
+                    if ($verbose >= 2) {
+                        $output .= "deleted $path\n";
+                    }
+                    $num++;
+                } elseif ($verbose >= 1) {
+                    $output .= "failed to delete $path $php_errormsg\n";
+                }
+            }
+        }
+
+        closedir($dp);
+        if ($verbose >= 1) {
+            $output .= "$num cache entries cleared\n";
+        }
+
+        $this->ui->outputData(rtrim($output), $command);
+        return $num;
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Channels.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Channels.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Channels.php	(revision 5146)
@@ -0,0 +1,883 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+/**
+ * PEAR_Command_Channels (list-channels, update-channels, channel-delete, channel-add,
+ * channel-update, channel-info, channel-alias, channel-discover commands)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Channels.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.0a1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Common.php';
+
+define('PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS', -500);
+
+/**
+ * PEAR commands for managing channels.
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.0a1
+ */
+class PEAR_Command_Channels extends PEAR_Command_Common
+{
+    var $commands = array(
+        'list-channels' => array(
+            'summary' => 'List Available Channels',
+            'function' => 'doList',
+            'shortcut' => 'lc',
+            'options' => array(),
+            'doc' => '
+List all available channels for installation.
+',
+            ),
+        'update-channels' => array(
+            'summary' => 'Update the Channel List',
+            'function' => 'doUpdateAll',
+            'shortcut' => 'uc',
+            'options' => array(),
+            'doc' => '
+List all installed packages in all channels.
+'
+            ),
+        'channel-delete' => array(
+            'summary' => 'Remove a Channel From the List',
+            'function' => 'doDelete',
+            'shortcut' => 'cde',
+            'options' => array(),
+            'doc' => '<channel name>
+Delete a channel from the registry.  You may not
+remove any channel that has installed packages.
+'
+            ),
+        'channel-add' => array(
+            'summary' => 'Add a Channel',
+            'function' => 'doAdd',
+            'shortcut' => 'ca',
+            'options' => array(),
+            'doc' => '<channel.xml>
+Add a private channel to the channel list.  Note that all
+public channels should be synced using "update-channels".
+Parameter may be either a local file or remote URL to a
+channel.xml.
+'
+            ),
+        'channel-update' => array(
+            'summary' => 'Update an Existing Channel',
+            'function' => 'doUpdate',
+            'shortcut' => 'cu',
+            'options' => array(
+                'force' => array(
+                    'shortopt' => 'f',
+                    'doc' => 'will force download of new channel.xml if an existing channel name is used',
+                    ),
+                'channel' => array(
+                    'shortopt' => 'c',
+                    'arg' => 'CHANNEL',
+                    'doc' => 'will force download of new channel.xml if an existing channel name is used',
+                    ),
+),
+            'doc' => '[<channel.xml>|<channel name>]
+Update a channel in the channel list directly.  Note that all
+public channels can be synced using "update-channels".
+Parameter may be a local or remote channel.xml, or the name of
+an existing channel.
+'
+            ),
+        'channel-info' => array(
+            'summary' => 'Retrieve Information on a Channel',
+            'function' => 'doInfo',
+            'shortcut' => 'ci',
+            'options' => array(),
+            'doc' => '<package>
+List the files in an installed package.
+'
+            ),
+        'channel-alias' => array(
+            'summary' => 'Specify an alias to a channel name',
+            'function' => 'doAlias',
+            'shortcut' => 'cha',
+            'options' => array(),
+            'doc' => '<channel> <alias>
+Specify a specific alias to use for a channel name.
+The alias may not be an existing channel name or
+alias.
+'
+            ),
+        'channel-discover' => array(
+            'summary' => 'Initialize a Channel from its server',
+            'function' => 'doDiscover',
+            'shortcut' => 'di',
+            'options' => array(),
+            'doc' => '[<channel.xml>|<channel name>]
+Initialize a channel from its server and create a local channel.xml.
+If <channel name> is in the format "<username>:<password>@<channel>" then
+<username> and <password> will be set as the login username/password for
+<channel>. Use caution when passing the username/password in this way, as
+it may allow other users on your computer to briefly view your username/
+password via the system\'s process list.
+'
+            ),
+        'channel-login' => array(
+            'summary' => 'Connects and authenticates to remote channel server',
+            'shortcut' => 'cli',
+            'function' => 'doLogin',
+            'options' => array(),
+            'doc' => '<channel name>
+Log in to a remote channel server.  If <channel name> is not supplied,
+the default channel is used. To use remote functions in the installer
+that require any kind of privileges, you need to log in first.  The
+username and password you enter here will be stored in your per-user
+PEAR configuration (~/.pearrc on Unix-like systems).  After logging
+in, your username and password will be sent along in subsequent
+operations on the remote server.',
+            ),
+        'channel-logout' => array(
+            'summary' => 'Logs out from the remote channel server',
+            'shortcut' => 'clo',
+            'function' => 'doLogout',
+            'options' => array(),
+            'doc' => '<channel name>
+Logs out from a remote channel server.  If <channel name> is not supplied,
+the default channel is used. This command does not actually connect to the
+remote server, it only deletes the stored username and password from your user
+configuration.',
+            ),
+        );
+
+    /**
+     * PEAR_Command_Registry constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Channels(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    function _sortChannels($a, $b)
+    {
+        return strnatcasecmp($a->getName(), $b->getName());
+    }
+
+    function doList($command, $options, $params)
+    {
+        $reg = &$this->config->getRegistry();
+        $registered = $reg->getChannels();
+        usort($registered, array(&$this, '_sortchannels'));
+        $i = $j = 0;
+        $data = array(
+            'caption' => 'Registered Channels:',
+            'border' => true,
+            'headline' => array('Channel', 'Alias', 'Summary')
+            );
+        foreach ($registered as $channel) {
+            $data['data'][] = array($channel->getName(),
+                                    $channel->getAlias(),
+                                    $channel->getSummary());
+        }
+
+        if (count($registered) === 0) {
+            $data = '(no registered channels)';
+        }
+        $this->ui->outputData($data, $command);
+        return true;
+    }
+
+    function doUpdateAll($command, $options, $params)
+    {
+        $reg = &$this->config->getRegistry();
+        $channels = $reg->getChannels();
+
+        $success = true;
+        foreach ($channels as $channel) {
+            if ($channel->getName() != '__uri') {
+                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                $err = $this->doUpdate('channel-update',
+                                          $options,
+                                          array($channel->getName()));
+                if (PEAR::isError($err)) {
+                    $this->ui->outputData($err->getMessage(), $command);
+                    $success = false;
+                } else {
+                    $success &= $err;
+                }
+            }
+        }
+        return $success;
+    }
+
+    function doInfo($command, $options, $params)
+    {
+        if (count($params) !== 1) {
+            return $this->raiseError("No channel specified");
+        }
+
+        $reg     = &$this->config->getRegistry();
+        $channel = strtolower($params[0]);
+        if ($reg->channelExists($channel)) {
+            $chan = $reg->getChannel($channel);
+            if (PEAR::isError($chan)) {
+                return $this->raiseError($chan);
+            }
+        } else {
+            if (strpos($channel, '://')) {
+                $downloader = &$this->getDownloader();
+                $tmpdir = $this->config->get('temp_dir');
+                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                $loc = $downloader->downloadHttp($channel, $this->ui, $tmpdir);
+                PEAR::staticPopErrorHandling();
+                if (PEAR::isError($loc)) {
+                    return $this->raiseError('Cannot open "' . $channel .
+                        '" (' . $loc->getMessage() . ')');
+                } else {
+                    $contents = implode('', file($loc));
+                }
+            } else {
+                if (!file_exists($params[0])) {
+                    return $this->raiseError('Unknown channel "' . $channel . '"');
+                }
+
+                $fp = fopen($params[0], 'r');
+                if (!$fp) {
+                    return $this->raiseError('Cannot open "' . $params[0] . '"');
+                }
+
+                $contents = '';
+                while (!feof($fp)) {
+                    $contents .= fread($fp, 1024);
+                }
+                fclose($fp);
+            }
+
+            if (!class_exists('PEAR_ChannelFile')) {
+                require_once 'PEAR/ChannelFile.php';
+            }
+
+            $chan = new PEAR_ChannelFile;
+            $chan->fromXmlString($contents);
+            $chan->validate();
+            if ($errs = $chan->getErrors(true)) {
+                foreach ($errs as $err) {
+                    $this->ui->outputData($err['level'] . ': ' . $err['message']);
+                }
+                return $this->raiseError('Channel file "' . $params[0] . '" is not valid');
+            }
+        }
+
+        if (!$chan) {
+            return $this->raiseError('Serious error: Channel "' . $params[0] .
+                '" has a corrupted registry entry');
+        }
+
+        $channel = $chan->getName();
+        $caption = 'Channel ' . $channel . ' Information:';
+        $data1 = array(
+            'caption' => $caption,
+            'border' => true);
+        $data1['data']['server'] = array('Name and Server', $chan->getName());
+        if ($chan->getAlias() != $chan->getName()) {
+            $data1['data']['alias'] = array('Alias', $chan->getAlias());
+        }
+
+        $data1['data']['summary'] = array('Summary', $chan->getSummary());
+        $validate = $chan->getValidationPackage();
+        $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']);
+        $data1['data']['vpackageversion'] =
+            array('Validation Package Version', $validate['attribs']['version']);
+        $d = array();
+        $d['main'] = $data1;
+
+        $data['data'] = array();
+        $data['caption'] = 'Server Capabilities';
+        $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
+        if ($chan->supportsREST()) {
+            if ($chan->supportsREST()) {
+                $funcs = $chan->getFunctions('rest');
+                if (!isset($funcs[0])) {
+                    $funcs = array($funcs);
+                }
+                foreach ($funcs as $protocol) {
+                    $data['data'][] = array('rest', $protocol['attribs']['type'],
+                        $protocol['_content']);
+                }
+            }
+        } else {
+            $data['data'][] = array('No supported protocols');
+        }
+
+        $d['protocols'] = $data;
+        $data['data'] = array();
+        $mirrors = $chan->getMirrors();
+        if ($mirrors) {
+            $data['caption'] = 'Channel ' . $channel . ' Mirrors:';
+            unset($data['headline']);
+            foreach ($mirrors as $mirror) {
+                $data['data'][] = array($mirror['attribs']['host']);
+                $d['mirrors'] = $data;
+            }
+
+            foreach ($mirrors as $i => $mirror) {
+                $data['data'] = array();
+                $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities';
+                $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
+                if ($chan->supportsREST($mirror['attribs']['host'])) {
+                    if ($chan->supportsREST($mirror['attribs']['host'])) {
+                        $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']);
+                        if (!isset($funcs[0])) {
+                            $funcs = array($funcs);
+                        }
+
+                        foreach ($funcs as $protocol) {
+                            $data['data'][] = array('rest', $protocol['attribs']['type'],
+                                $protocol['_content']);
+                        }
+                    }
+                } else {
+                    $data['data'][] = array('No supported protocols');
+                }
+                $d['mirrorprotocols' . $i] = $data;
+            }
+        }
+        $this->ui->outputData($d, 'channel-info');
+    }
+
+    // }}}
+
+    function doDelete($command, $options, $params)
+    {
+        if (count($params) !== 1) {
+            return $this->raiseError('channel-delete: no channel specified');
+        }
+
+        $reg = &$this->config->getRegistry();
+        if (!$reg->channelExists($params[0])) {
+            return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist');
+        }
+
+        $channel = $reg->channelName($params[0]);
+        if ($channel == 'pear.php.net') {
+            return $this->raiseError('Cannot delete the pear.php.net channel');
+        }
+
+        if ($channel == 'pecl.php.net') {
+            return $this->raiseError('Cannot delete the pecl.php.net channel');
+        }
+
+        if ($channel == 'doc.php.net') {
+            return $this->raiseError('Cannot delete the doc.php.net channel');
+        }
+
+        if ($channel == '__uri') {
+            return $this->raiseError('Cannot delete the __uri pseudo-channel');
+        }
+
+        if (PEAR::isError($err = $reg->listPackages($channel))) {
+            return $err;
+        }
+
+        if (count($err)) {
+            return $this->raiseError('Channel "' . $channel .
+                '" has installed packages, cannot delete');
+        }
+
+        if (!$reg->deleteChannel($channel)) {
+            return $this->raiseError('Channel "' . $channel . '" deletion failed');
+        } else {
+            $this->config->deleteChannel($channel);
+            $this->ui->outputData('Channel "' . $channel . '" deleted', $command);
+        }
+    }
+
+    function doAdd($command, $options, $params)
+    {
+        if (count($params) !== 1) {
+            return $this->raiseError('channel-add: no channel file specified');
+        }
+
+        if (strpos($params[0], '://')) {
+            $downloader = &$this->getDownloader();
+            $tmpdir = $this->config->get('temp_dir');
+            if (!file_exists($tmpdir)) {
+                require_once 'System.php';
+                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                $err = System::mkdir(array('-p', $tmpdir));
+                PEAR::staticPopErrorHandling();
+                if (PEAR::isError($err)) {
+                    return $this->raiseError('channel-add: temp_dir does not exist: "' .
+                        $tmpdir .
+                        '" - You can change this location with "pear config-set temp_dir"');
+                }
+            }
+
+            if (!is_writable($tmpdir)) {
+                return $this->raiseError('channel-add: temp_dir is not writable: "' .
+                    $tmpdir .
+                    '" - You can change this location with "pear config-set temp_dir"');
+            }
+
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $loc = $downloader->downloadHttp($params[0], $this->ui, $tmpdir, null, false);
+            PEAR::staticPopErrorHandling();
+            if (PEAR::isError($loc)) {
+                return $this->raiseError('channel-add: Cannot open "' . $params[0] .
+                    '" (' . $loc->getMessage() . ')');
+            }
+
+            list($loc, $lastmodified) = $loc;
+            $contents = implode('', file($loc));
+        } else {
+            $lastmodified = $fp = false;
+            if (file_exists($params[0])) {
+                $fp = fopen($params[0], 'r');
+            }
+
+            if (!$fp) {
+                return $this->raiseError('channel-add: cannot open "' . $params[0] . '"');
+            }
+
+            $contents = '';
+            while (!feof($fp)) {
+                $contents .= fread($fp, 1024);
+            }
+            fclose($fp);
+        }
+
+        if (!class_exists('PEAR_ChannelFile')) {
+            require_once 'PEAR/ChannelFile.php';
+        }
+
+        $channel = new PEAR_ChannelFile;
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $result = $channel->fromXmlString($contents);
+        PEAR::staticPopErrorHandling();
+        if (!$result) {
+            $exit = false;
+            if (count($errors = $channel->getErrors(true))) {
+                foreach ($errors as $error) {
+                    $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
+                    if (!$exit) {
+                        $exit = $error['level'] == 'error' ? true : false;
+                    }
+                }
+                if ($exit) {
+                    return $this->raiseError('channel-add: invalid channel.xml file');
+                }
+            }
+        }
+
+        $reg = &$this->config->getRegistry();
+        if ($reg->channelExists($channel->getName())) {
+            return $this->raiseError('channel-add: Channel "' . $channel->getName() .
+                '" exists, use channel-update to update entry', PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS);
+        }
+
+        $ret = $reg->addChannel($channel, $lastmodified);
+        if (PEAR::isError($ret)) {
+            return $ret;
+        }
+
+        if (!$ret) {
+            return $this->raiseError('channel-add: adding Channel "' . $channel->getName() .
+                '" to registry failed');
+        }
+
+        $this->config->setChannels($reg->listChannels());
+        $this->config->writeConfigFile();
+        $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command);
+    }
+
+    function doUpdate($command, $options, $params)
+    {
+        if (count($params) !== 1) {
+            return $this->raiseError("No channel file specified");
+        }
+
+        $tmpdir = $this->config->get('temp_dir');
+        if (!file_exists($tmpdir)) {
+            require_once 'System.php';
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $err = System::mkdir(array('-p', $tmpdir));
+            PEAR::staticPopErrorHandling();
+            if (PEAR::isError($err)) {
+                return $this->raiseError('channel-add: temp_dir does not exist: "' .
+                    $tmpdir .
+                    '" - You can change this location with "pear config-set temp_dir"');
+            }
+        }
+
+        if (!is_writable($tmpdir)) {
+            return $this->raiseError('channel-add: temp_dir is not writable: "' .
+                $tmpdir .
+                '" - You can change this location with "pear config-set temp_dir"');
+        }
+
+        $reg = &$this->config->getRegistry();
+        $lastmodified = false;
+        if ((!file_exists($params[0]) || is_dir($params[0]))
+              && $reg->channelExists(strtolower($params[0]))) {
+            $c = $reg->getChannel(strtolower($params[0]));
+            if (PEAR::isError($c)) {
+                return $this->raiseError($c);
+            }
+
+            $this->ui->outputData("Updating channel \"$params[0]\"", $command);
+            $dl = &$this->getDownloader(array());
+            // if force is specified, use a timestamp of "1" to force retrieval
+            $lastmodified = isset($options['force']) ? false : $c->lastModified();
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $contents = $dl->downloadHttp('http://' . $c->getName() . '/channel.xml',
+                $this->ui, $tmpdir, null, $lastmodified);
+            PEAR::staticPopErrorHandling();
+            if (PEAR::isError($contents)) {
+                // Attempt to fall back to https
+                $this->ui->outputData("Channel \"$params[0]\" is not responding over http://, failed with message: " . $contents->getMessage());
+                $this->ui->outputData("Trying channel \"$params[0]\" over https:// instead");
+                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                $contents = $dl->downloadHttp('https://' . $c->getName() . '/channel.xml',
+                    $this->ui, $tmpdir, null, $lastmodified);
+                PEAR::staticPopErrorHandling();
+                if (PEAR::isError($contents)) {
+                    return $this->raiseError('Cannot retrieve channel.xml for channel "' .
+                        $c->getName() . '" (' . $contents->getMessage() . ')');
+                }
+            }
+
+            list($contents, $lastmodified) = $contents;
+            if (!$contents) {
+                $this->ui->outputData("Channel \"$params[0]\" is up to date");
+                return;
+            }
+
+            $contents = implode('', file($contents));
+            if (!class_exists('PEAR_ChannelFile')) {
+                require_once 'PEAR/ChannelFile.php';
+            }
+
+            $channel = new PEAR_ChannelFile;
+            $channel->fromXmlString($contents);
+            if (!$channel->getErrors()) {
+                // security check: is the downloaded file for the channel we got it from?
+                if (strtolower($channel->getName()) != strtolower($c->getName())) {
+                    if (!isset($options['force'])) {
+                        return $this->raiseError('ERROR: downloaded channel definition file' .
+                            ' for channel "' . $channel->getName() . '" from channel "' .
+                            strtolower($c->getName()) . '"');
+                    }
+
+                    $this->ui->log(0, 'WARNING: downloaded channel definition file' .
+                        ' for channel "' . $channel->getName() . '" from channel "' .
+                        strtolower($c->getName()) . '"');
+                }
+            }
+        } else {
+            if (strpos($params[0], '://')) {
+                $dl = &$this->getDownloader();
+                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                $loc = $dl->downloadHttp($params[0],
+                    $this->ui, $tmpdir, null, $lastmodified);
+                PEAR::staticPopErrorHandling();
+                if (PEAR::isError($loc)) {
+                    return $this->raiseError("Cannot open " . $params[0] .
+                         ' (' . $loc->getMessage() . ')');
+                }
+
+                list($loc, $lastmodified) = $loc;
+                $contents = implode('', file($loc));
+            } else {
+                $fp = false;
+                if (file_exists($params[0])) {
+                    $fp = fopen($params[0], 'r');
+                }
+
+                if (!$fp) {
+                    return $this->raiseError("Cannot open " . $params[0]);
+                }
+
+                $contents = '';
+                while (!feof($fp)) {
+                    $contents .= fread($fp, 1024);
+                }
+                fclose($fp);
+            }
+
+            if (!class_exists('PEAR_ChannelFile')) {
+                require_once 'PEAR/ChannelFile.php';
+            }
+
+            $channel = new PEAR_ChannelFile;
+            $channel->fromXmlString($contents);
+        }
+
+        $exit = false;
+        if (count($errors = $channel->getErrors(true))) {
+            foreach ($errors as $error) {
+                $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
+                if (!$exit) {
+                    $exit = $error['level'] == 'error' ? true : false;
+                }
+            }
+            if ($exit) {
+                return $this->raiseError('Invalid channel.xml file');
+            }
+        }
+
+        if (!$reg->channelExists($channel->getName())) {
+            return $this->raiseError('Error: Channel "' . $channel->getName() .
+                '" does not exist, use channel-add to add an entry');
+        }
+
+        $ret = $reg->updateChannel($channel, $lastmodified);
+        if (PEAR::isError($ret)) {
+            return $ret;
+        }
+
+        if (!$ret) {
+            return $this->raiseError('Updating Channel "' . $channel->getName() .
+                '" in registry failed');
+        }
+
+        $this->config->setChannels($reg->listChannels());
+        $this->config->writeConfigFile();
+        $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded');
+    }
+
+    function &getDownloader()
+    {
+        if (!class_exists('PEAR_Downloader')) {
+            require_once 'PEAR/Downloader.php';
+        }
+        $a = new PEAR_Downloader($this->ui, array(), $this->config);
+        return $a;
+    }
+
+    function doAlias($command, $options, $params)
+    {
+        if (count($params) === 1) {
+            return $this->raiseError('No channel alias specified');
+        }
+
+        if (count($params) !== 2 || (!empty($params[1]) && $params[1]{0} == '-')) {
+            return $this->raiseError(
+                'Invalid format, correct is: channel-alias channel alias');
+        }
+
+        $reg = &$this->config->getRegistry();
+        if (!$reg->channelExists($params[0], true)) {
+            $extra = '';
+            if ($reg->isAlias($params[0])) {
+                $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' .
+                    strtolower($params[1]) . '")';
+            }
+
+            return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra);
+        }
+
+        if ($reg->isAlias($params[1])) {
+            return $this->raiseError('Channel "' . $reg->channelName($params[1]) . '" is ' .
+                'already aliased to "' . strtolower($params[1]) . '", cannot re-alias');
+        }
+
+        $chan = &$reg->getChannel($params[0]);
+        if (PEAR::isError($chan)) {
+            return $this->raiseError('Corrupt registry?  Error retrieving channel "' . $params[0] .
+                '" information (' . $chan->getMessage() . ')');
+        }
+
+        // make it a local alias
+        if (!$chan->setAlias(strtolower($params[1]), true)) {
+            return $this->raiseError('Alias "' . strtolower($params[1]) .
+                '" is not a valid channel alias');
+        }
+
+        $reg->updateChannel($chan);
+        $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' .
+            strtolower($params[1]) . '"');
+    }
+
+    /**
+     * The channel-discover command
+     *
+     * @param string $command command name
+     * @param array  $options option_name => value
+     * @param array  $params  list of additional parameters.
+     *               $params[0] should contain a string with either:
+     *               - <channel name> or
+     *               - <username>:<password>@<channel name>
+     * @return null|PEAR_Error
+     */
+    function doDiscover($command, $options, $params)
+    {
+        if (count($params) !== 1) {
+            return $this->raiseError("No channel server specified");
+        }
+
+        // Look for the possible input format "<username>:<password>@<channel>"
+        if (preg_match('/^(.+):(.+)@(.+)\\z/', $params[0], $matches)) {
+            $username = $matches[1];
+            $password = $matches[2];
+            $channel  = $matches[3];
+        } else {
+            $channel = $params[0];
+        }
+
+        $reg = &$this->config->getRegistry();
+        if ($reg->channelExists($channel)) {
+            if (!$reg->isAlias($channel)) {
+                return $this->raiseError("Channel \"$channel\" is already initialized", PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS);
+            }
+
+            return $this->raiseError("A channel alias named \"$channel\" " .
+                'already exists, aliasing channel "' . $reg->channelName($channel)
+                . '"');
+        }
+
+        $this->pushErrorHandling(PEAR_ERROR_RETURN);
+        $err = $this->doAdd($command, $options, array('http://' . $channel . '/channel.xml'));
+        $this->popErrorHandling();
+        if (PEAR::isError($err)) {
+            if ($err->getCode() === PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS) {
+                return $this->raiseError("Discovery of channel \"$channel\" failed (" .
+                    $err->getMessage() . ')');
+            }
+            // Attempt fetch via https
+            $this->ui->outputData("Discovering channel $channel over http:// failed with message: " . $err->getMessage());
+            $this->ui->outputData("Trying to discover channel $channel over https:// instead");
+            $this->pushErrorHandling(PEAR_ERROR_RETURN);
+            $err = $this->doAdd($command, $options, array('https://' . $channel . '/channel.xml'));
+            $this->popErrorHandling();
+            if (PEAR::isError($err)) {
+                return $this->raiseError("Discovery of channel \"$channel\" failed (" .
+                    $err->getMessage() . ')');
+            }
+        }
+
+        // Store username/password if they were given
+        // Arguably we should do a logintest on the channel here, but since
+        // that's awkward on a REST-based channel (even "pear login" doesn't
+        // do it for those), and XML-RPC is deprecated, it's fairly pointless.
+        if (isset($username)) {
+            $this->config->set('username', $username, 'user', $channel);
+            $this->config->set('password', $password, 'user', $channel);
+            $this->config->store();
+            $this->ui->outputData("Stored login for channel \"$channel\" using username \"$username\"", $command);
+        }
+
+        $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command);
+    }
+
+    /**
+     * Execute the 'login' command.
+     *
+     * @param string $command command name
+     * @param array $options option_name => value
+     * @param array $params list of additional parameters
+     *
+     * @return bool TRUE on success or
+     * a PEAR error on failure
+     *
+     * @access public
+     */
+    function doLogin($command, $options, $params)
+    {
+        $reg = &$this->config->getRegistry();
+
+        // If a parameter is supplied, use that as the channel to log in to
+        $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel');
+
+        $chan = $reg->getChannel($channel);
+        if (PEAR::isError($chan)) {
+            return $this->raiseError($chan);
+        }
+
+        $server   = $this->config->get('preferred_mirror', null, $channel);
+        $username = $this->config->get('username',         null, $channel);
+        if (empty($username)) {
+            $username = isset($_ENV['USER']) ? $_ENV['USER'] : null;
+        }
+        $this->ui->outputData("Logging in to $server.", $command);
+
+        list($username, $password) = $this->ui->userDialog(
+            $command,
+            array('Username', 'Password'),
+            array('text',     'password'),
+            array($username,  '')
+            );
+        $username = trim($username);
+        $password = trim($password);
+
+        $ourfile = $this->config->getConfFile('user');
+        if (!$ourfile) {
+            $ourfile = $this->config->getConfFile('system');
+        }
+
+        $this->config->set('username', $username, 'user', $channel);
+        $this->config->set('password', $password, 'user', $channel);
+
+        if ($chan->supportsREST()) {
+            $ok = true;
+        }
+
+        if ($ok !== true) {
+            return $this->raiseError('Login failed!');
+        }
+
+        $this->ui->outputData("Logged in.", $command);
+        // avoid changing any temporary settings changed with -d
+        $ourconfig = new PEAR_Config($ourfile, $ourfile);
+        $ourconfig->set('username', $username, 'user', $channel);
+        $ourconfig->set('password', $password, 'user', $channel);
+        $ourconfig->store();
+
+        return true;
+    }
+
+    /**
+     * Execute the 'logout' command.
+     *
+     * @param string $command command name
+     * @param array $options option_name => value
+     * @param array $params list of additional parameters
+     *
+     * @return bool TRUE on success or
+     * a PEAR error on failure
+     *
+     * @access public
+     */
+    function doLogout($command, $options, $params)
+    {
+        $reg     = &$this->config->getRegistry();
+
+        // If a parameter is supplied, use that as the channel to log in to
+        $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel');
+
+        $chan    = $reg->getChannel($channel);
+        if (PEAR::isError($chan)) {
+            return $this->raiseError($chan);
+        }
+
+        $server = $this->config->get('preferred_mirror', null, $channel);
+        $this->ui->outputData("Logging out from $server.", $command);
+        $this->config->remove('username', 'user', $channel);
+        $this->config->remove('password', 'user', $channel);
+        $this->config->store();
+        return true;
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Package.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Package.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Package.php	(revision 5146)
@@ -0,0 +1,1124 @@
+<?php
+/**
+ * PEAR_Command_Package (package, package-validate, cvsdiff, cvstag, package-dependencies,
+ * sign, makerpm, convert commands)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Package.php 313024 2011-07-06 19:51:24Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Common.php';
+
+/**
+ * PEAR commands for login/logout
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+
+class PEAR_Command_Package extends PEAR_Command_Common
+{
+    var $commands = array(
+        'package' => array(
+            'summary' => 'Build Package',
+            'function' => 'doPackage',
+            'shortcut' => 'p',
+            'options' => array(
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'Do not gzip the package file'
+                    ),
+                'showname' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'Print the name of the packaged file.',
+                    ),
+                ),
+            'doc' => '[descfile] [descfile2]
+Creates a PEAR package from its description file (usually called
+package.xml).  If a second packagefile is passed in, then
+the packager will check to make sure that one is a package.xml
+version 1.0, and the other is a package.xml version 2.0.  The
+package.xml version 1.0 will be saved as "package.xml" in the archive,
+and the other as "package2.xml" in the archive"
+'
+            ),
+        'package-validate' => array(
+            'summary' => 'Validate Package Consistency',
+            'function' => 'doPackageValidate',
+            'shortcut' => 'pv',
+            'options' => array(),
+            'doc' => '
+',
+            ),
+        'cvsdiff' => array(
+            'summary' => 'Run a "cvs diff" for all files in a package',
+            'function' => 'doCvsDiff',
+            'shortcut' => 'cd',
+            'options' => array(
+                'quiet' => array(
+                    'shortopt' => 'q',
+                    'doc' => 'Be quiet',
+                    ),
+                'reallyquiet' => array(
+                    'shortopt' => 'Q',
+                    'doc' => 'Be really quiet',
+                    ),
+                'date' => array(
+                    'shortopt' => 'D',
+                    'doc' => 'Diff against revision of DATE',
+                    'arg' => 'DATE',
+                    ),
+                'release' => array(
+                    'shortopt' => 'R',
+                    'doc' => 'Diff against tag for package release REL',
+                    'arg' => 'REL',
+                    ),
+                'revision' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'Diff against revision REV',
+                    'arg' => 'REV',
+                    ),
+                'context' => array(
+                    'shortopt' => 'c',
+                    'doc' => 'Generate context diff',
+                    ),
+                'unified' => array(
+                    'shortopt' => 'u',
+                    'doc' => 'Generate unified diff',
+                    ),
+                'ignore-case' => array(
+                    'shortopt' => 'i',
+                    'doc' => 'Ignore case, consider upper- and lower-case letters equivalent',
+                    ),
+                'ignore-whitespace' => array(
+                    'shortopt' => 'b',
+                    'doc' => 'Ignore changes in amount of white space',
+                    ),
+                'ignore-blank-lines' => array(
+                    'shortopt' => 'B',
+                    'doc' => 'Ignore changes that insert or delete blank lines',
+                    ),
+                'brief' => array(
+                    'doc' => 'Report only whether the files differ, no details',
+                    ),
+                'dry-run' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'Don\'t do anything, just pretend',
+                    ),
+                ),
+            'doc' => '<package.xml>
+Compares all the files in a package.  Without any options, this
+command will compare the current code with the last checked-in code.
+Using the -r or -R option you may compare the current code with that
+of a specific release.
+',
+            ),
+         'svntag' => array(
+             'summary' => 'Set SVN Release Tag',
+             'function' => 'doSvnTag',
+             'shortcut' => 'sv',
+             'options' => array(
+                 'quiet' => array(
+                     'shortopt' => 'q',
+                     'doc' => 'Be quiet',
+                     ),
+                 'slide' => array(
+                     'shortopt' => 'F',
+                     'doc' => 'Move (slide) tag if it exists',
+                     ),
+                 'delete' => array(
+                     'shortopt' => 'd',
+                     'doc' => 'Remove tag',
+                     ),
+                 'dry-run' => array(
+                     'shortopt' => 'n',
+                     'doc' => 'Don\'t do anything, just pretend',
+                     ),
+                 ),
+             'doc' => '<package.xml> [files...]
+ Sets a SVN tag on all files in a package.  Use this command after you have
+ packaged a distribution tarball with the "package" command to tag what
+ revisions of what files were in that release.  If need to fix something
+ after running svntag once, but before the tarball is released to the public,
+ use the "slide" option to move the release tag.
+
+ to include files (such as a second package.xml, or tests not included in the
+ release), pass them as additional parameters.
+ ',
+             ),
+        'cvstag' => array(
+            'summary' => 'Set CVS Release Tag',
+            'function' => 'doCvsTag',
+            'shortcut' => 'ct',
+            'options' => array(
+                'quiet' => array(
+                    'shortopt' => 'q',
+                    'doc' => 'Be quiet',
+                    ),
+                'reallyquiet' => array(
+                    'shortopt' => 'Q',
+                    'doc' => 'Be really quiet',
+                    ),
+                'slide' => array(
+                    'shortopt' => 'F',
+                    'doc' => 'Move (slide) tag if it exists',
+                    ),
+                'delete' => array(
+                    'shortopt' => 'd',
+                    'doc' => 'Remove tag',
+                    ),
+                'dry-run' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'Don\'t do anything, just pretend',
+                    ),
+                ),
+            'doc' => '<package.xml> [files...]
+Sets a CVS tag on all files in a package.  Use this command after you have
+packaged a distribution tarball with the "package" command to tag what
+revisions of what files were in that release.  If need to fix something
+after running cvstag once, but before the tarball is released to the public,
+use the "slide" option to move the release tag.
+
+to include files (such as a second package.xml, or tests not included in the
+release), pass them as additional parameters.
+',
+            ),
+        'package-dependencies' => array(
+            'summary' => 'Show package dependencies',
+            'function' => 'doPackageDependencies',
+            'shortcut' => 'pd',
+            'options' => array(),
+            'doc' => '<package-file> or <package.xml> or <install-package-name>
+List all dependencies the package has.
+Can take a tgz / tar file, package.xml or a package name of an installed package.'
+            ),
+        'sign' => array(
+            'summary' => 'Sign a package distribution file',
+            'function' => 'doSign',
+            'shortcut' => 'si',
+            'options' => array(
+                'verbose' => array(
+                    'shortopt' => 'v',
+                    'doc' => 'Display GnuPG output',
+                    ),
+            ),
+            'doc' => '<package-file>
+Signs a package distribution (.tar or .tgz) file with GnuPG.',
+            ),
+        'makerpm' => array(
+            'summary' => 'Builds an RPM spec file from a PEAR package',
+            'function' => 'doMakeRPM',
+            'shortcut' => 'rpm',
+            'options' => array(
+                'spec-template' => array(
+                    'shortopt' => 't',
+                    'arg' => 'FILE',
+                    'doc' => 'Use FILE as RPM spec file template'
+                    ),
+                'rpm-pkgname' => array(
+                    'shortopt' => 'p',
+                    'arg' => 'FORMAT',
+                    'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced
+by the PEAR package name, defaults to "PEAR::%s".',
+                    ),
+                ),
+            'doc' => '<package-file>
+
+Creates an RPM .spec file for wrapping a PEAR package inside an RPM
+package.  Intended to be used from the SPECS directory, with the PEAR
+package tarball in the SOURCES directory:
+
+$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
+Wrote RPM spec file PEAR::Net_Geo-1.0.spec
+$ rpm -bb PEAR::Net_Socket-1.0.spec
+...
+Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
+',
+            ),
+        'convert' => array(
+            'summary' => 'Convert a package.xml 1.0 to package.xml 2.0 format',
+            'function' => 'doConvert',
+            'shortcut' => 'c2',
+            'options' => array(
+                'flat' => array(
+                    'shortopt' => 'f',
+                    'doc' => 'do not beautify the filelist.',
+                    ),
+                ),
+            'doc' => '[descfile] [descfile2]
+Converts a package.xml in 1.0 format into a package.xml
+in 2.0 format.  The new file will be named package2.xml by default,
+and package.xml will be used as the old file by default.
+This is not the most intelligent conversion, and should only be
+used for automated conversion or learning the format.
+'
+            ),
+        );
+
+    var $output;
+
+    /**
+     * PEAR_Command_Package constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Package(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    function _displayValidationResults($err, $warn, $strict = false)
+    {
+        foreach ($err as $e) {
+            $this->output .= "Error: $e\n";
+        }
+        foreach ($warn as $w) {
+            $this->output .= "Warning: $w\n";
+        }
+        $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n",
+                                       sizeof($err), sizeof($warn));
+        if ($strict && count($err) > 0) {
+            $this->output .= "Fix these errors and try again.";
+            return false;
+        }
+        return true;
+    }
+
+    function &getPackager()
+    {
+        if (!class_exists('PEAR_Packager')) {
+            require_once 'PEAR/Packager.php';
+        }
+        $a = &new PEAR_Packager;
+        return $a;
+    }
+
+    function &getPackageFile($config, $debug = false)
+    {
+        if (!class_exists('PEAR_Common')) {
+            require_once 'PEAR/Common.php';
+        }
+        if (!class_exists('PEAR_PackageFile')) {
+            require_once 'PEAR/PackageFile.php';
+        }
+        $a = &new PEAR_PackageFile($config, $debug);
+        $common = new PEAR_Common;
+        $common->ui = $this->ui;
+        $a->setLogger($common);
+        return $a;
+    }
+
+    function doPackage($command, $options, $params)
+    {
+        $this->output = '';
+        $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml';
+        $pkg2 = isset($params[1]) ? $params[1] : null;
+        if (!$pkg2 && !isset($params[0]) && file_exists('package2.xml')) {
+            $pkg2 = 'package2.xml';
+        }
+
+        $packager = &$this->getPackager();
+        $compress = empty($options['nocompress']) ? true : false;
+        $result   = $packager->package($pkginfofile, $compress, $pkg2);
+        if (PEAR::isError($result)) {
+            return $this->raiseError($result);
+        }
+
+        // Don't want output, only the package file name just created
+        if (isset($options['showname'])) {
+            $this->output = $result;
+        }
+
+        if ($this->output) {
+            $this->ui->outputData($this->output, $command);
+        }
+
+        return true;
+    }
+
+    function doPackageValidate($command, $options, $params)
+    {
+        $this->output = '';
+        if (count($params) < 1) {
+            $params[0] = 'package.xml';
+        }
+
+        $obj = &$this->getPackageFile($this->config, $this->_debug);
+        $obj->rawReturn();
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
+        if (PEAR::isError($info)) {
+            $info = $obj->fromPackageFile($params[0], PEAR_VALIDATE_NORMAL);
+        } else {
+            $archive = $info->getArchiveFile();
+            $tar = &new Archive_Tar($archive);
+            $tar->extract(dirname($info->getPackageFile()));
+            $info->setPackageFile(dirname($info->getPackageFile()) . DIRECTORY_SEPARATOR .
+                $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR .
+                basename($info->getPackageFile()));
+        }
+
+        PEAR::staticPopErrorHandling();
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+
+        $valid = false;
+        if ($info->getPackagexmlVersion() == '2.0') {
+            if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) {
+                $info->flattenFileList();
+                $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
+            }
+        } else {
+            $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
+        }
+
+        $err = $warn = array();
+        if ($errors = $info->getValidationWarnings()) {
+            foreach ($errors as $error) {
+                if ($error['level'] == 'warning') {
+                    $warn[] = $error['message'];
+                } else {
+                    $err[] = $error['message'];
+                }
+            }
+        }
+
+        $this->_displayValidationResults($err, $warn);
+        $this->ui->outputData($this->output, $command);
+        return true;
+    }
+
+    function doSvnTag($command, $options, $params)
+    {
+        $this->output = '';
+        $_cmd = $command;
+        if (count($params) < 1) {
+            $help = $this->getHelp($command);
+            return $this->raiseError("$command: missing parameter: $help[0]");
+        }
+
+        $packageFile = realpath($params[0]);
+        $dir = dirname($packageFile);
+        $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1);
+        $obj  = &$this->getPackageFile($this->config, $this->_debug);
+        $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL);
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+
+        $err = $warn = array();
+        if (!$info->validate()) {
+            foreach ($info->getValidationWarnings() as $error) {
+                if ($error['level'] == 'warning') {
+                    $warn[] = $error['message'];
+                } else {
+                    $err[] = $error['message'];
+                }
+            }
+        }
+
+        if (!$this->_displayValidationResults($err, $warn, true)) {
+            $this->ui->outputData($this->output, $command);
+            return $this->raiseError('SVN tag failed');
+        }
+
+        $version    = $info->getVersion();
+        $package    = $info->getName();
+        $svntag     = "$package-$version";
+
+        if (isset($options['delete'])) {
+            return $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options);
+        }
+
+        $path = $this->_svnFindPath($packageFile);
+
+        // Check if there are any modified files
+        $fp = popen('svn st --xml ' . dirname($packageFile), "r");
+        $out = '';
+        while ($line = fgets($fp, 1024)) {
+            $out .= rtrim($line)."\n";
+        }
+        pclose($fp);
+
+        if (!isset($options['quiet']) && strpos($out, 'item="modified"')) {
+            $params = array(array(
+                'name' => 'modified',
+                'type' => 'yesno',
+                'default' => 'no',
+                'prompt' => 'You have files in your SVN checkout (' . $path['from']  . ') that have been modified but not commited, do you still want to tag ' . $version . '?',
+            ));
+            $answers = $this->ui->confirmDialog($params);
+
+            if (!in_array($answers['modified'], array('y', 'yes', 'on', '1'))) {
+                return true;
+            }
+        }
+
+        if (isset($options['slide'])) {
+            $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options);
+        }
+
+        // Check if tag already exists
+        $releaseTag = $path['local']['base'] . 'tags' . DIRECTORY_SEPARATOR . $svntag;
+        $existsCommand = 'svn ls ' . $path['base'] . 'tags/';
+
+        $fp = popen($existsCommand, "r");
+        $out = '';
+        while ($line = fgets($fp, 1024)) {
+            $out .= rtrim($line)."\n";
+        }
+        pclose($fp);
+
+        if (in_array($svntag . DIRECTORY_SEPARATOR, explode("\n", $out))) {
+            $this->ui->outputData($this->output, $command);
+            return $this->raiseError('SVN tag ' . $svntag . ' for ' . $package . ' already exists.');
+        } elseif (file_exists($path['local']['base'] . 'tags') === false) {
+            return $this->raiseError('Can not locate the tags directory at ' . $path['local']['base'] . 'tags');
+        } elseif (is_writeable($path['local']['base'] . 'tags') === false) {
+            return $this->raiseError('Can not write to the tag directory at ' . $path['local']['base'] . 'tags');
+        } else {
+            $makeCommand = 'svn mkdir ' . $releaseTag;
+            $this->output .= "+ $makeCommand\n";
+            if (empty($options['dry-run'])) {
+                // We need to create the tag dir.
+                $fp = popen($makeCommand, "r");
+                $out = '';
+                while ($line = fgets($fp, 1024)) {
+                    $out .= rtrim($line)."\n";
+                }
+                pclose($fp);
+                $this->output .= "$out\n";
+            }
+        }
+
+        $command = 'svn';
+        if (isset($options['quiet'])) {
+            $command .= ' -q';
+        }
+
+        $command .= ' copy --parents ';
+
+        $dir   = dirname($packageFile);
+        $dir   = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1);
+        $files = array_keys($info->getFilelist());
+        if (!in_array(basename($packageFile), $files)) {
+            $files[] = basename($packageFile);
+        }
+
+        array_shift($params);
+        if (count($params)) {
+            // add in additional files to be tagged (package files and such)
+            $files = array_merge($files, $params);
+        }
+
+        $commands = array();
+        foreach ($files as $file) {
+            if (!file_exists($file)) {
+                $file = $dir . DIRECTORY_SEPARATOR . $file;
+            }
+            $commands[] = $command . ' ' . escapeshellarg($file) . ' ' .
+                          escapeshellarg($releaseTag . DIRECTORY_SEPARATOR . $file);
+        }
+
+        $this->output .= implode("\n", $commands) . "\n";
+        if (empty($options['dry-run'])) {
+            foreach ($commands as $command) {
+                $fp = popen($command, "r");
+                while ($line = fgets($fp, 1024)) {
+                    $this->output .= rtrim($line)."\n";
+                }
+                pclose($fp);
+            }
+        }
+
+        $command = 'svn ci -m "Tagging the ' . $version  . ' release" ' . $releaseTag . "\n";
+        $this->output .= "+ $command\n";
+        if (empty($options['dry-run'])) {
+            $fp = popen($command, "r");
+            while ($line = fgets($fp, 1024)) {
+                $this->output .= rtrim($line)."\n";
+            }
+            pclose($fp);
+        }
+
+        $this->ui->outputData($this->output, $_cmd);
+        return true;
+    }
+
+    function _svnFindPath($file)
+    {
+        $xml = '';
+        $command = "svn info --xml $file";
+        $fp = popen($command, "r");
+        while ($line = fgets($fp, 1024)) {
+            $xml .= rtrim($line)."\n";
+        }
+        pclose($fp);
+        $url_tag = strpos($xml, '<url>');
+        $url = substr($xml, $url_tag + 5, strpos($xml, '</url>', $url_tag + 5) - ($url_tag + 5));
+
+        $path = array();
+        $path['from'] = substr($url, 0, strrpos($url, '/'));
+        $path['base'] = substr($path['from'], 0, strrpos($path['from'], '/') + 1);
+
+        // Figure out the local paths - see http://pear.php.net/bugs/17463
+        $pos = strpos($file, DIRECTORY_SEPARATOR . 'trunk' . DIRECTORY_SEPARATOR);
+        if ($pos === false) {
+            $pos = strpos($file, DIRECTORY_SEPARATOR . 'branches' . DIRECTORY_SEPARATOR);
+        }
+        $path['local']['base'] = substr($file, 0, $pos + 1);
+
+        return $path;
+    }
+
+    function _svnRemoveTag($version, $package, $tag, $packageFile, $options)
+    {
+        $command = 'svn';
+
+        if (isset($options['quiet'])) {
+            $command .= ' -q';
+        }
+
+        $command .= ' remove';
+        $command .= ' -m "Removing tag for the ' . $version  . ' release."';
+
+        $path = $this->_svnFindPath($packageFile);
+        $command .= ' ' . $path['base'] . 'tags/' . $tag;
+
+
+        if ($this->config->get('verbose') > 1) {
+            $this->output .= "+ $command\n";
+        }
+
+        $this->output .= "+ $command\n";
+        if (empty($options['dry-run'])) {
+            $fp = popen($command, "r");
+            while ($line = fgets($fp, 1024)) {
+                $this->output .= rtrim($line)."\n";
+            }
+            pclose($fp);
+        }
+
+        $this->ui->outputData($this->output, $command);
+        return true;
+    }
+
+    function doCvsTag($command, $options, $params)
+    {
+        $this->output = '';
+        $_cmd = $command;
+        if (count($params) < 1) {
+            $help = $this->getHelp($command);
+            return $this->raiseError("$command: missing parameter: $help[0]");
+        }
+
+        $packageFile = realpath($params[0]);
+        $obj  = &$this->getPackageFile($this->config, $this->_debug);
+        $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL);
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+
+        $err = $warn = array();
+        if (!$info->validate()) {
+            foreach ($info->getValidationWarnings() as $error) {
+                if ($error['level'] == 'warning') {
+                    $warn[] = $error['message'];
+                } else {
+                    $err[] = $error['message'];
+                }
+            }
+        }
+
+        if (!$this->_displayValidationResults($err, $warn, true)) {
+            $this->ui->outputData($this->output, $command);
+            return $this->raiseError('CVS tag failed');
+        }
+
+        $version    = $info->getVersion();
+        $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version);
+        $cvstag     = "RELEASE_$cvsversion";
+        $files      = array_keys($info->getFilelist());
+        $command = 'cvs';
+        if (isset($options['quiet'])) {
+            $command .= ' -q';
+        }
+
+        if (isset($options['reallyquiet'])) {
+            $command .= ' -Q';
+        }
+
+        $command .= ' tag';
+        if (isset($options['slide'])) {
+            $command .= ' -F';
+        }
+
+        if (isset($options['delete'])) {
+            $command .= ' -d';
+        }
+
+        $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]);
+        array_shift($params);
+        if (count($params)) {
+            // add in additional files to be tagged
+            $files = array_merge($files, $params);
+        }
+
+        $dir = dirname($packageFile);
+        $dir = substr($dir, strrpos($dir, '/') + 1);
+        foreach ($files as $file) {
+            if (!file_exists($file)) {
+                $file = $dir . DIRECTORY_SEPARATOR . $file;
+            }
+            $command .= ' ' . escapeshellarg($file);
+        }
+
+        if ($this->config->get('verbose') > 1) {
+            $this->output .= "+ $command\n";
+        }
+
+        $this->output .= "+ $command\n";
+        if (empty($options['dry-run'])) {
+            $fp = popen($command, "r");
+            while ($line = fgets($fp, 1024)) {
+                $this->output .= rtrim($line)."\n";
+            }
+            pclose($fp);
+        }
+
+        $this->ui->outputData($this->output, $_cmd);
+        return true;
+    }
+
+    function doCvsDiff($command, $options, $params)
+    {
+        $this->output = '';
+        if (sizeof($params) < 1) {
+            $help = $this->getHelp($command);
+            return $this->raiseError("$command: missing parameter: $help[0]");
+        }
+
+        $file = realpath($params[0]);
+        $obj  = &$this->getPackageFile($this->config, $this->_debug);
+        $info = $obj->fromAnyFile($file, PEAR_VALIDATE_NORMAL);
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+
+        $err = $warn = array();
+        if (!$info->validate()) {
+            foreach ($info->getValidationWarnings() as $error) {
+                if ($error['level'] == 'warning') {
+                    $warn[] = $error['message'];
+                } else {
+                    $err[] = $error['message'];
+                }
+            }
+        }
+
+        if (!$this->_displayValidationResults($err, $warn, true)) {
+            $this->ui->outputData($this->output, $command);
+            return $this->raiseError('CVS diff failed');
+        }
+
+        $info1 = $info->getFilelist();
+        $files = $info1;
+        $cmd = "cvs";
+        if (isset($options['quiet'])) {
+            $cmd .= ' -q';
+            unset($options['quiet']);
+        }
+
+        if (isset($options['reallyquiet'])) {
+            $cmd .= ' -Q';
+            unset($options['reallyquiet']);
+        }
+
+        if (isset($options['release'])) {
+            $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']);
+            $cvstag = "RELEASE_$cvsversion";
+            $options['revision'] = $cvstag;
+            unset($options['release']);
+        }
+
+        $execute = true;
+        if (isset($options['dry-run'])) {
+            $execute = false;
+            unset($options['dry-run']);
+        }
+
+        $cmd .= ' diff';
+        // the rest of the options are passed right on to "cvs diff"
+        foreach ($options as $option => $optarg) {
+            $arg = $short = false;
+            if (isset($this->commands[$command]['options'][$option])) {
+                $arg = $this->commands[$command]['options'][$option]['arg'];
+                $short = $this->commands[$command]['options'][$option]['shortopt'];
+            }
+            $cmd .= $short ? " -$short" : " --$option";
+            if ($arg && $optarg) {
+                $cmd .= ($short ? '' : '=') . escapeshellarg($optarg);
+            }
+        }
+
+        foreach ($files as $file) {
+            $cmd .= ' ' . escapeshellarg($file['name']);
+        }
+
+        if ($this->config->get('verbose') > 1) {
+            $this->output .= "+ $cmd\n";
+        }
+
+        if ($execute) {
+            $fp = popen($cmd, "r");
+            while ($line = fgets($fp, 1024)) {
+                $this->output .= rtrim($line)."\n";
+            }
+            pclose($fp);
+        }
+
+        $this->ui->outputData($this->output, $command);
+        return true;
+    }
+
+    function doPackageDependencies($command, $options, $params)
+    {
+        // $params[0] -> the PEAR package to list its information
+        if (count($params) !== 1) {
+            return $this->raiseError("bad parameter(s), try \"help $command\"");
+        }
+
+        $obj = &$this->getPackageFile($this->config, $this->_debug);
+        if (is_file($params[0]) || strpos($params[0], '.xml') > 0) {
+           $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
+        } else {
+            $reg  = $this->config->getRegistry();
+            $info = $obj->fromArray($reg->packageInfo($params[0]));
+        }
+
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+
+        $deps = $info->getDeps();
+        if (is_array($deps)) {
+            if ($info->getPackagexmlVersion() == '1.0') {
+                $data = array(
+                    'caption' => 'Dependencies for pear/' . $info->getPackage(),
+                    'border' => true,
+                    'headline' => array("Required?", "Type", "Name", "Relation", "Version"),
+                    );
+
+                foreach ($deps as $d) {
+                    if (isset($d['optional'])) {
+                        if ($d['optional'] == 'yes') {
+                            $req = 'No';
+                        } else {
+                            $req = 'Yes';
+                        }
+                    } else {
+                        $req = 'Yes';
+                    }
+
+                    if (isset($this->_deps_rel_trans[$d['rel']])) {
+                        $rel = $this->_deps_rel_trans[$d['rel']];
+                    } else {
+                        $rel = $d['rel'];
+                    }
+
+                    if (isset($this->_deps_type_trans[$d['type']])) {
+                        $type = ucfirst($this->_deps_type_trans[$d['type']]);
+                    } else {
+                        $type = $d['type'];
+                    }
+
+                    if (isset($d['name'])) {
+                        $name = $d['name'];
+                    } else {
+                        $name = '';
+                    }
+
+                    if (isset($d['version'])) {
+                        $version = $d['version'];
+                    } else {
+                        $version = '';
+                    }
+
+                    $data['data'][] = array($req, $type, $name, $rel, $version);
+                }
+            } else { // package.xml 2.0 dependencies display
+                require_once 'PEAR/Dependency2.php';
+                $deps = $info->getDependencies();
+                $reg = &$this->config->getRegistry();
+                if (is_array($deps)) {
+                    $d = new PEAR_Dependency2($this->config, array(), '');
+                    $data = array(
+                        'caption' => 'Dependencies for ' . $info->getPackage(),
+                        'border' => true,
+                        'headline' => array("Required?", "Type", "Name", 'Versioning', 'Group'),
+                        );
+                    foreach ($deps as $type => $subd) {
+                        $req = ($type == 'required') ? 'Yes' : 'No';
+                        if ($type == 'group') {
+                            $group = $subd['attribs']['name'];
+                        } else {
+                            $group = '';
+                        }
+
+                        if (!isset($subd[0])) {
+                            $subd = array($subd);
+                        }
+
+                        foreach ($subd as $groupa) {
+                            foreach ($groupa as $deptype => $depinfo) {
+                                if ($deptype == 'attribs') {
+                                    continue;
+                                }
+
+                                if ($deptype == 'pearinstaller') {
+                                    $deptype = 'pear Installer';
+                                }
+
+                                if (!isset($depinfo[0])) {
+                                    $depinfo = array($depinfo);
+                                }
+
+                                foreach ($depinfo as $inf) {
+                                    $name = '';
+                                    if (isset($inf['channel'])) {
+                                        $alias = $reg->channelAlias($inf['channel']);
+                                        if (!$alias) {
+                                            $alias = '(channel?) ' .$inf['channel'];
+                                        }
+                                        $name = $alias . '/';
+
+                                    }
+                                    if (isset($inf['name'])) {
+                                        $name .= $inf['name'];
+                                    } elseif (isset($inf['pattern'])) {
+                                        $name .= $inf['pattern'];
+                                    } else {
+                                        $name .= '';
+                                    }
+
+                                    if (isset($inf['uri'])) {
+                                        $name .= ' [' . $inf['uri'] .  ']';
+                                    }
+
+                                    if (isset($inf['conflicts'])) {
+                                        $ver = 'conflicts';
+                                    } else {
+                                        $ver = $d->_getExtraString($inf);
+                                    }
+
+                                    $data['data'][] = array($req, ucfirst($deptype), $name,
+                                        $ver, $group);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            $this->ui->outputData($data, $command);
+            return true;
+        }
+
+        // Fallback
+        $this->ui->outputData("This package does not have any dependencies.", $command);
+    }
+
+    function doSign($command, $options, $params)
+    {
+        // should move most of this code into PEAR_Packager
+        // so it'll be easy to implement "pear package --sign"
+        if (count($params) !== 1) {
+            return $this->raiseError("bad parameter(s), try \"help $command\"");
+        }
+
+        require_once 'System.php';
+        require_once 'Archive/Tar.php';
+
+        if (!file_exists($params[0])) {
+            return $this->raiseError("file does not exist: $params[0]");
+        }
+
+        $obj = $this->getPackageFile($this->config, $this->_debug);
+        $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+
+        $tar = new Archive_Tar($params[0]);
+
+        $tmpdir = $this->config->get('temp_dir');
+        $tmpdir = System::mktemp(' -t "' . $tmpdir . '" -d pearsign');
+        if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) {
+            return $this->raiseError("failed to extract tar file");
+        }
+
+        if (file_exists("$tmpdir/package.sig")) {
+            return $this->raiseError("package already signed");
+        }
+
+        $packagexml = 'package.xml';
+        if (file_exists("$tmpdir/package2.xml")) {
+            $packagexml = 'package2.xml';
+        }
+
+        if (file_exists("$tmpdir/package.sig")) {
+            unlink("$tmpdir/package.sig");
+        }
+
+        if (!file_exists("$tmpdir/$packagexml")) {
+            return $this->raiseError("Extracted file $tmpdir/$packagexml not found.");
+        }
+
+        $input = $this->ui->userDialog($command,
+                                       array('GnuPG Passphrase'),
+                                       array('password'));
+        if (!isset($input[0])) {
+            //use empty passphrase
+            $input[0] = '';
+        }
+
+        $devnull = (isset($options['verbose'])) ? '' : ' 2>/dev/null';
+        $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/$packagexml" . $devnull, "w");
+        if (!$gpg) {
+            return $this->raiseError("gpg command failed");
+        }
+
+        fwrite($gpg, "$input[0]\n");
+        if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) {
+            return $this->raiseError("gpg sign failed");
+        }
+
+        if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) {
+            return $this->raiseError('failed adding signature to file');
+        }
+
+        $this->ui->outputData("Package signed.", $command);
+        return true;
+    }
+
+    /**
+     * For unit testing purposes
+     */
+    function &getInstaller(&$ui)
+    {
+        if (!class_exists('PEAR_Installer')) {
+            require_once 'PEAR/Installer.php';
+        }
+        $a = &new PEAR_Installer($ui);
+        return $a;
+    }
+
+    /**
+     * For unit testing purposes
+     */
+    function &getCommandPackaging(&$ui, &$config)
+    {
+        if (!class_exists('PEAR_Command_Packaging')) {
+            if ($fp = @fopen('PEAR/Command/Packaging.php', 'r', true)) {
+                fclose($fp);
+                include_once 'PEAR/Command/Packaging.php';
+            }
+        }
+
+        if (class_exists('PEAR_Command_Packaging')) {
+            $a = &new PEAR_Command_Packaging($ui, $config);
+        } else {
+            $a = null;
+        }
+
+        return $a;
+    }
+
+    function doMakeRPM($command, $options, $params)
+    {
+
+        // Check to see if PEAR_Command_Packaging is installed, and
+        // transparently switch to use the "make-rpm-spec" command from it
+        // instead, if it does. Otherwise, continue to use the old version
+        // of "makerpm" supplied with this package (PEAR).
+        $packaging_cmd = $this->getCommandPackaging($this->ui, $this->config);
+        if ($packaging_cmd !== null) {
+            $this->ui->outputData('PEAR_Command_Packaging is installed; using '.
+                'newer "make-rpm-spec" command instead');
+            return $packaging_cmd->run('make-rpm-spec', $options, $params);
+        }
+
+        $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '.
+          'improved version is available via "pear make-rpm-spec", which '.
+          'is available by installing PEAR_Command_Packaging');
+        return true;
+    }
+
+    function doConvert($command, $options, $params)
+    {
+        $packagexml    = isset($params[0]) ? $params[0] : 'package.xml';
+        $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) .
+            DIRECTORY_SEPARATOR . 'package2.xml';
+        $pkg = &$this->getPackageFile($this->config, $this->_debug);
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
+        PEAR::staticPopErrorHandling();
+        if (PEAR::isError($pf)) {
+            if (is_array($pf->getUserInfo())) {
+                foreach ($pf->getUserInfo() as $warning) {
+                    $this->ui->outputData($warning['message']);
+                }
+            }
+            return $this->raiseError($pf);
+        }
+
+        if (is_a($pf, 'PEAR_PackageFile_v2')) {
+            $this->ui->outputData($packagexml . ' is already a package.xml version 2.0');
+            return true;
+        }
+
+        $gen   = &$pf->getDefaultGenerator();
+        $newpf = &$gen->toV2();
+        $newpf->setPackagefile($newpackagexml);
+        $gen = &$newpf->getDefaultGenerator();
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL);
+        $saved = $gen->toPackageFile(dirname($newpackagexml), $state, basename($newpackagexml));
+        PEAR::staticPopErrorHandling();
+        if (PEAR::isError($saved)) {
+            if (is_array($saved->getUserInfo())) {
+                foreach ($saved->getUserInfo() as $warning) {
+                    $this->ui->outputData($warning['message']);
+                }
+            }
+
+            $this->ui->outputData($saved->getMessage());
+            return true;
+        }
+
+        $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"');
+        return true;
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Build.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Build.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Build.php	(revision 5146)
@@ -0,0 +1,85 @@
+<?php
+/**
+ * PEAR_Command_Auth (build command)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Build.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Common.php';
+
+/**
+ * PEAR commands for building extensions.
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Command_Build extends PEAR_Command_Common
+{
+    var $commands = array(
+        'build' => array(
+            'summary' => 'Build an Extension From C Source',
+            'function' => 'doBuild',
+            'shortcut' => 'b',
+            'options' => array(),
+            'doc' => '[package.xml]
+Builds one or more extensions contained in a package.'
+            ),
+        );
+
+    /**
+     * PEAR_Command_Build constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Build(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    function doBuild($command, $options, $params)
+    {
+        require_once 'PEAR/Builder.php';
+        if (sizeof($params) < 1) {
+            $params[0] = 'package.xml';
+        }
+
+        $builder = &new PEAR_Builder($this->ui);
+        $this->debug = $this->config->get('verbose');
+        $err = $builder->build($params[0], array(&$this, 'buildCallback'));
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+
+        return true;
+    }
+
+    function buildCallback($what, $data)
+    {
+        if (($what == 'cmdoutput' && $this->debug > 1) ||
+            ($what == 'output' && $this->debug > 0)) {
+            $this->ui->outputData(rtrim($data), 'build');
+        }
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Auth.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Auth.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Auth.php	(revision 5146)
@@ -0,0 +1,81 @@
+<?php
+/**
+ * PEAR_Command_Auth (login, logout commands)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Auth.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ * @deprecated since 1.8.0alpha1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Channels.php';
+
+/**
+ * PEAR commands for login/logout
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ * @deprecated since 1.8.0alpha1
+ */
+class PEAR_Command_Auth extends PEAR_Command_Channels
+{
+    var $commands = array(
+        'login' => array(
+            'summary' => 'Connects and authenticates to remote server [Deprecated in favor of channel-login]',
+            'shortcut' => 'li',
+            'function' => 'doLogin',
+            'options' => array(),
+            'doc' => '<channel name>
+WARNING: This function is deprecated in favor of using channel-login
+
+Log in to a remote channel server.  If <channel name> is not supplied,
+the default channel is used. To use remote functions in the installer
+that require any kind of privileges, you need to log in first.  The
+username and password you enter here will be stored in your per-user
+PEAR configuration (~/.pearrc on Unix-like systems).  After logging
+in, your username and password will be sent along in subsequent
+operations on the remote server.',
+            ),
+        'logout' => array(
+            'summary' => 'Logs out from the remote server [Deprecated in favor of channel-logout]',
+            'shortcut' => 'lo',
+            'function' => 'doLogout',
+            'options' => array(),
+            'doc' => '
+WARNING: This function is deprecated in favor of using channel-logout
+
+Logs out from the remote server.  This command does not actually
+connect to the remote server, it only deletes the stored username and
+password from your user configuration.',
+            )
+
+        );
+
+    /**
+     * PEAR_Command_Auth constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Auth(&$ui, &$config)
+    {
+        parent::PEAR_Command_Channels($ui, $config);
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Remote.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Remote.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Remote.xml	(revision 5146)
@@ -0,0 +1,109 @@
+<commands version="1.0">
+ <remote-info>
+  <summary>Information About Remote Packages</summary>
+  <function>doRemoteInfo</function>
+  <shortcut>ri</shortcut>
+  <options />
+  <doc>&lt;package&gt;
+Get details on a package from the server.</doc>
+ </remote-info>
+ <list-upgrades>
+  <summary>List Available Upgrades</summary>
+  <function>doListUpgrades</function>
+  <shortcut>lu</shortcut>
+  <options>
+   <channelinfo>
+    <shortopt>i</shortopt>
+    <doc>output fully channel-aware data, even on failure</doc>
+   </channelinfo>
+  </options>
+  <doc>[preferred_state]
+List releases on the server of packages you have installed where
+a newer version is available with the same release state (stable etc.)
+or the state passed as the second parameter.</doc>
+ </list-upgrades>
+ <remote-list>
+  <summary>List Remote Packages</summary>
+  <function>doRemoteList</function>
+  <shortcut>rl</shortcut>
+  <options>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>specify a channel other than the default channel</doc>
+    <arg>CHAN</arg>
+   </channel>
+  </options>
+  <doc>
+Lists the packages available on the configured server along with the
+latest stable release of each package.</doc>
+ </remote-list>
+ <search>
+  <summary>Search remote package database</summary>
+  <function>doSearch</function>
+  <shortcut>sp</shortcut>
+  <options>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>specify a channel other than the default channel</doc>
+    <arg>CHAN</arg>
+   </channel>
+   <allchannels>
+    <shortopt>a</shortopt>
+    <doc>search packages from all known channels</doc>
+   </allchannels>
+   <channelinfo>
+    <shortopt>i</shortopt>
+    <doc>output fully channel-aware data, even on failure</doc>
+   </channelinfo>
+  </options>
+  <doc>[packagename] [packageinfo]
+Lists all packages which match the search parameters.  The first
+parameter is a fragment of a packagename.  The default channel
+will be used unless explicitly overridden.  The second parameter
+will be used to match any portion of the summary/description</doc>
+ </search>
+ <list-all>
+  <summary>List All Packages</summary>
+  <function>doListAll</function>
+  <shortcut>la</shortcut>
+  <options>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>specify a channel other than the default channel</doc>
+    <arg>CHAN</arg>
+   </channel>
+   <channelinfo>
+    <shortopt>i</shortopt>
+    <doc>output fully channel-aware data, even on failure</doc>
+   </channelinfo>
+  </options>
+  <doc>
+Lists the packages available on the configured server along with the
+latest stable release of each package.</doc>
+ </list-all>
+ <download>
+  <summary>Download Package</summary>
+  <function>doDownload</function>
+  <shortcut>d</shortcut>
+  <options>
+   <nocompress>
+    <shortopt>Z</shortopt>
+    <doc>download an uncompressed (.tar) file</doc>
+   </nocompress>
+  </options>
+  <doc>&lt;package&gt;...
+Download package tarballs.  The files will be named as suggested by the
+server, for example if you download the DB package and the latest stable
+version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.</doc>
+ </download>
+ <clear-cache>
+  <summary>Clear Web Services Cache</summary>
+  <function>doClearCache</function>
+  <shortcut>cc</shortcut>
+  <options />
+  <doc>
+Clear the XML-RPC/REST cache.  See also the cache_ttl configuration
+parameter.
+</doc>
+ </clear-cache>
+</commands>
Index: trunk/library/PEAR/PEAR/Command/Channels.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Channels.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Channels.xml	(revision 5146)
@@ -0,0 +1,123 @@
+<commands version="1.0">
+ <list-channels>
+  <summary>List Available Channels</summary>
+  <function>doList</function>
+  <shortcut>lc</shortcut>
+  <options />
+  <doc>
+List all available channels for installation.
+</doc>
+ </list-channels>
+ <update-channels>
+  <summary>Update the Channel List</summary>
+  <function>doUpdateAll</function>
+  <shortcut>uc</shortcut>
+  <options />
+  <doc>
+List all installed packages in all channels.
+</doc>
+ </update-channels>
+ <channel-delete>
+  <summary>Remove a Channel From the List</summary>
+  <function>doDelete</function>
+  <shortcut>cde</shortcut>
+  <options />
+  <doc>&lt;channel name&gt;
+Delete a channel from the registry.  You may not
+remove any channel that has installed packages.
+</doc>
+ </channel-delete>
+ <channel-add>
+  <summary>Add a Channel</summary>
+  <function>doAdd</function>
+  <shortcut>ca</shortcut>
+  <options />
+  <doc>&lt;channel.xml&gt;
+Add a private channel to the channel list.  Note that all
+public channels should be synced using &quot;update-channels&quot;.
+Parameter may be either a local file or remote URL to a
+channel.xml.
+</doc>
+ </channel-add>
+ <channel-update>
+  <summary>Update an Existing Channel</summary>
+  <function>doUpdate</function>
+  <shortcut>cu</shortcut>
+  <options>
+   <force>
+    <shortopt>f</shortopt>
+    <doc>will force download of new channel.xml if an existing channel name is used</doc>
+   </force>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>will force download of new channel.xml if an existing channel name is used</doc>
+    <arg>CHANNEL</arg>
+   </channel>
+  </options>
+  <doc>[&lt;channel.xml&gt;|&lt;channel name&gt;]
+Update a channel in the channel list directly.  Note that all
+public channels can be synced using &quot;update-channels&quot;.
+Parameter may be a local or remote channel.xml, or the name of
+an existing channel.
+</doc>
+ </channel-update>
+ <channel-info>
+  <summary>Retrieve Information on a Channel</summary>
+  <function>doInfo</function>
+  <shortcut>ci</shortcut>
+  <options />
+  <doc>&lt;package&gt;
+List the files in an installed package.
+</doc>
+ </channel-info>
+ <channel-alias>
+  <summary>Specify an alias to a channel name</summary>
+  <function>doAlias</function>
+  <shortcut>cha</shortcut>
+  <options />
+  <doc>&lt;channel&gt; &lt;alias&gt;
+Specify a specific alias to use for a channel name.
+The alias may not be an existing channel name or
+alias.
+</doc>
+ </channel-alias>
+ <channel-discover>
+  <summary>Initialize a Channel from its server</summary>
+  <function>doDiscover</function>
+  <shortcut>di</shortcut>
+  <options />
+  <doc>[&lt;channel.xml&gt;|&lt;channel name&gt;]
+Initialize a channel from its server and create a local channel.xml.
+If &lt;channel name&gt; is in the format &quot;&lt;username&gt;:&lt;password&gt;@&lt;channel&gt;&quot; then
+&lt;username&gt; and &lt;password&gt; will be set as the login username/password for
+&lt;channel&gt;. Use caution when passing the username/password in this way, as
+it may allow other users on your computer to briefly view your username/
+password via the system&#039;s process list.
+</doc>
+ </channel-discover>
+ <channel-login>
+  <summary>Connects and authenticates to remote channel server</summary>
+  <function>doLogin</function>
+  <shortcut>cli</shortcut>
+  <options />
+  <doc>&lt;channel name&gt;
+Log in to a remote channel server.  If &lt;channel name&gt; is not supplied,
+the default channel is used. To use remote functions in the installer
+that require any kind of privileges, you need to log in first.  The
+username and password you enter here will be stored in your per-user
+PEAR configuration (~/.pearrc on Unix-like systems).  After logging
+in, your username and password will be sent along in subsequent
+operations on the remote server.</doc>
+ </channel-login>
+ <channel-logout>
+  <summary>Logs out from the remote channel server</summary>
+  <function>doLogout</function>
+  <shortcut>clo</shortcut>
+  <options />
+  <doc>&lt;channel name&gt;
+Logs out from a remote channel server.  If &lt;channel name&gt; is not supplied,
+the default channel is used. This command does not actually connect to the
+remote server, it only deletes the stored username and password from your user
+configuration.</doc>
+ </channel-logout>
+</commands>
Index: trunk/library/PEAR/PEAR/Command/Package.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Package.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Package.xml	(revision 5146)
@@ -0,0 +1,237 @@
+<commands version="1.0">
+ <package>
+  <summary>Build Package</summary>
+  <function>doPackage</function>
+  <shortcut>p</shortcut>
+  <options>
+   <nocompress>
+    <shortopt>Z</shortopt>
+    <doc>Do not gzip the package file</doc>
+   </nocompress>
+   <showname>
+    <shortopt>n</shortopt>
+    <doc>Print the name of the packaged file.</doc>
+   </showname>
+  </options>
+  <doc>[descfile] [descfile2]
+Creates a PEAR package from its description file (usually called
+package.xml).  If a second packagefile is passed in, then
+the packager will check to make sure that one is a package.xml
+version 1.0, and the other is a package.xml version 2.0.  The
+package.xml version 1.0 will be saved as &quot;package.xml&quot; in the archive,
+and the other as &quot;package2.xml&quot; in the archive&quot;
+</doc>
+ </package>
+ <package-validate>
+  <summary>Validate Package Consistency</summary>
+  <function>doPackageValidate</function>
+  <shortcut>pv</shortcut>
+  <options />
+  <doc>
+</doc>
+ </package-validate>
+ <cvsdiff>
+  <summary>Run a &quot;cvs diff&quot; for all files in a package</summary>
+  <function>doCvsDiff</function>
+  <shortcut>cd</shortcut>
+  <options>
+   <quiet>
+    <shortopt>q</shortopt>
+    <doc>Be quiet</doc>
+   </quiet>
+   <reallyquiet>
+    <shortopt>Q</shortopt>
+    <doc>Be really quiet</doc>
+   </reallyquiet>
+   <date>
+    <shortopt>D</shortopt>
+    <doc>Diff against revision of DATE</doc>
+    <arg>DATE</arg>
+   </date>
+   <release>
+    <shortopt>R</shortopt>
+    <doc>Diff against tag for package release REL</doc>
+    <arg>REL</arg>
+   </release>
+   <revision>
+    <shortopt>r</shortopt>
+    <doc>Diff against revision REV</doc>
+    <arg>REV</arg>
+   </revision>
+   <context>
+    <shortopt>c</shortopt>
+    <doc>Generate context diff</doc>
+   </context>
+   <unified>
+    <shortopt>u</shortopt>
+    <doc>Generate unified diff</doc>
+   </unified>
+   <ignore-case>
+    <shortopt>i</shortopt>
+    <doc>Ignore case, consider upper- and lower-case letters equivalent</doc>
+   </ignore-case>
+   <ignore-whitespace>
+    <shortopt>b</shortopt>
+    <doc>Ignore changes in amount of white space</doc>
+   </ignore-whitespace>
+   <ignore-blank-lines>
+    <shortopt>B</shortopt>
+    <doc>Ignore changes that insert or delete blank lines</doc>
+   </ignore-blank-lines>
+   <brief>
+    <shortopt></shortopt>
+    <doc>Report only whether the files differ, no details</doc>
+   </brief>
+   <dry-run>
+    <shortopt>n</shortopt>
+    <doc>Don&#039;t do anything, just pretend</doc>
+   </dry-run>
+  </options>
+  <doc>&lt;package.xml&gt;
+Compares all the files in a package.  Without any options, this
+command will compare the current code with the last checked-in code.
+Using the -r or -R option you may compare the current code with that
+of a specific release.
+</doc>
+ </cvsdiff>
+ <svntag>
+  <summary>Set SVN Release Tag</summary>
+  <function>doSvnTag</function>
+  <shortcut>sv</shortcut>
+  <options>
+   <quiet>
+    <shortopt>q</shortopt>
+    <doc>Be quiet</doc>
+   </quiet>
+   <slide>
+    <shortopt>F</shortopt>
+    <doc>Move (slide) tag if it exists</doc>
+   </slide>
+   <delete>
+    <shortopt>d</shortopt>
+    <doc>Remove tag</doc>
+   </delete>
+   <dry-run>
+    <shortopt>n</shortopt>
+    <doc>Don&#039;t do anything, just pretend</doc>
+   </dry-run>
+  </options>
+  <doc>&lt;package.xml&gt; [files...]
+ Sets a SVN tag on all files in a package.  Use this command after you have
+ packaged a distribution tarball with the &quot;package&quot; command to tag what
+ revisions of what files were in that release.  If need to fix something
+ after running svntag once, but before the tarball is released to the public,
+ use the &quot;slide&quot; option to move the release tag.
+
+ to include files (such as a second package.xml, or tests not included in the
+ release), pass them as additional parameters.
+ </doc>
+ </svntag>
+ <cvstag>
+  <summary>Set CVS Release Tag</summary>
+  <function>doCvsTag</function>
+  <shortcut>ct</shortcut>
+  <options>
+   <quiet>
+    <shortopt>q</shortopt>
+    <doc>Be quiet</doc>
+   </quiet>
+   <reallyquiet>
+    <shortopt>Q</shortopt>
+    <doc>Be really quiet</doc>
+   </reallyquiet>
+   <slide>
+    <shortopt>F</shortopt>
+    <doc>Move (slide) tag if it exists</doc>
+   </slide>
+   <delete>
+    <shortopt>d</shortopt>
+    <doc>Remove tag</doc>
+   </delete>
+   <dry-run>
+    <shortopt>n</shortopt>
+    <doc>Don&#039;t do anything, just pretend</doc>
+   </dry-run>
+  </options>
+  <doc>&lt;package.xml&gt; [files...]
+Sets a CVS tag on all files in a package.  Use this command after you have
+packaged a distribution tarball with the &quot;package&quot; command to tag what
+revisions of what files were in that release.  If need to fix something
+after running cvstag once, but before the tarball is released to the public,
+use the &quot;slide&quot; option to move the release tag.
+
+to include files (such as a second package.xml, or tests not included in the
+release), pass them as additional parameters.
+</doc>
+ </cvstag>
+ <package-dependencies>
+  <summary>Show package dependencies</summary>
+  <function>doPackageDependencies</function>
+  <shortcut>pd</shortcut>
+  <options />
+  <doc>&lt;package-file&gt; or &lt;package.xml&gt; or &lt;install-package-name&gt;
+List all dependencies the package has.
+Can take a tgz / tar file, package.xml or a package name of an installed package.</doc>
+ </package-dependencies>
+ <sign>
+  <summary>Sign a package distribution file</summary>
+  <function>doSign</function>
+  <shortcut>si</shortcut>
+  <options>
+   <verbose>
+    <shortopt>v</shortopt>
+    <doc>Display GnuPG output</doc>
+   </verbose>
+  </options>
+  <doc>&lt;package-file&gt;
+Signs a package distribution (.tar or .tgz) file with GnuPG.</doc>
+ </sign>
+ <makerpm>
+  <summary>Builds an RPM spec file from a PEAR package</summary>
+  <function>doMakeRPM</function>
+  <shortcut>rpm</shortcut>
+  <options>
+   <spec-template>
+    <shortopt>t</shortopt>
+    <doc>Use FILE as RPM spec file template</doc>
+    <arg>FILE</arg>
+   </spec-template>
+   <rpm-pkgname>
+    <shortopt>p</shortopt>
+    <doc>Use FORMAT as format string for RPM package name, %s is replaced
+by the PEAR package name, defaults to &quot;PEAR::%s&quot;.</doc>
+    <arg>FORMAT</arg>
+   </rpm-pkgname>
+  </options>
+  <doc>&lt;package-file&gt;
+
+Creates an RPM .spec file for wrapping a PEAR package inside an RPM
+package.  Intended to be used from the SPECS directory, with the PEAR
+package tarball in the SOURCES directory:
+
+$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
+Wrote RPM spec file PEAR::Net_Geo-1.0.spec
+$ rpm -bb PEAR::Net_Socket-1.0.spec
+...
+Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
+</doc>
+ </makerpm>
+ <convert>
+  <summary>Convert a package.xml 1.0 to package.xml 2.0 format</summary>
+  <function>doConvert</function>
+  <shortcut>c2</shortcut>
+  <options>
+   <flat>
+    <shortopt>f</shortopt>
+    <doc>do not beautify the filelist.</doc>
+   </flat>
+  </options>
+  <doc>[descfile] [descfile2]
+Converts a package.xml in 1.0 format into a package.xml
+in 2.0 format.  The new file will be named package2.xml by default,
+and package.xml will be used as the old file by default.
+This is not the most intelligent conversion, and should only be
+used for automated conversion or learning the format.
+</doc>
+ </convert>
+</commands>
Index: trunk/library/PEAR/PEAR/Command/Config.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Config.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Config.php	(revision 5146)
@@ -0,0 +1,414 @@
+<?php
+/**
+ * PEAR_Command_Config (config-show, config-get, config-set, config-help, config-create commands)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Config.php 313024 2011-07-06 19:51:24Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Common.php';
+
+/**
+ * PEAR commands for managing configuration data.
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Command_Config extends PEAR_Command_Common
+{
+    var $commands = array(
+        'config-show' => array(
+            'summary' => 'Show All Settings',
+            'function' => 'doConfigShow',
+            'shortcut' => 'csh',
+            'options' => array(
+                'channel' => array(
+                    'shortopt' => 'c',
+                    'doc' => 'show configuration variables for another channel',
+                    'arg' => 'CHAN',
+                    ),
+),
+            'doc' => '[layer]
+Displays all configuration values.  An optional argument
+may be used to tell which configuration layer to display.  Valid
+configuration layers are "user", "system" and "default". To display
+configurations for different channels, set the default_channel
+configuration variable and run config-show again.
+',
+            ),
+        'config-get' => array(
+            'summary' => 'Show One Setting',
+            'function' => 'doConfigGet',
+            'shortcut' => 'cg',
+            'options' => array(
+                'channel' => array(
+                    'shortopt' => 'c',
+                    'doc' => 'show configuration variables for another channel',
+                    'arg' => 'CHAN',
+                    ),
+),
+            'doc' => '<parameter> [layer]
+Displays the value of one configuration parameter.  The
+first argument is the name of the parameter, an optional second argument
+may be used to tell which configuration layer to look in.  Valid configuration
+layers are "user", "system" and "default".  If no layer is specified, a value
+will be picked from the first layer that defines the parameter, in the order
+just specified.  The configuration value will be retrieved for the channel
+specified by the default_channel configuration variable.
+',
+            ),
+        'config-set' => array(
+            'summary' => 'Change Setting',
+            'function' => 'doConfigSet',
+            'shortcut' => 'cs',
+            'options' => array(
+                'channel' => array(
+                    'shortopt' => 'c',
+                    'doc' => 'show configuration variables for another channel',
+                    'arg' => 'CHAN',
+                    ),
+),
+            'doc' => '<parameter> <value> [layer]
+Sets the value of one configuration parameter.  The first argument is
+the name of the parameter, the second argument is the new value.  Some
+parameters are subject to validation, and the command will fail with
+an error message if the new value does not make sense.  An optional
+third argument may be used to specify in which layer to set the
+configuration parameter.  The default layer is "user".  The
+configuration value will be set for the current channel, which
+is controlled by the default_channel configuration variable.
+',
+            ),
+        'config-help' => array(
+            'summary' => 'Show Information About Setting',
+            'function' => 'doConfigHelp',
+            'shortcut' => 'ch',
+            'options' => array(),
+            'doc' => '[parameter]
+Displays help for a configuration parameter.  Without arguments it
+displays help for all configuration parameters.
+',
+           ),
+        'config-create' => array(
+            'summary' => 'Create a Default configuration file',
+            'function' => 'doConfigCreate',
+            'shortcut' => 'coc',
+            'options' => array(
+                'windows' => array(
+                    'shortopt' => 'w',
+                    'doc' => 'create a config file for a windows install',
+                    ),
+            ),
+            'doc' => '<root path> <filename>
+Create a default configuration file with all directory configuration
+variables set to subdirectories of <root path>, and save it as <filename>.
+This is useful especially for creating a configuration file for a remote
+PEAR installation (using the --remoteconfig option of install, upgrade,
+and uninstall).
+',
+            ),
+        );
+
+    /**
+     * PEAR_Command_Config constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Config(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    function doConfigShow($command, $options, $params)
+    {
+        $layer = null;
+        if (is_array($params)) {
+            $layer = isset($params[0]) ? $params[0] : null;
+        }
+
+        // $params[0] -> the layer
+        if ($error = $this->_checkLayer($layer)) {
+            return $this->raiseError("config-show:$error");
+        }
+
+        $keys = $this->config->getKeys();
+        sort($keys);
+        $channel = isset($options['channel']) ? $options['channel'] :
+            $this->config->get('default_channel');
+        $reg = &$this->config->getRegistry();
+        if (!$reg->channelExists($channel)) {
+            return $this->raiseError('Channel "' . $channel . '" does not exist');
+        }
+
+        $channel = $reg->channelName($channel);
+        $data = array('caption' => 'Configuration (channel ' . $channel . '):');
+        foreach ($keys as $key) {
+            $type = $this->config->getType($key);
+            $value = $this->config->get($key, $layer, $channel);
+            if ($type == 'password' && $value) {
+                $value = '********';
+            }
+
+            if ($value === false) {
+                $value = 'false';
+            } elseif ($value === true) {
+                $value = 'true';
+            }
+
+            $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value);
+        }
+
+        foreach ($this->config->getLayers() as $layer) {
+            $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , $this->config->getConfFile($layer));
+        }
+
+        $this->ui->outputData($data, $command);
+        return true;
+    }
+
+    function doConfigGet($command, $options, $params)
+    {
+        $args_cnt = is_array($params) ? count($params) : 0;
+        switch ($args_cnt) {
+            case 1:
+                $config_key = $params[0];
+                $layer = null;
+                break;
+            case 2:
+                $config_key = $params[0];
+                $layer = $params[1];
+                if ($error = $this->_checkLayer($layer)) {
+                    return $this->raiseError("config-get:$error");
+                }
+                break;
+            case 0:
+            default:
+                return $this->raiseError("config-get expects 1 or 2 parameters");
+        }
+
+        $reg = &$this->config->getRegistry();
+        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
+        if (!$reg->channelExists($channel)) {
+            return $this->raiseError('Channel "' . $channel . '" does not exist');
+        }
+
+        $channel = $reg->channelName($channel);
+        $this->ui->outputData($this->config->get($config_key, $layer, $channel), $command);
+        return true;
+    }
+
+    function doConfigSet($command, $options, $params)
+    {
+        // $param[0] -> a parameter to set
+        // $param[1] -> the value for the parameter
+        // $param[2] -> the layer
+        $failmsg = '';
+        if (count($params) < 2 || count($params) > 3) {
+            $failmsg .= "config-set expects 2 or 3 parameters";
+            return PEAR::raiseError($failmsg);
+        }
+
+        if (isset($params[2]) && ($error = $this->_checkLayer($params[2]))) {
+            $failmsg .= $error;
+            return PEAR::raiseError("config-set:$failmsg");
+        }
+
+        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
+        $reg = &$this->config->getRegistry();
+        if (!$reg->channelExists($channel)) {
+            return $this->raiseError('Channel "' . $channel . '" does not exist');
+        }
+
+        $channel = $reg->channelName($channel);
+        if ($params[0] == 'default_channel' && !$reg->channelExists($params[1])) {
+            return $this->raiseError('Channel "' . $params[1] . '" does not exist');
+        }
+
+        if ($params[0] == 'preferred_mirror'
+            && (
+                !$reg->mirrorExists($channel, $params[1]) &&
+                (!$reg->channelExists($params[1]) || $channel != $params[1])
+            )
+        ) {
+            $msg  = 'Channel Mirror "' . $params[1] . '" does not exist';
+            $msg .= ' in your registry for channel "' . $channel . '".';
+            $msg .= "\n" . 'Attempt to run "pear channel-update ' . $channel .'"';
+            $msg .= ' if you believe this mirror should exist as you may';
+            $msg .= ' have outdated channel information.';
+            return $this->raiseError($msg);
+        }
+
+        if (count($params) == 2) {
+            array_push($params, 'user');
+            $layer = 'user';
+        } else {
+            $layer = $params[2];
+        }
+
+        array_push($params, $channel);
+        if (!call_user_func_array(array(&$this->config, 'set'), $params)) {
+            array_pop($params);
+            $failmsg = "config-set (" . implode(", ", $params) . ") failed, channel $channel";
+        } else {
+            $this->config->store($layer);
+        }
+
+        if ($failmsg) {
+            return $this->raiseError($failmsg);
+        }
+
+        $this->ui->outputData('config-set succeeded', $command);
+        return true;
+    }
+
+    function doConfigHelp($command, $options, $params)
+    {
+        if (empty($params)) {
+            $params = $this->config->getKeys();
+        }
+
+        $data['caption']  = "Config help" . ((count($params) == 1) ? " for $params[0]" : '');
+        $data['headline'] = array('Name', 'Type', 'Description');
+        $data['border']   = true;
+        foreach ($params as $name) {
+            $type = $this->config->getType($name);
+            $docs = $this->config->getDocs($name);
+            if ($type == 'set') {
+                $docs = rtrim($docs) . "\nValid set: " .
+                    implode(' ', $this->config->getSetValues($name));
+            }
+
+            $data['data'][] = array($name, $type, $docs);
+        }
+
+        $this->ui->outputData($data, $command);
+    }
+
+    function doConfigCreate($command, $options, $params)
+    {
+        if (count($params) != 2) {
+            return PEAR::raiseError('config-create: must have 2 parameters, root path and ' .
+                'filename to save as');
+        }
+
+        $root = $params[0];
+        // Clean up the DIRECTORY_SEPARATOR mess
+        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
+        $root = preg_replace(array('!\\\\+!', '!/+!', "!$ds2+!"),
+                             array('/', '/', '/'),
+                            $root);
+        if ($root{0} != '/') {
+            if (!isset($options['windows'])) {
+                return PEAR::raiseError('Root directory must be an absolute path beginning ' .
+                    'with "/", was: "' . $root . '"');
+            }
+
+            if (!preg_match('/^[A-Za-z]:/', $root)) {
+                return PEAR::raiseError('Root directory must be an absolute path beginning ' .
+                    'with "\\" or "C:\\", was: "' . $root . '"');
+            }
+        }
+
+        $windows = isset($options['windows']);
+        if ($windows) {
+            $root = str_replace('/', '\\', $root);
+        }
+
+        if (!file_exists($params[1]) && !@touch($params[1])) {
+            return PEAR::raiseError('Could not create "' . $params[1] . '"');
+        }
+
+        $params[1] = realpath($params[1]);
+        $config = &new PEAR_Config($params[1], '#no#system#config#', false, false);
+        if ($root{strlen($root) - 1} == '/') {
+            $root = substr($root, 0, strlen($root) - 1);
+        }
+
+        $config->noRegistry();
+        $config->set('php_dir', $windows ? "$root\\pear\\php" : "$root/pear/php", 'user');
+        $config->set('data_dir', $windows ? "$root\\pear\\data" : "$root/pear/data");
+        $config->set('www_dir', $windows ? "$root\\pear\\www" : "$root/pear/www");
+        $config->set('cfg_dir', $windows ? "$root\\pear\\cfg" : "$root/pear/cfg");
+        $config->set('ext_dir', $windows ? "$root\\pear\\ext" : "$root/pear/ext");
+        $config->set('doc_dir', $windows ? "$root\\pear\\docs" : "$root/pear/docs");
+        $config->set('test_dir', $windows ? "$root\\pear\\tests" : "$root/pear/tests");
+        $config->set('cache_dir', $windows ? "$root\\pear\\cache" : "$root/pear/cache");
+        $config->set('download_dir', $windows ? "$root\\pear\\download" : "$root/pear/download");
+        $config->set('temp_dir', $windows ? "$root\\pear\\temp" : "$root/pear/temp");
+        $config->set('bin_dir', $windows ? "$root\\pear" : "$root/pear");
+        $config->writeConfigFile();
+        $this->_showConfig($config);
+        $this->ui->outputData('Successfully created default configuration file "' . $params[1] . '"',
+            $command);
+    }
+
+    function _showConfig(&$config)
+    {
+        $params = array('user');
+        $keys = $config->getKeys();
+        sort($keys);
+        $channel = 'pear.php.net';
+        $data = array('caption' => 'Configuration (channel ' . $channel . '):');
+        foreach ($keys as $key) {
+            $type = $config->getType($key);
+            $value = $config->get($key, 'user', $channel);
+            if ($type == 'password' && $value) {
+                $value = '********';
+            }
+
+            if ($value === false) {
+                $value = 'false';
+            } elseif ($value === true) {
+                $value = 'true';
+            }
+            $data['data'][$config->getGroup($key)][] =
+                array($config->getPrompt($key) , $key, $value);
+        }
+
+        foreach ($config->getLayers() as $layer) {
+            $data['data']['Config Files'][] =
+                array(ucfirst($layer) . ' Configuration File', 'Filename' ,
+                    $config->getConfFile($layer));
+        }
+
+        $this->ui->outputData($data, 'config-show');
+        return true;
+    }
+
+    /**
+     * Checks if a layer is defined or not
+     *
+     * @param string $layer The layer to search for
+     * @return mixed False on no error or the error message
+     */
+    function _checkLayer($layer = null)
+    {
+        if (!empty($layer) && $layer != 'default') {
+            $layers = $this->config->getLayers();
+            if (!in_array($layer, $layers)) {
+                return " only the layers: \"" . implode('" or "', $layers) . "\" are supported";
+            }
+        }
+
+        return false;
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Install.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Install.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Install.php	(revision 5146)
@@ -0,0 +1,1268 @@
+<?php
+/**
+ * PEAR_Command_Install (install, upgrade, upgrade-all, uninstall, bundle, run-scripts commands)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Install.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Common.php';
+
+/**
+ * PEAR commands for installation or deinstallation/upgrading of
+ * packages.
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Command_Install extends PEAR_Command_Common
+{
+    // {{{ properties
+
+    var $commands = array(
+        'install' => array(
+            'summary' => 'Install Package',
+            'function' => 'doInstall',
+            'shortcut' => 'i',
+            'options' => array(
+                'force' => array(
+                    'shortopt' => 'f',
+                    'doc' => 'will overwrite newer installed packages',
+                    ),
+                'loose' => array(
+                    'shortopt' => 'l',
+                    'doc' => 'do not check for recommended dependency version',
+                    ),
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, install anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not install files, only register the package as installed',
+                    ),
+                'soft' => array(
+                    'shortopt' => 's',
+                    'doc' => 'soft install, fail silently, or upgrade if already installed',
+                    ),
+                'nobuild' => array(
+                    'shortopt' => 'B',
+                    'doc' => 'don\'t build C extensions',
+                    ),
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'request uncompressed files when downloading',
+                    ),
+                'installroot' => array(
+                    'shortopt' => 'R',
+                    'arg' => 'DIR',
+                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
+                    ),
+                'packagingroot' => array(
+                    'shortopt' => 'P',
+                    'arg' => 'DIR',
+                    'doc' => 'root directory used when packaging files, like RPM packaging',
+                    ),
+                'ignore-errors' => array(
+                    'doc' => 'force install even if there were errors',
+                    ),
+                'alldeps' => array(
+                    'shortopt' => 'a',
+                    'doc' => 'install all required and optional dependencies',
+                    ),
+                'onlyreqdeps' => array(
+                    'shortopt' => 'o',
+                    'doc' => 'install all required dependencies',
+                    ),
+                'offline' => array(
+                    'shortopt' => 'O',
+                    'doc' => 'do not attempt to download any urls or contact channels',
+                    ),
+                'pretend' => array(
+                    'shortopt' => 'p',
+                    'doc' => 'Only list the packages that would be downloaded',
+                    ),
+                ),
+            'doc' => '[channel/]<package> ...
+Installs one or more PEAR packages.  You can specify a package to
+install in four ways:
+
+"Package-1.0.tgz" : installs from a local file
+
+"http://example.com/Package-1.0.tgz" : installs from
+anywhere on the net.
+
+"package.xml" : installs the package described in
+package.xml.  Useful for testing, or for wrapping a PEAR package in
+another package manager such as RPM.
+
+"Package[-version/state][.tar]" : queries your default channel\'s server
+({config master_server}) and downloads the newest package with
+the preferred quality/state ({config preferred_state}).
+
+To retrieve Package version 1.1, use "Package-1.1," to retrieve
+Package state beta, use "Package-beta."  To retrieve an uncompressed
+file, append .tar (make sure there is no file by the same name first)
+
+To download a package from another channel, prefix with the channel name like
+"channel/Package"
+
+More than one package may be specified at once.  It is ok to mix these
+four ways of specifying packages.
+'),
+        'upgrade' => array(
+            'summary' => 'Upgrade Package',
+            'function' => 'doInstall',
+            'shortcut' => 'up',
+            'options' => array(
+                'channel' => array(
+                    'shortopt' => 'c',
+                    'doc' => 'upgrade packages from a specific channel',
+                    'arg' => 'CHAN',
+                    ),
+                'force' => array(
+                    'shortopt' => 'f',
+                    'doc' => 'overwrite newer installed packages',
+                    ),
+                'loose' => array(
+                    'shortopt' => 'l',
+                    'doc' => 'do not check for recommended dependency version',
+                    ),
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, upgrade anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not install files, only register the package as upgraded',
+                    ),
+                'nobuild' => array(
+                    'shortopt' => 'B',
+                    'doc' => 'don\'t build C extensions',
+                    ),
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'request uncompressed files when downloading',
+                    ),
+                'installroot' => array(
+                    'shortopt' => 'R',
+                    'arg' => 'DIR',
+                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+                    ),
+                'ignore-errors' => array(
+                    'doc' => 'force install even if there were errors',
+                    ),
+                'alldeps' => array(
+                    'shortopt' => 'a',
+                    'doc' => 'install all required and optional dependencies',
+                    ),
+                'onlyreqdeps' => array(
+                    'shortopt' => 'o',
+                    'doc' => 'install all required dependencies',
+                    ),
+                'offline' => array(
+                    'shortopt' => 'O',
+                    'doc' => 'do not attempt to download any urls or contact channels',
+                    ),
+                'pretend' => array(
+                    'shortopt' => 'p',
+                    'doc' => 'Only list the packages that would be downloaded',
+                    ),
+                ),
+            'doc' => '<package> ...
+Upgrades one or more PEAR packages.  See documentation for the
+"install" command for ways to specify a package.
+
+When upgrading, your package will be updated if the provided new
+package has a higher version number (use the -f option if you need to
+upgrade anyway).
+
+More than one package may be specified at once.
+'),
+        'upgrade-all' => array(
+            'summary' => 'Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]',
+            'function' => 'doUpgradeAll',
+            'shortcut' => 'ua',
+            'options' => array(
+                'channel' => array(
+                    'shortopt' => 'c',
+                    'doc' => 'upgrade packages from a specific channel',
+                    'arg' => 'CHAN',
+                    ),
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, upgrade anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not install files, only register the package as upgraded',
+                    ),
+                'nobuild' => array(
+                    'shortopt' => 'B',
+                    'doc' => 'don\'t build C extensions',
+                    ),
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'request uncompressed files when downloading',
+                    ),
+                'installroot' => array(
+                    'shortopt' => 'R',
+                    'arg' => 'DIR',
+                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
+                    ),
+                'ignore-errors' => array(
+                    'doc' => 'force install even if there were errors',
+                    ),
+                'loose' => array(
+                    'doc' => 'do not check for recommended dependency version',
+                    ),
+                ),
+            'doc' => '
+WARNING: This function is deprecated in favor of using the upgrade command with no params
+
+Upgrades all packages that have a newer release available.  Upgrades are
+done only if there is a release available of the state specified in
+"preferred_state" (currently {config preferred_state}), or a state considered
+more stable.
+'),
+        'uninstall' => array(
+            'summary' => 'Un-install Package',
+            'function' => 'doUninstall',
+            'shortcut' => 'un',
+            'options' => array(
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, uninstall anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not remove files, only register the packages as not installed',
+                    ),
+                'installroot' => array(
+                    'shortopt' => 'R',
+                    'arg' => 'DIR',
+                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+                    ),
+                'ignore-errors' => array(
+                    'doc' => 'force install even if there were errors',
+                    ),
+                'offline' => array(
+                    'shortopt' => 'O',
+                    'doc' => 'do not attempt to uninstall remotely',
+                    ),
+                ),
+            'doc' => '[channel/]<package> ...
+Uninstalls one or more PEAR packages.  More than one package may be
+specified at once.  Prefix with channel name to uninstall from a
+channel not in your default channel ({config default_channel})
+'),
+        'bundle' => array(
+            'summary' => 'Unpacks a Pecl Package',
+            'function' => 'doBundle',
+            'shortcut' => 'bun',
+            'options' => array(
+                'destination' => array(
+                   'shortopt' => 'd',
+                    'arg' => 'DIR',
+                    'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)',
+                    ),
+                'force' => array(
+                    'shortopt' => 'f',
+                    'doc' => 'Force the unpacking even if there were errors in the package',
+                ),
+            ),
+            'doc' => '<package>
+Unpacks a Pecl Package into the selected location. It will download the
+package if needed.
+'),
+        'run-scripts' => array(
+            'summary' => 'Run Post-Install Scripts bundled with a package',
+            'function' => 'doRunScripts',
+            'shortcut' => 'rs',
+            'options' => array(
+            ),
+            'doc' => '<package>
+Run post-installation scripts in package <package>, if any exist.
+'),
+    );
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * PEAR_Command_Install constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Install(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    // }}}
+
+    /**
+     * For unit testing purposes
+     */
+    function &getDownloader(&$ui, $options, &$config)
+    {
+        if (!class_exists('PEAR_Downloader')) {
+            require_once 'PEAR/Downloader.php';
+        }
+        $a = &new PEAR_Downloader($ui, $options, $config);
+        return $a;
+    }
+
+    /**
+     * For unit testing purposes
+     */
+    function &getInstaller(&$ui)
+    {
+        if (!class_exists('PEAR_Installer')) {
+            require_once 'PEAR/Installer.php';
+        }
+        $a = &new PEAR_Installer($ui);
+        return $a;
+    }
+
+    function enableExtension($binaries, $type)
+    {
+        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
+            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
+        }
+        $ini = $this->_parseIni($phpini);
+        if (PEAR::isError($ini)) {
+            return $ini;
+        }
+        $line = 0;
+        if ($type == 'extsrc' || $type == 'extbin') {
+            $search = 'extensions';
+            $enable = 'extension';
+        } else {
+            $search = 'zend_extensions';
+            ob_start();
+            phpinfo(INFO_GENERAL);
+            $info = ob_get_contents();
+            ob_end_clean();
+            $debug = function_exists('leak') ? '_debug' : '';
+            $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
+            $enable = 'zend_extension' . $debug . $ts;
+        }
+        foreach ($ini[$search] as $line => $extension) {
+            if (in_array($extension, $binaries, true) || in_array(
+                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
+                // already enabled - assume if one is, all are
+                return true;
+            }
+        }
+        if ($line) {
+            $newini = array_slice($ini['all'], 0, $line);
+        } else {
+            $newini = array();
+        }
+        foreach ($binaries as $binary) {
+            if ($ini['extension_dir']) {
+                $binary = basename($binary);
+            }
+            $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n");
+        }
+        $newini = array_merge($newini, array_slice($ini['all'], $line));
+        $fp = @fopen($phpini, 'wb');
+        if (!$fp) {
+            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
+        }
+        foreach ($newini as $line) {
+            fwrite($fp, $line);
+        }
+        fclose($fp);
+        return true;
+    }
+
+    function disableExtension($binaries, $type)
+    {
+        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
+            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
+        }
+        $ini = $this->_parseIni($phpini);
+        if (PEAR::isError($ini)) {
+            return $ini;
+        }
+        $line = 0;
+        if ($type == 'extsrc' || $type == 'extbin') {
+            $search = 'extensions';
+            $enable = 'extension';
+        } else {
+            $search = 'zend_extensions';
+            ob_start();
+            phpinfo(INFO_GENERAL);
+            $info = ob_get_contents();
+            ob_end_clean();
+            $debug = function_exists('leak') ? '_debug' : '';
+            $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
+            $enable = 'zend_extension' . $debug . $ts;
+        }
+        $found = false;
+        foreach ($ini[$search] as $line => $extension) {
+            if (in_array($extension, $binaries, true) || in_array(
+                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
+                $found = true;
+                break;
+            }
+        }
+        if (!$found) {
+            // not enabled
+            return true;
+        }
+        $fp = @fopen($phpini, 'wb');
+        if (!$fp) {
+            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
+        }
+        if ($line) {
+            $newini = array_slice($ini['all'], 0, $line);
+            // delete the enable line
+            $newini = array_merge($newini, array_slice($ini['all'], $line + 1));
+        } else {
+            $newini = array_slice($ini['all'], 1);
+        }
+        foreach ($newini as $line) {
+            fwrite($fp, $line);
+        }
+        fclose($fp);
+        return true;
+    }
+
+    function _parseIni($filename)
+    {
+        if (!file_exists($filename)) {
+            return PEAR::raiseError('php.ini "' . $filename . '" does not exist');
+        }
+
+        if (filesize($filename) > 300000) {
+            return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting');
+        }
+
+        ob_start();
+        phpinfo(INFO_GENERAL);
+        $info = ob_get_contents();
+        ob_end_clean();
+        $debug = function_exists('leak') ? '_debug' : '';
+        $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
+        $zend_extension_line = 'zend_extension' . $debug . $ts;
+        $all = @file($filename);
+        if (!$all) {
+            return PEAR::raiseError('php.ini "' . $filename .'" could not be read');
+        }
+        $zend_extensions = $extensions = array();
+        // assume this is right, but pull from the php.ini if it is found
+        $extension_dir = ini_get('extension_dir');
+        foreach ($all as $linenum => $line) {
+            $line = trim($line);
+            if (!$line) {
+                continue;
+            }
+            if ($line[0] == ';') {
+                continue;
+            }
+            if (strtolower(substr($line, 0, 13)) == 'extension_dir') {
+                $line = trim(substr($line, 13));
+                if ($line[0] == '=') {
+                    $x = trim(substr($line, 1));
+                    $x = explode(';', $x);
+                    $extension_dir = str_replace('"', '', array_shift($x));
+                    continue;
+                }
+            }
+            if (strtolower(substr($line, 0, 9)) == 'extension') {
+                $line = trim(substr($line, 9));
+                if ($line[0] == '=') {
+                    $x = trim(substr($line, 1));
+                    $x = explode(';', $x);
+                    $extensions[$linenum] = str_replace('"', '', array_shift($x));
+                    continue;
+                }
+            }
+            if (strtolower(substr($line, 0, strlen($zend_extension_line))) ==
+                  $zend_extension_line) {
+                $line = trim(substr($line, strlen($zend_extension_line)));
+                if ($line[0] == '=') {
+                    $x = trim(substr($line, 1));
+                    $x = explode(';', $x);
+                    $zend_extensions[$linenum] = str_replace('"', '', array_shift($x));
+                    continue;
+                }
+            }
+        }
+        return array(
+            'extensions' => $extensions,
+            'zend_extensions' => $zend_extensions,
+            'extension_dir' => $extension_dir,
+            'all' => $all,
+        );
+    }
+
+    // {{{ doInstall()
+
+    function doInstall($command, $options, $params)
+    {
+        if (!class_exists('PEAR_PackageFile')) {
+            require_once 'PEAR/PackageFile.php';
+        }
+
+        if (isset($options['installroot']) && isset($options['packagingroot'])) {
+            return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot');
+        }
+
+        $reg = &$this->config->getRegistry();
+        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
+        if (!$reg->channelExists($channel)) {
+            return $this->raiseError('Channel "' . $channel . '" does not exist');
+        }
+
+        if (empty($this->installer)) {
+            $this->installer = &$this->getInstaller($this->ui);
+        }
+
+        if ($command == 'upgrade' || $command == 'upgrade-all') {
+            // If people run the upgrade command but pass nothing, emulate a upgrade-all
+            if ($command == 'upgrade' && empty($params)) {
+                return $this->doUpgradeAll($command, $options, $params);
+            }
+            $options['upgrade'] = true;
+        } else {
+            $packages = $params;
+        }
+
+        $instreg = &$reg; // instreg used to check if package is installed
+        if (isset($options['packagingroot']) && !isset($options['upgrade'])) {
+            $packrootphp_dir = $this->installer->_prependPath(
+                $this->config->get('php_dir', null, 'pear.php.net'),
+                $options['packagingroot']);
+            $instreg = new PEAR_Registry($packrootphp_dir); // other instreg!
+
+            if ($this->config->get('verbose') > 2) {
+                $this->ui->outputData('using package root: ' . $options['packagingroot']);
+            }
+        }
+
+        $abstractpackages = $otherpackages = array();
+        // parse params
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+
+        foreach ($params as $param) {
+            if (strpos($param, 'http://') === 0) {
+                $otherpackages[] = $param;
+                continue;
+            }
+
+            if (strpos($param, 'channel://') === false && @file_exists($param)) {
+                if (isset($options['force'])) {
+                    $otherpackages[] = $param;
+                    continue;
+                }
+
+                $pkg = new PEAR_PackageFile($this->config);
+                $pf  = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING);
+                if (PEAR::isError($pf)) {
+                    $otherpackages[] = $param;
+                    continue;
+                }
+
+                $exists   = $reg->packageExists($pf->getPackage(), $pf->getChannel());
+                $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel());
+                $version_compare = version_compare($pf->getVersion(), $pversion, '<=');
+                if ($exists && $version_compare) {
+                    if ($this->config->get('verbose')) {
+                        $this->ui->outputData('Ignoring installed package ' .
+                            $reg->parsedPackageNameToString(
+                            array('package' => $pf->getPackage(),
+                                  'channel' => $pf->getChannel()), true));
+                    }
+                    continue;
+                }
+                $otherpackages[] = $param;
+                continue;
+            }
+
+            $e = $reg->parsePackageName($param, $channel);
+            if (PEAR::isError($e)) {
+                $otherpackages[] = $param;
+            } else {
+                $abstractpackages[] = $e;
+            }
+        }
+        PEAR::staticPopErrorHandling();
+
+        // if there are any local package .tgz or remote static url, we can't
+        // filter.  The filter only works for abstract packages
+        if (count($abstractpackages) && !isset($options['force'])) {
+            // when not being forced, only do necessary upgrades/installs
+            if (isset($options['upgrade'])) {
+                $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command);
+            } else {
+                $count = count($abstractpackages);
+                foreach ($abstractpackages as $i => $package) {
+                    if (isset($package['group'])) {
+                        // do not filter out install groups
+                        continue;
+                    }
+
+                    if ($instreg->packageExists($package['package'], $package['channel'])) {
+                        if ($count > 1) {
+                            if ($this->config->get('verbose')) {
+                                $this->ui->outputData('Ignoring installed package ' .
+                                    $reg->parsedPackageNameToString($package, true));
+                            }
+                            unset($abstractpackages[$i]);
+                        } elseif ($count === 1) {
+                            // Lets try to upgrade it since it's already installed
+                            $options['upgrade'] = true;
+                        }
+                    }
+                }
+            }
+            $abstractpackages =
+                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
+        } elseif (count($abstractpackages)) {
+            $abstractpackages =
+                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
+        }
+
+        $packages = array_merge($abstractpackages, $otherpackages);
+        if (!count($packages)) {
+            $c = '';
+            if (isset($options['channel'])){
+                $c .= ' in channel "' . $options['channel'] . '"';
+            }
+            $this->ui->outputData('Nothing to ' . $command . $c);
+            return true;
+        }
+
+        $this->downloader = &$this->getDownloader($this->ui, $options, $this->config);
+        $errors = $downloaded = $binaries = array();
+        $downloaded = &$this->downloader->download($packages);
+        if (PEAR::isError($downloaded)) {
+            return $this->raiseError($downloaded);
+        }
+
+        $errors = $this->downloader->getErrorMsgs();
+        if (count($errors)) {
+            $err = array();
+            $err['data'] = array();
+            foreach ($errors as $error) {
+                if ($error !== null) {
+                    $err['data'][] = array($error);
+                }
+            }
+
+            if (!empty($err['data'])) {
+                $err['headline'] = 'Install Errors';
+                $this->ui->outputData($err);
+            }
+
+            if (!count($downloaded)) {
+                return $this->raiseError("$command failed");
+            }
+        }
+
+        $data = array(
+            'headline' => 'Packages that would be Installed'
+        );
+
+        if (isset($options['pretend'])) {
+            foreach ($downloaded as $package) {
+                $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage()));
+            }
+            $this->ui->outputData($data, 'pretend');
+            return true;
+        }
+
+        $this->installer->setOptions($options);
+        $this->installer->sortPackagesForInstall($downloaded);
+        if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) {
+            $this->raiseError($err->getMessage());
+            return true;
+        }
+
+        $binaries = $extrainfo = array();
+        foreach ($downloaded as $param) {
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $info = $this->installer->install($param, $options);
+            PEAR::staticPopErrorHandling();
+            if (PEAR::isError($info)) {
+                $oldinfo = $info;
+                $pkg = &$param->getPackageFile();
+                if ($info->getCode() != PEAR_INSTALLER_NOBINARY) {
+                    if (!($info = $pkg->installBinary($this->installer))) {
+                        $this->ui->outputData('ERROR: ' .$oldinfo->getMessage());
+                        continue;
+                    }
+
+                    // we just installed a different package than requested,
+                    // let's change the param and info so that the rest of this works
+                    $param = $info[0];
+                    $info  = $info[1];
+                }
+            }
+
+            if (!is_array($info)) {
+                return $this->raiseError("$command failed");
+            }
+
+            if ($param->getPackageType() == 'extsrc' ||
+                  $param->getPackageType() == 'extbin' ||
+                  $param->getPackageType() == 'zendextsrc' ||
+                  $param->getPackageType() == 'zendextbin'
+            ) {
+                $pkg = &$param->getPackageFile();
+                if ($instbin = $pkg->getInstalledBinary()) {
+                    $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel());
+                } else {
+                    $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel());
+                }
+
+                foreach ($instpkg->getFilelist() as $name => $atts) {
+                    $pinfo = pathinfo($atts['installed_as']);
+                    if (!isset($pinfo['extension']) ||
+                          in_array($pinfo['extension'], array('c', 'h'))
+                    ) {
+                        continue; // make sure we don't match php_blah.h
+                    }
+
+                    if ((strpos($pinfo['basename'], 'php_') === 0 &&
+                          $pinfo['extension'] == 'dll') ||
+                          // most unices
+                          $pinfo['extension'] == 'so' ||
+                          // hp-ux
+                          $pinfo['extension'] == 'sl') {
+                        $binaries[] = array($atts['installed_as'], $pinfo);
+                        break;
+                    }
+                }
+
+                if (count($binaries)) {
+                    foreach ($binaries as $pinfo) {
+                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                        $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType());
+                        PEAR::staticPopErrorHandling();
+                        if (PEAR::isError($ret)) {
+                            $extrainfo[] = $ret->getMessage();
+                            if ($param->getPackageType() == 'extsrc' ||
+                                  $param->getPackageType() == 'extbin') {
+                                $exttype = 'extension';
+                            } else {
+                                ob_start();
+                                phpinfo(INFO_GENERAL);
+                                $info = ob_get_contents();
+                                ob_end_clean();
+                                $debug = function_exists('leak') ? '_debug' : '';
+                                $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
+                                $exttype = 'zend_extension' . $debug . $ts;
+                            }
+                            $extrainfo[] = 'You should add "' . $exttype . '=' .
+                                $pinfo[1]['basename'] . '" to php.ini';
+                        } else {
+                            $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() .
+                                ' enabled in php.ini';
+                        }
+                    }
+                }
+            }
+
+            if ($this->config->get('verbose') > 0) {
+                $chan = $param->getChannel();
+                $label = $reg->parsedPackageNameToString(
+                    array(
+                        'channel' => $chan,
+                        'package' => $param->getPackage(),
+                        'version' => $param->getVersion(),
+                    ));
+                $out = array('data' => "$command ok: $label");
+                if (isset($info['release_warnings'])) {
+                    $out['release_warnings'] = $info['release_warnings'];
+                }
+                $this->ui->outputData($out, $command);
+
+                if (!isset($options['register-only']) && !isset($options['offline'])) {
+                    if ($this->config->isDefinedLayer('ftp')) {
+                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                        $info = $this->installer->ftpInstall($param);
+                        PEAR::staticPopErrorHandling();
+                        if (PEAR::isError($info)) {
+                            $this->ui->outputData($info->getMessage());
+                            $this->ui->outputData("remote install failed: $label");
+                        } else {
+                            $this->ui->outputData("remote install ok: $label");
+                        }
+                    }
+                }
+            }
+
+            $deps = $param->getDeps();
+            if ($deps) {
+                if (isset($deps['group'])) {
+                    $groups = $deps['group'];
+                    if (!isset($groups[0])) {
+                        $groups = array($groups);
+                    }
+
+                    foreach ($groups as $group) {
+                        if ($group['attribs']['name'] == 'default') {
+                            // default group is always installed, unless the user
+                            // explicitly chooses to install another group
+                            continue;
+                        }
+                        $extrainfo[] = $param->getPackage() . ': Optional feature ' .
+                            $group['attribs']['name'] . ' available (' .
+                            $group['attribs']['hint'] . ')';
+                    }
+
+                    $extrainfo[] = $param->getPackage() .
+                        ': To install optional features use "pear install ' .
+                        $reg->parsedPackageNameToString(
+                            array('package' => $param->getPackage(),
+                                  'channel' => $param->getChannel()), true) .
+                              '#featurename"';
+                }
+            }
+
+            $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel());
+            // $pkg may be NULL if install is a 'fake' install via --packagingroot
+            if (is_object($pkg)) {
+                $pkg->setConfig($this->config);
+                if ($list = $pkg->listPostinstallScripts()) {
+                    $pn = $reg->parsedPackageNameToString(array('channel' =>
+                       $param->getChannel(), 'package' => $param->getPackage()), true);
+                    $extrainfo[] = $pn . ' has post-install scripts:';
+                    foreach ($list as $file) {
+                        $extrainfo[] = $file;
+                    }
+                    $extrainfo[] = $param->getPackage() .
+                        ': Use "pear run-scripts ' . $pn . '" to finish setup.';
+                    $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES';
+                }
+            }
+        }
+
+        if (count($extrainfo)) {
+            foreach ($extrainfo as $info) {
+                $this->ui->outputData($info);
+            }
+        }
+
+        return true;
+    }
+
+    // }}}
+    // {{{ doUpgradeAll()
+
+    function doUpgradeAll($command, $options, $params)
+    {
+        $reg = &$this->config->getRegistry();
+        $upgrade = array();
+
+        if (isset($options['channel'])) {
+            $channels = array($options['channel']);
+        } else {
+            $channels = $reg->listChannels();
+        }
+
+        foreach ($channels as $channel) {
+            if ($channel == '__uri') {
+                continue;
+            }
+
+            // parse name with channel
+            foreach ($reg->listPackages($channel) as $name) {
+                $upgrade[] = $reg->parsedPackageNameToString(array(
+                        'channel' => $channel,
+                        'package' => $name
+                    ));
+            }
+        }
+
+        $err = $this->doInstall($command, $options, $upgrade);
+        if (PEAR::isError($err)) {
+            $this->ui->outputData($err->getMessage(), $command);
+        }
+   }
+
+    // }}}
+    // {{{ doUninstall()
+
+    function doUninstall($command, $options, $params)
+    {
+        if (count($params) < 1) {
+            return $this->raiseError("Please supply the package(s) you want to uninstall");
+        }
+
+        if (empty($this->installer)) {
+            $this->installer = &$this->getInstaller($this->ui);
+        }
+
+        if (isset($options['remoteconfig'])) {
+            $e = $this->config->readFTPConfigFile($options['remoteconfig']);
+            if (!PEAR::isError($e)) {
+                $this->installer->setConfig($this->config);
+            }
+        }
+
+        $reg = &$this->config->getRegistry();
+        $newparams = array();
+        $binaries = array();
+        $badparams = array();
+        foreach ($params as $pkg) {
+            $channel = $this->config->get('default_channel');
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $parsed = $reg->parsePackageName($pkg, $channel);
+            PEAR::staticPopErrorHandling();
+            if (!$parsed || PEAR::isError($parsed)) {
+                $badparams[] = $pkg;
+                continue;
+            }
+            $package = $parsed['package'];
+            $channel = $parsed['channel'];
+            $info = &$reg->getPackage($package, $channel);
+            if ($info === null &&
+                 ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) {
+                // make sure this isn't a package that has flipped from pear to pecl but
+                // used a package.xml 1.0
+                $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net';
+                $info = &$reg->getPackage($package, $testc);
+                if ($info !== null) {
+                    $channel = $testc;
+                }
+            }
+            if ($info === null) {
+                $badparams[] = $pkg;
+            } else {
+                $newparams[] = &$info;
+                // check for binary packages (this is an alias for those packages if so)
+                if ($installedbinary = $info->getInstalledBinary()) {
+                    $this->ui->log('adding binary package ' .
+                        $reg->parsedPackageNameToString(array('channel' => $channel,
+                            'package' => $installedbinary), true));
+                    $newparams[] = &$reg->getPackage($installedbinary, $channel);
+                }
+                // add the contents of a dependency group to the list of installed packages
+                if (isset($parsed['group'])) {
+                    $group = $info->getDependencyGroup($parsed['group']);
+                    if ($group) {
+                        $installed = $reg->getInstalledGroup($group);
+                        if ($installed) {
+                            foreach ($installed as $i => $p) {
+                                $newparams[] = &$installed[$i];
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        $err = $this->installer->sortPackagesForUninstall($newparams);
+        if (PEAR::isError($err)) {
+            $this->ui->outputData($err->getMessage(), $command);
+            return true;
+        }
+        $params = $newparams;
+        // twist this to use it to check on whether dependent packages are also being uninstalled
+        // for circular dependencies like subpackages
+        $this->installer->setUninstallPackages($newparams);
+        $params = array_merge($params, $badparams);
+        $binaries = array();
+        foreach ($params as $pkg) {
+            $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
+            if ($err = $this->installer->uninstall($pkg, $options)) {
+                $this->installer->popErrorHandling();
+                if (PEAR::isError($err)) {
+                    $this->ui->outputData($err->getMessage(), $command);
+                    continue;
+                }
+                if ($pkg->getPackageType() == 'extsrc' ||
+                      $pkg->getPackageType() == 'extbin' ||
+                      $pkg->getPackageType() == 'zendextsrc' ||
+                      $pkg->getPackageType() == 'zendextbin') {
+                    if ($instbin = $pkg->getInstalledBinary()) {
+                        continue; // this will be uninstalled later
+                    }
+
+                    foreach ($pkg->getFilelist() as $name => $atts) {
+                        $pinfo = pathinfo($atts['installed_as']);
+                        if (!isset($pinfo['extension']) ||
+                              in_array($pinfo['extension'], array('c', 'h'))) {
+                            continue; // make sure we don't match php_blah.h
+                        }
+                        if ((strpos($pinfo['basename'], 'php_') === 0 &&
+                              $pinfo['extension'] == 'dll') ||
+                              // most unices
+                              $pinfo['extension'] == 'so' ||
+                              // hp-ux
+                              $pinfo['extension'] == 'sl') {
+                            $binaries[] = array($atts['installed_as'], $pinfo);
+                            break;
+                        }
+                    }
+                    if (count($binaries)) {
+                        foreach ($binaries as $pinfo) {
+                            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                            $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType());
+                            PEAR::staticPopErrorHandling();
+                            if (PEAR::isError($ret)) {
+                                $extrainfo[] = $ret->getMessage();
+                                if ($pkg->getPackageType() == 'extsrc' ||
+                                      $pkg->getPackageType() == 'extbin') {
+                                    $exttype = 'extension';
+                                } else {
+                                    ob_start();
+                                    phpinfo(INFO_GENERAL);
+                                    $info = ob_get_contents();
+                                    ob_end_clean();
+                                    $debug = function_exists('leak') ? '_debug' : '';
+                                    $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
+                                    $exttype = 'zend_extension' . $debug . $ts;
+                                }
+                                $this->ui->outputData('Unable to remove "' . $exttype . '=' .
+                                    $pinfo[1]['basename'] . '" from php.ini', $command);
+                            } else {
+                                $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() .
+                                    ' disabled in php.ini', $command);
+                            }
+                        }
+                    }
+                }
+                $savepkg = $pkg;
+                if ($this->config->get('verbose') > 0) {
+                    if (is_object($pkg)) {
+                        $pkg = $reg->parsedPackageNameToString($pkg);
+                    }
+                    $this->ui->outputData("uninstall ok: $pkg", $command);
+                }
+                if (!isset($options['offline']) && is_object($savepkg) &&
+                      defined('PEAR_REMOTEINSTALL_OK')) {
+                    if ($this->config->isDefinedLayer('ftp')) {
+                        $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
+                        $info = $this->installer->ftpUninstall($savepkg);
+                        $this->installer->popErrorHandling();
+                        if (PEAR::isError($info)) {
+                            $this->ui->outputData($info->getMessage());
+                            $this->ui->outputData("remote uninstall failed: $pkg");
+                        } else {
+                            $this->ui->outputData("remote uninstall ok: $pkg");
+                        }
+                    }
+                }
+            } else {
+                $this->installer->popErrorHandling();
+                if (!is_object($pkg)) {
+                    return $this->raiseError("uninstall failed: $pkg");
+                }
+                $pkg = $reg->parsedPackageNameToString($pkg);
+            }
+        }
+
+        return true;
+    }
+
+    // }}}
+
+
+    // }}}
+    // {{{ doBundle()
+    /*
+    (cox) It just downloads and untars the package, does not do
+            any check that the PEAR_Installer::_installFile() does.
+    */
+
+    function doBundle($command, $options, $params)
+    {
+        $opts = array(
+            'force'        => true,
+            'nodeps'       => true,
+            'soft'         => true,
+            'downloadonly' => true
+        );
+        $downloader = &$this->getDownloader($this->ui, $opts, $this->config);
+        $reg = &$this->config->getRegistry();
+        if (count($params) < 1) {
+            return $this->raiseError("Please supply the package you want to bundle");
+        }
+
+        if (isset($options['destination'])) {
+            if (!is_dir($options['destination'])) {
+                System::mkdir('-p ' . $options['destination']);
+            }
+            $dest = realpath($options['destination']);
+        } else {
+            $pwd  = getcwd();
+            $dir  = $pwd . DIRECTORY_SEPARATOR . 'ext';
+            $dest = is_dir($dir) ? $dir : $pwd;
+        }
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $err = $downloader->setDownloadDir($dest);
+        PEAR::staticPopErrorHandling();
+        if (PEAR::isError($err)) {
+            return PEAR::raiseError('download directory "' . $dest .
+                '" is not writeable.');
+        }
+        $result = &$downloader->download(array($params[0]));
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        if (!isset($result[0])) {
+            return $this->raiseError('unable to unpack ' . $params[0]);
+        }
+        $pkgfile = &$result[0]->getPackageFile();
+        $pkgname = $pkgfile->getName();
+        $pkgversion = $pkgfile->getVersion();
+
+        // Unpacking -------------------------------------------------
+        $dest .= DIRECTORY_SEPARATOR . $pkgname;
+        $orig = $pkgname . '-' . $pkgversion;
+
+        $tar = &new Archive_Tar($pkgfile->getArchiveFile());
+        if (!$tar->extractModify($dest, $orig)) {
+            return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile());
+        }
+        $this->ui->outputData("Package ready at '$dest'");
+    // }}}
+    }
+
+    // }}}
+
+    function doRunScripts($command, $options, $params)
+    {
+        if (!isset($params[0])) {
+            return $this->raiseError('run-scripts expects 1 parameter: a package name');
+        }
+
+        $reg = &$this->config->getRegistry();
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
+        PEAR::staticPopErrorHandling();
+        if (PEAR::isError($parsed)) {
+            return $this->raiseError($parsed);
+        }
+
+        $package = &$reg->getPackage($parsed['package'], $parsed['channel']);
+        if (!is_object($package)) {
+            return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry');
+        }
+
+        $package->setConfig($this->config);
+        $package->runPostinstallScripts();
+        $this->ui->outputData('Install scripts complete', $command);
+        return true;
+    }
+
+    /**
+     * Given a list of packages, filter out those ones that are already up to date
+     *
+     * @param $packages: packages, in parsed array format !
+     * @return list of packages that can be upgraded
+     */
+    function _filterUptodatePackages($packages, $command)
+    {
+        $reg = &$this->config->getRegistry();
+        $latestReleases = array();
+
+        $ret = array();
+        foreach ($packages as $package) {
+            if (isset($package['group'])) {
+                $ret[] = $package;
+                continue;
+            }
+
+            $channel = $package['channel'];
+            $name    = $package['package'];
+            if (!$reg->packageExists($name, $channel)) {
+                $ret[] = $package;
+                continue;
+            }
+
+            if (!isset($latestReleases[$channel])) {
+                // fill in cache for this channel
+                $chan = &$reg->getChannel($channel);
+                if (PEAR::isError($chan)) {
+                    return $this->raiseError($chan);
+                }
+
+                $base2 = false;
+                $preferred_mirror = $this->config->get('preferred_mirror', null, $channel);
+                if ($chan->supportsREST($preferred_mirror) &&
+                    (
+                       //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) ||
+                       ($base  = $chan->getBaseURL('REST1.0', $preferred_mirror))
+                    )
+                ) {
+                    $dorest = true;
+                }
+
+                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+                if (!isset($package['state'])) {
+                    $state = $this->config->get('preferred_state', null, $channel);
+                } else {
+                    $state = $package['state'];
+                }
+
+                if ($dorest) {
+                    if ($base2) {
+                        $rest = &$this->config->getREST('1.4', array());
+                        $base = $base2;
+                    } else {
+                        $rest = &$this->config->getREST('1.0', array());
+                    }
+
+                    $installed = array_flip($reg->listPackages($channel));
+                    $latest    = $rest->listLatestUpgrades($base, $state, $installed, $channel, $reg);
+                }
+
+                PEAR::staticPopErrorHandling();
+                if (PEAR::isError($latest)) {
+                    $this->ui->outputData('Error getting channel info from ' . $channel .
+                        ': ' . $latest->getMessage());
+                    continue;
+                }
+
+                $latestReleases[$channel] = array_change_key_case($latest);
+            }
+
+            // check package for latest release
+            $name_lower = strtolower($name);
+            if (isset($latestReleases[$channel][$name_lower])) {
+                // if not set, up to date
+                $inst_version    = $reg->packageInfo($name, 'version', $channel);
+                $channel_version = $latestReleases[$channel][$name_lower]['version'];
+                if (version_compare($channel_version, $inst_version, 'le')) {
+                    // installed version is up-to-date
+                    continue;
+                }
+
+                // maintain BC
+                if ($command == 'upgrade-all') {
+                    $this->ui->outputData(array('data' => 'Will upgrade ' .
+                        $reg->parsedPackageNameToString($package)), $command);
+                }
+                $ret[] = $package;
+            }
+        }
+
+        return $ret;
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Pickle.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Pickle.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Pickle.php	(revision 5146)
@@ -0,0 +1,421 @@
+<?php
+/**
+ * PEAR_Command_Pickle (pickle command)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  2005-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Pickle.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.4.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Common.php';
+
+/**
+ * PEAR commands for login/logout
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  2005-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.4.1
+ */
+
+class PEAR_Command_Pickle extends PEAR_Command_Common
+{
+    var $commands = array(
+        'pickle' => array(
+            'summary' => 'Build PECL Package',
+            'function' => 'doPackage',
+            'shortcut' => 'pi',
+            'options' => array(
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'Do not gzip the package file'
+                    ),
+                'showname' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'Print the name of the packaged file.',
+                    ),
+                ),
+            'doc' => '[descfile]
+Creates a PECL package from its package2.xml file.
+
+An automatic conversion will be made to a package.xml 1.0 and written out to
+disk in the current directory as "package.xml".  Note that
+only simple package.xml 2.0 will be converted.  package.xml 2.0 with:
+
+ - dependency types other than required/optional PECL package/ext/php/pearinstaller
+ - more than one extsrcrelease or zendextsrcrelease
+ - zendextbinrelease, extbinrelease, phprelease, or bundle release type
+ - dependency groups
+ - ignore tags in release filelist
+ - tasks other than replace
+ - custom roles
+
+will cause pickle to fail, and output an error message.  If your package2.xml
+uses any of these features, you are best off using PEAR_PackageFileManager to
+generate both package.xml.
+'
+            ),
+        );
+
+    /**
+     * PEAR_Command_Package constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Pickle(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    /**
+     * For unit-testing ease
+     *
+     * @return PEAR_Packager
+     */
+    function &getPackager()
+    {
+        if (!class_exists('PEAR_Packager')) {
+            require_once 'PEAR/Packager.php';
+        }
+
+        $a = &new PEAR_Packager;
+        return $a;
+    }
+
+    /**
+     * For unit-testing ease
+     *
+     * @param PEAR_Config $config
+     * @param bool $debug
+     * @param string|null $tmpdir
+     * @return PEAR_PackageFile
+     */
+    function &getPackageFile($config, $debug = false)
+    {
+        if (!class_exists('PEAR_Common')) {
+            require_once 'PEAR/Common.php';
+        }
+
+        if (!class_exists('PEAR_PackageFile')) {
+            require_once 'PEAR/PackageFile.php';
+        }
+
+        $a = &new PEAR_PackageFile($config, $debug);
+        $common = new PEAR_Common;
+        $common->ui = $this->ui;
+        $a->setLogger($common);
+        return $a;
+    }
+
+    function doPackage($command, $options, $params)
+    {
+        $this->output = '';
+        $pkginfofile = isset($params[0]) ? $params[0] : 'package2.xml';
+        $packager = &$this->getPackager();
+        if (PEAR::isError($err = $this->_convertPackage($pkginfofile))) {
+            return $err;
+        }
+
+        $compress = empty($options['nocompress']) ? true : false;
+        $result = $packager->package($pkginfofile, $compress, 'package.xml');
+        if (PEAR::isError($result)) {
+            return $this->raiseError($result);
+        }
+
+        // Don't want output, only the package file name just created
+        if (isset($options['showname'])) {
+            $this->ui->outputData($result, $command);
+        }
+
+        return true;
+    }
+
+    function _convertPackage($packagexml)
+    {
+        $pkg = &$this->getPackageFile($this->config);
+        $pf2 = &$pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
+        if (!is_a($pf2, 'PEAR_PackageFile_v2')) {
+            return $this->raiseError('Cannot process "' .
+                $packagexml . '", is not a package.xml 2.0');
+        }
+
+        require_once 'PEAR/PackageFile/v1.php';
+        $pf = new PEAR_PackageFile_v1;
+        $pf->setConfig($this->config);
+        if ($pf2->getPackageType() != 'extsrc' && $pf2->getPackageType() != 'zendextsrc') {
+            return $this->raiseError('Cannot safely convert "' . $packagexml .
+            '", is not an extension source package.  Using a PEAR_PackageFileManager-based ' .
+            'script is an option');
+        }
+
+        if (is_array($pf2->getUsesRole())) {
+            return $this->raiseError('Cannot safely convert "' . $packagexml .
+            '", contains custom roles.  Using a PEAR_PackageFileManager-based script or ' .
+            'the convert command is an option');
+        }
+
+        if (is_array($pf2->getUsesTask())) {
+            return $this->raiseError('Cannot safely convert "' . $packagexml .
+            '", contains custom tasks.  Using a PEAR_PackageFileManager-based script or ' .
+            'the convert command is an option');
+        }
+
+        $deps = $pf2->getDependencies();
+        if (isset($deps['group'])) {
+            return $this->raiseError('Cannot safely convert "' . $packagexml .
+            '", contains dependency groups.  Using a PEAR_PackageFileManager-based script ' .
+            'or the convert command is an option');
+        }
+
+        if (isset($deps['required']['subpackage']) ||
+              isset($deps['optional']['subpackage'])) {
+            return $this->raiseError('Cannot safely convert "' . $packagexml .
+            '", contains subpackage dependencies.  Using a PEAR_PackageFileManager-based  '.
+            'script is an option');
+        }
+
+        if (isset($deps['required']['os'])) {
+            return $this->raiseError('Cannot safely convert "' . $packagexml .
+            '", contains os dependencies.  Using a PEAR_PackageFileManager-based  '.
+            'script is an option');
+        }
+
+        if (isset($deps['required']['arch'])) {
+            return $this->raiseError('Cannot safely convert "' . $packagexml .
+            '", contains arch dependencies.  Using a PEAR_PackageFileManager-based  '.
+            'script is an option');
+        }
+
+        $pf->setPackage($pf2->getPackage());
+        $pf->setSummary($pf2->getSummary());
+        $pf->setDescription($pf2->getDescription());
+        foreach ($pf2->getMaintainers() as $maintainer) {
+            $pf->addMaintainer($maintainer['role'], $maintainer['handle'],
+                $maintainer['name'], $maintainer['email']);
+        }
+
+        $pf->setVersion($pf2->getVersion());
+        $pf->setDate($pf2->getDate());
+        $pf->setLicense($pf2->getLicense());
+        $pf->setState($pf2->getState());
+        $pf->setNotes($pf2->getNotes());
+        $pf->addPhpDep($deps['required']['php']['min'], 'ge');
+        if (isset($deps['required']['php']['max'])) {
+            $pf->addPhpDep($deps['required']['php']['max'], 'le');
+        }
+
+        if (isset($deps['required']['package'])) {
+            if (!isset($deps['required']['package'][0])) {
+                $deps['required']['package'] = array($deps['required']['package']);
+            }
+
+            foreach ($deps['required']['package'] as $dep) {
+                if (!isset($dep['channel'])) {
+                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
+                    ' contains uri-based dependency on a package.  Using a ' .
+                    'PEAR_PackageFileManager-based script is an option');
+                }
+
+                if ($dep['channel'] != 'pear.php.net'
+                    && $dep['channel'] != 'pecl.php.net'
+                    && $dep['channel'] != 'doc.php.net') {
+                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
+                    ' contains dependency on a non-standard channel package.  Using a ' .
+                    'PEAR_PackageFileManager-based script is an option');
+                }
+
+                if (isset($dep['conflicts'])) {
+                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
+                    ' contains conflicts dependency.  Using a ' .
+                    'PEAR_PackageFileManager-based script is an option');
+                }
+
+                if (isset($dep['exclude'])) {
+                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
+                }
+
+                if (isset($dep['min'])) {
+                    $pf->addPackageDep($dep['name'], $dep['min'], 'ge');
+                }
+
+                if (isset($dep['max'])) {
+                    $pf->addPackageDep($dep['name'], $dep['max'], 'le');
+                }
+            }
+        }
+
+        if (isset($deps['required']['extension'])) {
+            if (!isset($deps['required']['extension'][0])) {
+                $deps['required']['extension'] = array($deps['required']['extension']);
+            }
+
+            foreach ($deps['required']['extension'] as $dep) {
+                if (isset($dep['conflicts'])) {
+                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
+                    ' contains conflicts dependency.  Using a ' .
+                    'PEAR_PackageFileManager-based script is an option');
+                }
+
+                if (isset($dep['exclude'])) {
+                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
+                }
+
+                if (isset($dep['min'])) {
+                    $pf->addExtensionDep($dep['name'], $dep['min'], 'ge');
+                }
+
+                if (isset($dep['max'])) {
+                    $pf->addExtensionDep($dep['name'], $dep['max'], 'le');
+                }
+            }
+        }
+
+        if (isset($deps['optional']['package'])) {
+            if (!isset($deps['optional']['package'][0])) {
+                $deps['optional']['package'] = array($deps['optional']['package']);
+            }
+
+            foreach ($deps['optional']['package'] as $dep) {
+                if (!isset($dep['channel'])) {
+                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
+                    ' contains uri-based dependency on a package.  Using a ' .
+                    'PEAR_PackageFileManager-based script is an option');
+                }
+
+                if ($dep['channel'] != 'pear.php.net'
+                    && $dep['channel'] != 'pecl.php.net'
+                    && $dep['channel'] != 'doc.php.net') {
+                    return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
+                    ' contains dependency on a non-standard channel package.  Using a ' .
+                    'PEAR_PackageFileManager-based script is an option');
+                }
+
+                if (isset($dep['exclude'])) {
+                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
+                }
+
+                if (isset($dep['min'])) {
+                    $pf->addPackageDep($dep['name'], $dep['min'], 'ge', 'yes');
+                }
+
+                if (isset($dep['max'])) {
+                    $pf->addPackageDep($dep['name'], $dep['max'], 'le', 'yes');
+                }
+            }
+        }
+
+        if (isset($deps['optional']['extension'])) {
+            if (!isset($deps['optional']['extension'][0])) {
+                $deps['optional']['extension'] = array($deps['optional']['extension']);
+            }
+
+            foreach ($deps['optional']['extension'] as $dep) {
+                if (isset($dep['exclude'])) {
+                    $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
+                }
+
+                if (isset($dep['min'])) {
+                    $pf->addExtensionDep($dep['name'], $dep['min'], 'ge', 'yes');
+                }
+
+                if (isset($dep['max'])) {
+                    $pf->addExtensionDep($dep['name'], $dep['max'], 'le', 'yes');
+                }
+            }
+        }
+
+        $contents = $pf2->getContents();
+        $release  = $pf2->getReleases();
+        if (isset($releases[0])) {
+            return $this->raiseError('Cannot safely process "' . $packagexml . '" contains '
+            . 'multiple extsrcrelease/zendextsrcrelease tags.  Using a PEAR_PackageFileManager-based script ' .
+            'or the convert command is an option');
+        }
+
+        if ($configoptions = $pf2->getConfigureOptions()) {
+            foreach ($configoptions as $option) {
+                $default = isset($option['default']) ? $option['default'] : false;
+                $pf->addConfigureOption($option['name'], $option['prompt'], $default);
+            }
+        }
+
+        if (isset($release['filelist']['ignore'])) {
+            return $this->raiseError('Cannot safely process "' . $packagexml . '" contains '
+            . 'ignore tags.  Using a PEAR_PackageFileManager-based script or the convert' .
+            ' command is an option');
+        }
+
+        if (isset($release['filelist']['install']) &&
+              !isset($release['filelist']['install'][0])) {
+            $release['filelist']['install'] = array($release['filelist']['install']);
+        }
+
+        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
+            $baseinstalldir = $contents['dir']['attribs']['baseinstalldir'];
+        } else {
+            $baseinstalldir = false;
+        }
+
+        if (!isset($contents['dir']['file'][0])) {
+            $contents['dir']['file'] = array($contents['dir']['file']);
+        }
+
+        foreach ($contents['dir']['file'] as $file) {
+            if ($baseinstalldir && !isset($file['attribs']['baseinstalldir'])) {
+                $file['attribs']['baseinstalldir'] = $baseinstalldir;
+            }
+
+            $processFile = $file;
+            unset($processFile['attribs']);
+            if (count($processFile)) {
+                foreach ($processFile as $name => $task) {
+                    if ($name != $pf2->getTasksNs() . ':replace') {
+                        return $this->raiseError('Cannot safely process "' . $packagexml .
+                        '" contains tasks other than replace.  Using a ' .
+                        'PEAR_PackageFileManager-based script is an option.');
+                    }
+                    $file['attribs']['replace'][] = $task;
+                }
+            }
+
+            if (!in_array($file['attribs']['role'], PEAR_Common::getFileRoles())) {
+                return $this->raiseError('Cannot safely convert "' . $packagexml .
+                '", contains custom roles.  Using a PEAR_PackageFileManager-based script ' .
+                'or the convert command is an option');
+            }
+
+            if (isset($release['filelist']['install'])) {
+                foreach ($release['filelist']['install'] as $installas) {
+                    if ($installas['attribs']['name'] == $file['attribs']['name']) {
+                        $file['attribs']['install-as'] = $installas['attribs']['as'];
+                    }
+                }
+            }
+
+            $pf->addFile('/', $file['attribs']['name'], $file['attribs']);
+        }
+
+        if ($pf2->getChangeLog()) {
+            $this->ui->outputData('WARNING: changelog is not translated to package.xml ' .
+                '1.0, use PEAR_PackageFileManager-based script if you need changelog-' .
+                'translation for package.xml 1.0');
+        }
+
+        $gen = &$pf->getDefaultGenerator();
+        $gen->toPackageFile('.');
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Registry.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Registry.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Registry.php	(revision 5146)
@@ -0,0 +1,1145 @@
+<?php
+/**
+ * PEAR_Command_Registry (list, list-files, shell-test, info commands)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Registry.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Common.php';
+
+/**
+ * PEAR commands for registry manipulation
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Command_Registry extends PEAR_Command_Common
+{
+    var $commands = array(
+        'list' => array(
+            'summary' => 'List Installed Packages In The Default Channel',
+            'function' => 'doList',
+            'shortcut' => 'l',
+            'options' => array(
+                'channel' => array(
+                    'shortopt' => 'c',
+                    'doc' => 'list installed packages from this channel',
+                    'arg' => 'CHAN',
+                    ),
+                'allchannels' => array(
+                    'shortopt' => 'a',
+                    'doc' => 'list installed packages from all channels',
+                    ),
+                'channelinfo' => array(
+                    'shortopt' => 'i',
+                    'doc' => 'output fully channel-aware data, even on failure',
+                    ),
+                ),
+            'doc' => '<package>
+If invoked without parameters, this command lists the PEAR packages
+installed in your php_dir ({config php_dir}).  With a parameter, it
+lists the files in a package.
+',
+            ),
+        'list-files' => array(
+            'summary' => 'List Files In Installed Package',
+            'function' => 'doFileList',
+            'shortcut' => 'fl',
+            'options' => array(),
+            'doc' => '<package>
+List the files in an installed package.
+'
+            ),
+        'shell-test' => array(
+            'summary' => 'Shell Script Test',
+            'function' => 'doShellTest',
+            'shortcut' => 'st',
+            'options' => array(),
+            'doc' => '<package> [[relation] version]
+Tests if a package is installed in the system. Will exit(1) if it is not.
+   <relation>   The version comparison operator. One of:
+                <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
+   <version>    The version to compare with
+'),
+        'info' => array(
+            'summary'  => 'Display information about a package',
+            'function' => 'doInfo',
+            'shortcut' => 'in',
+            'options'  => array(),
+            'doc'      => '<package>
+Displays information about a package. The package argument may be a
+local package file, an URL to a package file, or the name of an
+installed package.'
+            )
+        );
+
+    /**
+     * PEAR_Command_Registry constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Registry(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    function _sortinfo($a, $b)
+    {
+        $apackage = isset($a['package']) ? $a['package'] : $a['name'];
+        $bpackage = isset($b['package']) ? $b['package'] : $b['name'];
+        return strcmp($apackage, $bpackage);
+    }
+
+    function doList($command, $options, $params)
+    {
+        $reg = &$this->config->getRegistry();
+        $channelinfo = isset($options['channelinfo']);
+        if (isset($options['allchannels']) && !$channelinfo) {
+            return $this->doListAll($command, array(), $params);
+        }
+
+        if (isset($options['allchannels']) && $channelinfo) {
+            // allchannels with $channelinfo
+            unset($options['allchannels']);
+            $channels = $reg->getChannels();
+            $errors = array();
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            foreach ($channels as $channel) {
+                $options['channel'] = $channel->getName();
+                $ret = $this->doList($command, $options, $params);
+
+                if (PEAR::isError($ret)) {
+                    $errors[] = $ret;
+                }
+            }
+
+            PEAR::staticPopErrorHandling();
+            if (count($errors)) {
+                // for now, only give first error
+                return PEAR::raiseError($errors[0]);
+            }
+
+            return true;
+        }
+
+        if (count($params) === 1) {
+            return $this->doFileList($command, $options, $params);
+        }
+
+        if (isset($options['channel'])) {
+            if (!$reg->channelExists($options['channel'])) {
+                return $this->raiseError('Channel "' . $options['channel'] .'" does not exist');
+            }
+
+            $channel = $reg->channelName($options['channel']);
+        } else {
+            $channel = $this->config->get('default_channel');
+        }
+
+        $installed = $reg->packageInfo(null, null, $channel);
+        usort($installed, array(&$this, '_sortinfo'));
+
+        $data = array(
+            'caption' => 'Installed packages, channel ' .
+                $channel . ':',
+            'border' => true,
+            'headline' => array('Package', 'Version', 'State'),
+            'channel' => $channel,
+            );
+        if ($channelinfo) {
+            $data['headline'] = array('Channel', 'Package', 'Version', 'State');
+        }
+
+        if (count($installed) && !isset($data['data'])) {
+            $data['data'] = array();
+        }
+
+        foreach ($installed as $package) {
+            $pobj = $reg->getPackage(isset($package['package']) ?
+                                        $package['package'] : $package['name'], $channel);
+            if ($channelinfo) {
+                $packageinfo = array($pobj->getChannel(), $pobj->getPackage(), $pobj->getVersion(),
+                                    $pobj->getState() ? $pobj->getState() : null);
+            } else {
+                $packageinfo = array($pobj->getPackage(), $pobj->getVersion(),
+                                    $pobj->getState() ? $pobj->getState() : null);
+            }
+            $data['data'][] = $packageinfo;
+        }
+
+        if (count($installed) === 0) {
+            if (!$channelinfo) {
+                $data = '(no packages installed from channel ' . $channel . ')';
+            } else {
+                $data = array(
+                    'caption' => 'Installed packages, channel ' .
+                        $channel . ':',
+                    'border' => true,
+                    'channel' => $channel,
+                    'data' => array(array('(no packages installed)')),
+                );
+            }
+        }
+
+        $this->ui->outputData($data, $command);
+        return true;
+    }
+
+    function doListAll($command, $options, $params)
+    {
+        // This duplicate code is deprecated over
+        // list --channelinfo, which gives identical
+        // output for list and list --allchannels.
+        $reg = &$this->config->getRegistry();
+        $installed = $reg->packageInfo(null, null, null);
+        foreach ($installed as $channel => $packages) {
+            usort($packages, array($this, '_sortinfo'));
+            $data = array(
+                'caption'  => 'Installed packages, channel ' . $channel . ':',
+                'border'   => true,
+                'headline' => array('Package', 'Version', 'State'),
+                'channel'  => $channel
+            );
+
+            foreach ($packages as $package) {
+                $p = isset($package['package']) ? $package['package'] : $package['name'];
+                $pobj = $reg->getPackage($p, $channel);
+                $data['data'][] = array($pobj->getPackage(), $pobj->getVersion(),
+                                        $pobj->getState() ? $pobj->getState() : null);
+            }
+
+            // Adds a blank line after each section
+            $data['data'][] = array();
+
+            if (count($packages) === 0) {
+                $data = array(
+                    'caption' => 'Installed packages, channel ' . $channel . ':',
+                    'border' => true,
+                    'data' => array(array('(no packages installed)'), array()),
+                    'channel' => $channel
+                    );
+            }
+            $this->ui->outputData($data, $command);
+        }
+        return true;
+    }
+
+    function doFileList($command, $options, $params)
+    {
+        if (count($params) !== 1) {
+            return $this->raiseError('list-files expects 1 parameter');
+        }
+
+        $reg = &$this->config->getRegistry();
+        $fp = false;
+        if (!is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))) {
+            if ($fp) {
+                fclose($fp);
+            }
+
+            if (!class_exists('PEAR_PackageFile')) {
+                require_once 'PEAR/PackageFile.php';
+            }
+
+            $pkg = &new PEAR_PackageFile($this->config, $this->_debug);
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $info = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
+            PEAR::staticPopErrorHandling();
+            $headings = array('Package File', 'Install Path');
+            $installed = false;
+        } else {
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
+            PEAR::staticPopErrorHandling();
+            if (PEAR::isError($parsed)) {
+                return $this->raiseError($parsed);
+            }
+
+            $info = &$reg->getPackage($parsed['package'], $parsed['channel']);
+            $headings = array('Type', 'Install Path');
+            $installed = true;
+        }
+
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+
+        if ($info === null) {
+            return $this->raiseError("`$params[0]' not installed");
+        }
+
+        $list = ($info->getPackagexmlVersion() == '1.0' || $installed) ?
+            $info->getFilelist() : $info->getContents();
+        if ($installed) {
+            $caption = 'Installed Files For ' . $params[0];
+        } else {
+            $caption = 'Contents of ' . basename($params[0]);
+        }
+
+        $data = array(
+            'caption' => $caption,
+            'border' => true,
+            'headline' => $headings);
+        if ($info->getPackagexmlVersion() == '1.0' || $installed) {
+            foreach ($list as $file => $att) {
+                if ($installed) {
+                    if (empty($att['installed_as'])) {
+                        continue;
+                    }
+                    $data['data'][] = array($att['role'], $att['installed_as']);
+                } else {
+                    if (isset($att['baseinstalldir']) && !in_array($att['role'],
+                          array('test', 'data', 'doc'))) {
+                        $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR .
+                            $file;
+                    } else {
+                        $dest = $file;
+                    }
+                    switch ($att['role']) {
+                        case 'test':
+                        case 'data':
+                        case 'doc':
+                            $role = $att['role'];
+                            if ($role == 'test') {
+                                $role .= 's';
+                            }
+                            $dest = $this->config->get($role . '_dir') . DIRECTORY_SEPARATOR .
+                                $info->getPackage() . DIRECTORY_SEPARATOR . $dest;
+                            break;
+                        case 'php':
+                        default:
+                            $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR .
+                                $dest;
+                    }
+                    $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
+                    $dest = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
+                                                    array(DIRECTORY_SEPARATOR,
+                                                          DIRECTORY_SEPARATOR,
+                                                          DIRECTORY_SEPARATOR),
+                                                    $dest);
+                    $file = preg_replace('!/+!', '/', $file);
+                    $data['data'][] = array($file, $dest);
+                }
+            }
+        } else { // package.xml 2.0, not installed
+            if (!isset($list['dir']['file'][0])) {
+                $list['dir']['file'] = array($list['dir']['file']);
+            }
+
+            foreach ($list['dir']['file'] as $att) {
+                $att = $att['attribs'];
+                $file = $att['name'];
+                $role = &PEAR_Installer_Role::factory($info, $att['role'], $this->config);
+                $role->setup($this, $info, $att, $file);
+                if (!$role->isInstallable()) {
+                    $dest = '(not installable)';
+                } else {
+                    $dest = $role->processInstallation($info, $att, $file, '');
+                    if (PEAR::isError($dest)) {
+                        $dest = '(Unknown role "' . $att['role'] . ')';
+                    } else {
+                        list(,, $dest) = $dest;
+                    }
+                }
+                $data['data'][] = array($file, $dest);
+            }
+        }
+
+        $this->ui->outputData($data, $command);
+        return true;
+    }
+
+    function doShellTest($command, $options, $params)
+    {
+        if (count($params) < 1) {
+            return PEAR::raiseError('ERROR, usage: pear shell-test packagename [[relation] version]');
+        }
+
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $reg = &$this->config->getRegistry();
+        $info = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
+        if (PEAR::isError($info)) {
+            exit(1); // invalid package name
+        }
+
+        $package = $info['package'];
+        $channel = $info['channel'];
+        // "pear shell-test Foo"
+        if (!$reg->packageExists($package, $channel)) {
+            if ($channel == 'pecl.php.net') {
+                if ($reg->packageExists($package, 'pear.php.net')) {
+                    $channel = 'pear.php.net'; // magically change channels for extensions
+                }
+            }
+        }
+
+        if (count($params) === 1) {
+            if (!$reg->packageExists($package, $channel)) {
+                exit(1);
+            }
+            // "pear shell-test Foo 1.0"
+        } elseif (count($params) === 2) {
+            $v = $reg->packageInfo($package, 'version', $channel);
+            if (!$v || !version_compare("$v", "{$params[1]}", "ge")) {
+                exit(1);
+            }
+            // "pear shell-test Foo ge 1.0"
+        } elseif (count($params) === 3) {
+            $v = $reg->packageInfo($package, 'version', $channel);
+            if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) {
+                exit(1);
+            }
+        } else {
+            PEAR::staticPopErrorHandling();
+            $this->raiseError("$command: expects 1 to 3 parameters");
+            exit(1);
+        }
+    }
+
+    function doInfo($command, $options, $params)
+    {
+        if (count($params) !== 1) {
+            return $this->raiseError('pear info expects 1 parameter');
+        }
+
+        $info = $fp = false;
+        $reg = &$this->config->getRegistry();
+        if (is_file($params[0]) && !is_dir($params[0]) &&
+            (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))
+        ) {
+            if ($fp) {
+                fclose($fp);
+            }
+
+            if (!class_exists('PEAR_PackageFile')) {
+                require_once 'PEAR/PackageFile.php';
+            }
+
+            $pkg = &new PEAR_PackageFile($this->config, $this->_debug);
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            $obj = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
+            PEAR::staticPopErrorHandling();
+            if (PEAR::isError($obj)) {
+                $uinfo = $obj->getUserInfo();
+                if (is_array($uinfo)) {
+                    foreach ($uinfo as $message) {
+                        if (is_array($message)) {
+                            $message = $message['message'];
+                        }
+                        $this->ui->outputData($message);
+                    }
+                }
+
+                return $this->raiseError($obj);
+            }
+
+            if ($obj->getPackagexmlVersion() != '1.0') {
+                return $this->_doInfo2($command, $options, $params, $obj, false);
+            }
+
+            $info = $obj->toArray();
+        } else {
+            $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
+            if (PEAR::isError($parsed)) {
+                return $this->raiseError($parsed);
+            }
+
+            $package = $parsed['package'];
+            $channel = $parsed['channel'];
+            $info = $reg->packageInfo($package, null, $channel);
+            if (isset($info['old'])) {
+                $obj = $reg->getPackage($package, $channel);
+                return $this->_doInfo2($command, $options, $params, $obj, true);
+            }
+        }
+
+        if (PEAR::isError($info)) {
+            return $info;
+        }
+
+        if (empty($info)) {
+            $this->raiseError("No information found for `$params[0]'");
+            return;
+        }
+
+        unset($info['filelist']);
+        unset($info['dirtree']);
+        unset($info['changelog']);
+        if (isset($info['xsdversion'])) {
+            $info['package.xml version'] = $info['xsdversion'];
+            unset($info['xsdversion']);
+        }
+
+        if (isset($info['packagerversion'])) {
+            $info['packaged with PEAR version'] = $info['packagerversion'];
+            unset($info['packagerversion']);
+        }
+
+        $keys = array_keys($info);
+        $longtext = array('description', 'summary');
+        foreach ($keys as $key) {
+            if (is_array($info[$key])) {
+                switch ($key) {
+                    case 'maintainers': {
+                        $i = 0;
+                        $mstr = '';
+                        foreach ($info[$key] as $m) {
+                            if ($i++ > 0) {
+                                $mstr .= "\n";
+                            }
+                            $mstr .= $m['name'] . " <";
+                            if (isset($m['email'])) {
+                                $mstr .= $m['email'];
+                            } else {
+                                $mstr .= $m['handle'] . '@php.net';
+                            }
+                            $mstr .= "> ($m[role])";
+                        }
+                        $info[$key] = $mstr;
+                        break;
+                    }
+                    case 'release_deps': {
+                        $i = 0;
+                        $dstr = '';
+                        foreach ($info[$key] as $d) {
+                            if (isset($this->_deps_rel_trans[$d['rel']])) {
+                                $rel = $this->_deps_rel_trans[$d['rel']];
+                            } else {
+                                $rel = $d['rel'];
+                            }
+                            if (isset($this->_deps_type_trans[$d['type']])) {
+                                $type = ucfirst($this->_deps_type_trans[$d['type']]);
+                            } else {
+                                $type = $d['type'];
+                            }
+                            if (isset($d['name'])) {
+                                $name = $d['name'] . ' ';
+                            } else {
+                                $name = '';
+                            }
+                            if (isset($d['version'])) {
+                                $version = $d['version'] . ' ';
+                            } else {
+                                $version = '';
+                            }
+                            if (isset($d['optional']) && $d['optional'] == 'yes') {
+                                $optional = ' (optional)';
+                            } else {
+                                $optional = '';
+                            }
+                            $dstr .= "$type $name$rel $version$optional\n";
+                        }
+                        $info[$key] = $dstr;
+                        break;
+                    }
+                    case 'provides' : {
+                        $debug = $this->config->get('verbose');
+                        if ($debug < 2) {
+                            $pstr = 'Classes: ';
+                        } else {
+                            $pstr = '';
+                        }
+                        $i = 0;
+                        foreach ($info[$key] as $p) {
+                            if ($debug < 2 && $p['type'] != "class") {
+                                continue;
+                            }
+                            // Only print classes when verbosity mode is < 2
+                            if ($debug < 2) {
+                                if ($i++ > 0) {
+                                    $pstr .= ", ";
+                                }
+                                $pstr .= $p['name'];
+                            } else {
+                                if ($i++ > 0) {
+                                    $pstr .= "\n";
+                                }
+                                $pstr .= ucfirst($p['type']) . " " . $p['name'];
+                                if (isset($p['explicit']) && $p['explicit'] == 1) {
+                                    $pstr .= " (explicit)";
+                                }
+                            }
+                        }
+                        $info[$key] = $pstr;
+                        break;
+                    }
+                    case 'configure_options' : {
+                        foreach ($info[$key] as $i => $p) {
+                            $info[$key][$i] = array_map(null, array_keys($p), array_values($p));
+                            $info[$key][$i] = array_map(create_function('$a',
+                                'return join(" = ",$a);'), $info[$key][$i]);
+                            $info[$key][$i] = implode(', ', $info[$key][$i]);
+                        }
+                        $info[$key] = implode("\n", $info[$key]);
+                        break;
+                    }
+                    default: {
+                        $info[$key] = implode(", ", $info[$key]);
+                        break;
+                    }
+                }
+            }
+
+            if ($key == '_lastmodified') {
+                $hdate = date('Y-m-d', $info[$key]);
+                unset($info[$key]);
+                $info['Last Modified'] = $hdate;
+            } elseif ($key == '_lastversion') {
+                $info['Previous Installed Version'] = $info[$key] ? $info[$key] : '- None -';
+                unset($info[$key]);
+            } else {
+                $info[$key] = trim($info[$key]);
+                if (in_array($key, $longtext)) {
+                    $info[$key] = preg_replace('/  +/', ' ', $info[$key]);
+                }
+            }
+        }
+
+        $caption = 'About ' . $info['package'] . '-' . $info['version'];
+        $data = array(
+            'caption' => $caption,
+            'border' => true);
+        foreach ($info as $key => $value) {
+            $key = ucwords(trim(str_replace('_', ' ', $key)));
+            $data['data'][] = array($key, $value);
+        }
+        $data['raw'] = $info;
+
+        $this->ui->outputData($data, 'package-info');
+    }
+
+    /**
+     * @access private
+     */
+    function _doInfo2($command, $options, $params, &$obj, $installed)
+    {
+        $reg = &$this->config->getRegistry();
+        $caption = 'About ' . $obj->getChannel() . '/' .$obj->getPackage() . '-' .
+            $obj->getVersion();
+        $data = array(
+            'caption' => $caption,
+            'border' => true);
+        switch ($obj->getPackageType()) {
+            case 'php' :
+                $release = 'PEAR-style PHP-based Package';
+            break;
+            case 'extsrc' :
+                $release = 'PECL-style PHP extension (source code)';
+            break;
+            case 'zendextsrc' :
+                $release = 'PECL-style Zend extension (source code)';
+            break;
+            case 'extbin' :
+                $release = 'PECL-style PHP extension (binary)';
+            break;
+            case 'zendextbin' :
+                $release = 'PECL-style Zend extension (binary)';
+            break;
+            case 'bundle' :
+                $release = 'Package bundle (collection of packages)';
+            break;
+        }
+        $extends = $obj->getExtends();
+        $extends = $extends ?
+            $obj->getPackage() . ' (extends ' . $extends . ')' : $obj->getPackage();
+        if ($src = $obj->getSourcePackage()) {
+            $extends .= ' (source package ' . $src['channel'] . '/' . $src['package'] . ')';
+        }
+
+        $info = array(
+            'Release Type' => $release,
+            'Name' => $extends,
+            'Channel' => $obj->getChannel(),
+            'Summary' => preg_replace('/  +/', ' ', $obj->getSummary()),
+            'Description' => preg_replace('/  +/', ' ', $obj->getDescription()),
+            );
+        $info['Maintainers'] = '';
+        foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
+            $leads = $obj->{"get{$role}s"}();
+            if (!$leads) {
+                continue;
+            }
+
+            if (isset($leads['active'])) {
+                $leads = array($leads);
+            }
+
+            foreach ($leads as $lead) {
+                if (!empty($info['Maintainers'])) {
+                    $info['Maintainers'] .= "\n";
+                }
+
+                $active = $lead['active'] == 'no' ? ', inactive' : '';
+                $info['Maintainers'] .= $lead['name'] . ' <';
+                $info['Maintainers'] .= $lead['email'] . "> ($role$active)";
+            }
+        }
+
+        $info['Release Date'] = $obj->getDate();
+        if ($time = $obj->getTime()) {
+            $info['Release Date'] .= ' ' . $time;
+        }
+
+        $info['Release Version'] = $obj->getVersion() . ' (' . $obj->getState() . ')';
+        $info['API Version'] = $obj->getVersion('api') . ' (' . $obj->getState('api') . ')';
+        $info['License'] = $obj->getLicense();
+        $uri = $obj->getLicenseLocation();
+        if ($uri) {
+            if (isset($uri['uri'])) {
+                $info['License'] .= ' (' . $uri['uri'] . ')';
+            } else {
+                $extra = $obj->getInstalledLocation($info['filesource']);
+                if ($extra) {
+                    $info['License'] .= ' (' . $uri['filesource'] . ')';
+                }
+            }
+        }
+
+        $info['Release Notes'] = $obj->getNotes();
+        if ($compat = $obj->getCompatible()) {
+            if (!isset($compat[0])) {
+                $compat = array($compat);
+            }
+
+            $info['Compatible with'] = '';
+            foreach ($compat as $package) {
+                $info['Compatible with'] .= $package['channel'] . '/' . $package['name'] .
+                    "\nVersions >= " . $package['min'] . ', <= ' . $package['max'];
+                if (isset($package['exclude'])) {
+                    if (is_array($package['exclude'])) {
+                        $package['exclude'] = implode(', ', $package['exclude']);
+                    }
+
+                    if (!isset($info['Not Compatible with'])) {
+                        $info['Not Compatible with'] = '';
+                    } else {
+                        $info['Not Compatible with'] .= "\n";
+                    }
+                    $info['Not Compatible with'] .= $package['channel'] . '/' .
+                        $package['name'] . "\nVersions " . $package['exclude'];
+                }
+            }
+        }
+
+        $usesrole = $obj->getUsesrole();
+        if ($usesrole) {
+            if (!isset($usesrole[0])) {
+                $usesrole = array($usesrole);
+            }
+
+            foreach ($usesrole as $roledata) {
+                if (isset($info['Uses Custom Roles'])) {
+                    $info['Uses Custom Roles'] .= "\n";
+                } else {
+                    $info['Uses Custom Roles'] = '';
+                }
+
+                if (isset($roledata['package'])) {
+                    $rolepackage = $reg->parsedPackageNameToString($roledata, true);
+                } else {
+                    $rolepackage = $roledata['uri'];
+                }
+                $info['Uses Custom Roles'] .= $roledata['role'] . ' (' . $rolepackage . ')';
+            }
+        }
+
+        $usestask = $obj->getUsestask();
+        if ($usestask) {
+            if (!isset($usestask[0])) {
+                $usestask = array($usestask);
+            }
+
+            foreach ($usestask as $taskdata) {
+                if (isset($info['Uses Custom Tasks'])) {
+                    $info['Uses Custom Tasks'] .= "\n";
+                } else {
+                    $info['Uses Custom Tasks'] = '';
+                }
+
+                if (isset($taskdata['package'])) {
+                    $taskpackage = $reg->parsedPackageNameToString($taskdata, true);
+                } else {
+                    $taskpackage = $taskdata['uri'];
+                }
+                $info['Uses Custom Tasks'] .= $taskdata['task'] . ' (' . $taskpackage . ')';
+            }
+        }
+
+        $deps = $obj->getDependencies();
+        $info['Required Dependencies'] = 'PHP version ' . $deps['required']['php']['min'];
+        if (isset($deps['required']['php']['max'])) {
+            $info['Required Dependencies'] .= '-' . $deps['required']['php']['max'] . "\n";
+        } else {
+            $info['Required Dependencies'] .= "\n";
+        }
+
+        if (isset($deps['required']['php']['exclude'])) {
+            if (!isset($info['Not Compatible with'])) {
+                $info['Not Compatible with'] = '';
+            } else {
+                $info['Not Compatible with'] .= "\n";
+            }
+
+            if (is_array($deps['required']['php']['exclude'])) {
+                $deps['required']['php']['exclude'] =
+                    implode(', ', $deps['required']['php']['exclude']);
+            }
+            $info['Not Compatible with'] .= "PHP versions\n  " .
+                $deps['required']['php']['exclude'];
+        }
+
+        $info['Required Dependencies'] .= 'PEAR installer version';
+        if (isset($deps['required']['pearinstaller']['max'])) {
+            $info['Required Dependencies'] .= 's ' .
+                $deps['required']['pearinstaller']['min'] . '-' .
+                $deps['required']['pearinstaller']['max'];
+        } else {
+            $info['Required Dependencies'] .= ' ' .
+                $deps['required']['pearinstaller']['min'] . ' or newer';
+        }
+
+        if (isset($deps['required']['pearinstaller']['exclude'])) {
+            if (!isset($info['Not Compatible with'])) {
+                $info['Not Compatible with'] = '';
+            } else {
+                $info['Not Compatible with'] .= "\n";
+            }
+
+            if (is_array($deps['required']['pearinstaller']['exclude'])) {
+                $deps['required']['pearinstaller']['exclude'] =
+                    implode(', ', $deps['required']['pearinstaller']['exclude']);
+            }
+            $info['Not Compatible with'] .= "PEAR installer\n  Versions " .
+                $deps['required']['pearinstaller']['exclude'];
+        }
+
+        foreach (array('Package', 'Extension') as $type) {
+            $index = strtolower($type);
+            if (isset($deps['required'][$index])) {
+                if (isset($deps['required'][$index]['name'])) {
+                    $deps['required'][$index] = array($deps['required'][$index]);
+                }
+
+                foreach ($deps['required'][$index] as $package) {
+                    if (isset($package['conflicts'])) {
+                        $infoindex = 'Not Compatible with';
+                        if (!isset($info['Not Compatible with'])) {
+                            $info['Not Compatible with'] = '';
+                        } else {
+                            $info['Not Compatible with'] .= "\n";
+                        }
+                    } else {
+                        $infoindex = 'Required Dependencies';
+                        $info[$infoindex] .= "\n";
+                    }
+
+                    if ($index == 'extension') {
+                        $name = $package['name'];
+                    } else {
+                        if (isset($package['channel'])) {
+                            $name = $package['channel'] . '/' . $package['name'];
+                        } else {
+                            $name = '__uri/' . $package['name'] . ' (static URI)';
+                        }
+                    }
+
+                    $info[$infoindex] .= "$type $name";
+                    if (isset($package['uri'])) {
+                        $info[$infoindex] .= "\n  Download URI: $package[uri]";
+                        continue;
+                    }
+
+                    if (isset($package['max']) && isset($package['min'])) {
+                        $info[$infoindex] .= " \n  Versions " .
+                            $package['min'] . '-' . $package['max'];
+                    } elseif (isset($package['min'])) {
+                        $info[$infoindex] .= " \n  Version " .
+                            $package['min'] . ' or newer';
+                    } elseif (isset($package['max'])) {
+                        $info[$infoindex] .= " \n  Version " .
+                            $package['max'] . ' or older';
+                    }
+
+                    if (isset($package['recommended'])) {
+                        $info[$infoindex] .= "\n  Recommended version: $package[recommended]";
+                    }
+
+                    if (isset($package['exclude'])) {
+                        if (!isset($info['Not Compatible with'])) {
+                            $info['Not Compatible with'] = '';
+                        } else {
+                            $info['Not Compatible with'] .= "\n";
+                        }
+
+                        if (is_array($package['exclude'])) {
+                            $package['exclude'] = implode(', ', $package['exclude']);
+                        }
+
+                        $package['package'] = $package['name']; // for parsedPackageNameToString
+                         if (isset($package['conflicts'])) {
+                            $info['Not Compatible with'] .= '=> except ';
+                        }
+                       $info['Not Compatible with'] .= 'Package ' .
+                            $reg->parsedPackageNameToString($package, true);
+                        $info['Not Compatible with'] .= "\n  Versions " . $package['exclude'];
+                    }
+                }
+            }
+        }
+
+        if (isset($deps['required']['os'])) {
+            if (isset($deps['required']['os']['name'])) {
+                $dep['required']['os']['name'] = array($dep['required']['os']['name']);
+            }
+
+            foreach ($dep['required']['os'] as $os) {
+                if (isset($os['conflicts']) && $os['conflicts'] == 'yes') {
+                    if (!isset($info['Not Compatible with'])) {
+                        $info['Not Compatible with'] = '';
+                    } else {
+                        $info['Not Compatible with'] .= "\n";
+                    }
+                    $info['Not Compatible with'] .= "$os[name] Operating System";
+                } else {
+                    $info['Required Dependencies'] .= "\n";
+                    $info['Required Dependencies'] .= "$os[name] Operating System";
+                }
+            }
+        }
+
+        if (isset($deps['required']['arch'])) {
+            if (isset($deps['required']['arch']['pattern'])) {
+                $dep['required']['arch']['pattern'] = array($dep['required']['os']['pattern']);
+            }
+
+            foreach ($dep['required']['arch'] as $os) {
+                if (isset($os['conflicts']) && $os['conflicts'] == 'yes') {
+                    if (!isset($info['Not Compatible with'])) {
+                        $info['Not Compatible with'] = '';
+                    } else {
+                        $info['Not Compatible with'] .= "\n";
+                    }
+                    $info['Not Compatible with'] .= "OS/Arch matching pattern '/$os[pattern]/'";
+                } else {
+                    $info['Required Dependencies'] .= "\n";
+                    $info['Required Dependencies'] .= "OS/Arch matching pattern '/$os[pattern]/'";
+                }
+            }
+        }
+
+        if (isset($deps['optional'])) {
+            foreach (array('Package', 'Extension') as $type) {
+                $index = strtolower($type);
+                if (isset($deps['optional'][$index])) {
+                    if (isset($deps['optional'][$index]['name'])) {
+                        $deps['optional'][$index] = array($deps['optional'][$index]);
+                    }
+
+                    foreach ($deps['optional'][$index] as $package) {
+                        if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
+                            $infoindex = 'Not Compatible with';
+                            if (!isset($info['Not Compatible with'])) {
+                                $info['Not Compatible with'] = '';
+                            } else {
+                                $info['Not Compatible with'] .= "\n";
+                            }
+                        } else {
+                            $infoindex = 'Optional Dependencies';
+                            if (!isset($info['Optional Dependencies'])) {
+                                $info['Optional Dependencies'] = '';
+                            } else {
+                                $info['Optional Dependencies'] .= "\n";
+                            }
+                        }
+
+                        if ($index == 'extension') {
+                            $name = $package['name'];
+                        } else {
+                            if (isset($package['channel'])) {
+                                $name = $package['channel'] . '/' . $package['name'];
+                            } else {
+                                $name = '__uri/' . $package['name'] . ' (static URI)';
+                            }
+                        }
+
+                        $info[$infoindex] .= "$type $name";
+                        if (isset($package['uri'])) {
+                            $info[$infoindex] .= "\n  Download URI: $package[uri]";
+                            continue;
+                        }
+
+                        if ($infoindex == 'Not Compatible with') {
+                            // conflicts is only used to say that all versions conflict
+                            continue;
+                        }
+
+                        if (isset($package['max']) && isset($package['min'])) {
+                            $info[$infoindex] .= " \n  Versions " .
+                                $package['min'] . '-' . $package['max'];
+                        } elseif (isset($package['min'])) {
+                            $info[$infoindex] .= " \n  Version " .
+                                $package['min'] . ' or newer';
+                        } elseif (isset($package['max'])) {
+                            $info[$infoindex] .= " \n  Version " .
+                                $package['min'] . ' or older';
+                        }
+
+                        if (isset($package['recommended'])) {
+                            $info[$infoindex] .= "\n  Recommended version: $package[recommended]";
+                        }
+
+                        if (isset($package['exclude'])) {
+                            if (!isset($info['Not Compatible with'])) {
+                                $info['Not Compatible with'] = '';
+                            } else {
+                                $info['Not Compatible with'] .= "\n";
+                            }
+
+                            if (is_array($package['exclude'])) {
+                                $package['exclude'] = implode(', ', $package['exclude']);
+                            }
+
+                            $info['Not Compatible with'] .= "Package $package\n  Versions " .
+                                $package['exclude'];
+                        }
+                    }
+                }
+            }
+        }
+
+        if (isset($deps['group'])) {
+            if (!isset($deps['group'][0])) {
+                $deps['group'] = array($deps['group']);
+            }
+
+            foreach ($deps['group'] as $group) {
+                $info['Dependency Group ' . $group['attribs']['name']] = $group['attribs']['hint'];
+                $groupindex = $group['attribs']['name'] . ' Contents';
+                $info[$groupindex] = '';
+                foreach (array('Package', 'Extension') as $type) {
+                    $index = strtolower($type);
+                    if (isset($group[$index])) {
+                        if (isset($group[$index]['name'])) {
+                            $group[$index] = array($group[$index]);
+                        }
+
+                        foreach ($group[$index] as $package) {
+                            if (!empty($info[$groupindex])) {
+                                $info[$groupindex] .= "\n";
+                            }
+
+                            if ($index == 'extension') {
+                                $name = $package['name'];
+                            } else {
+                                if (isset($package['channel'])) {
+                                    $name = $package['channel'] . '/' . $package['name'];
+                                } else {
+                                    $name = '__uri/' . $package['name'] . ' (static URI)';
+                                }
+                            }
+
+                            if (isset($package['uri'])) {
+                                if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
+                                    $info[$groupindex] .= "Not Compatible with $type $name";
+                                } else {
+                                    $info[$groupindex] .= "$type $name";
+                                }
+
+                                $info[$groupindex] .= "\n  Download URI: $package[uri]";
+                                continue;
+                            }
+
+                            if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
+                                $info[$groupindex] .= "Not Compatible with $type $name";
+                                continue;
+                            }
+
+                            $info[$groupindex] .= "$type $name";
+                            if (isset($package['max']) && isset($package['min'])) {
+                                $info[$groupindex] .= " \n  Versions " .
+                                    $package['min'] . '-' . $package['max'];
+                            } elseif (isset($package['min'])) {
+                                $info[$groupindex] .= " \n  Version " .
+                                    $package['min'] . ' or newer';
+                            } elseif (isset($package['max'])) {
+                                $info[$groupindex] .= " \n  Version " .
+                                    $package['min'] . ' or older';
+                            }
+
+                            if (isset($package['recommended'])) {
+                                $info[$groupindex] .= "\n  Recommended version: $package[recommended]";
+                            }
+
+                            if (isset($package['exclude'])) {
+                                if (!isset($info['Not Compatible with'])) {
+                                    $info['Not Compatible with'] = '';
+                                } else {
+                                    $info[$groupindex] .= "Not Compatible with\n";
+                                }
+
+                                if (is_array($package['exclude'])) {
+                                    $package['exclude'] = implode(', ', $package['exclude']);
+                                }
+                                $info[$groupindex] .= "  Package $package\n  Versions " .
+                                    $package['exclude'];
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if ($obj->getPackageType() == 'bundle') {
+            $info['Bundled Packages'] = '';
+            foreach ($obj->getBundledPackages() as $package) {
+                if (!empty($info['Bundled Packages'])) {
+                    $info['Bundled Packages'] .= "\n";
+                }
+
+                if (isset($package['uri'])) {
+                    $info['Bundled Packages'] .= '__uri/' . $package['name'];
+                    $info['Bundled Packages'] .= "\n  (URI: $package[uri]";
+                } else {
+                    $info['Bundled Packages'] .= $package['channel'] . '/' . $package['name'];
+                }
+            }
+        }
+
+        $info['package.xml version'] = '2.0';
+        if ($installed) {
+            if ($obj->getLastModified()) {
+                $info['Last Modified'] = date('Y-m-d H:i', $obj->getLastModified());
+            }
+
+            $v = $obj->getLastInstalledVersion();
+            $info['Previous Installed Version'] = $v ? $v : '- None -';
+        }
+
+        foreach ($info as $key => $value) {
+            $data['data'][] = array($key, $value);
+        }
+
+        $data['raw'] = $obj->getArray(); // no validation needed
+        $this->ui->outputData($data, 'package-info');
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Build.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Build.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Build.xml	(revision 5146)
@@ -0,0 +1,10 @@
+<commands version="1.0">
+ <build>
+  <summary>Build an Extension From C Source</summary>
+  <function>doBuild</function>
+  <shortcut>b</shortcut>
+  <options />
+  <doc>[package.xml]
+Builds one or more extensions contained in a package.</doc>
+ </build>
+</commands>
Index: trunk/library/PEAR/PEAR/Command/Auth.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Auth.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Auth.xml	(revision 5146)
@@ -0,0 +1,30 @@
+<commands version="1.0">
+ <login>
+  <summary>Connects and authenticates to remote server [Deprecated in favor of channel-login]</summary>
+  <function>doLogin</function>
+  <shortcut>li</shortcut>
+  <options />
+  <doc>&lt;channel name&gt;
+WARNING: This function is deprecated in favor of using channel-login
+
+Log in to a remote channel server.  If &lt;channel name&gt; is not supplied,
+the default channel is used. To use remote functions in the installer
+that require any kind of privileges, you need to log in first.  The
+username and password you enter here will be stored in your per-user
+PEAR configuration (~/.pearrc on Unix-like systems).  After logging
+in, your username and password will be sent along in subsequent
+operations on the remote server.</doc>
+ </login>
+ <logout>
+  <summary>Logs out from the remote server [Deprecated in favor of channel-logout]</summary>
+  <function>doLogout</function>
+  <shortcut>lo</shortcut>
+  <options />
+  <doc>
+WARNING: This function is deprecated in favor of using channel-logout
+
+Logs out from the remote server.  This command does not actually
+connect to the remote server, it only deletes the stored username and
+password from your user configuration.</doc>
+ </logout>
+</commands>
Index: trunk/library/PEAR/PEAR/Command/Mirror.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Mirror.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Mirror.php	(revision 5146)
@@ -0,0 +1,139 @@
+<?php
+/**
+ * PEAR_Command_Mirror (download-all command)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Alexander Merz <alexmerz@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Mirror.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.2.0
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Common.php';
+
+/**
+ * PEAR commands for providing file mirrors
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Alexander Merz <alexmerz@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.2.0
+ */
+class PEAR_Command_Mirror extends PEAR_Command_Common
+{
+    var $commands = array(
+        'download-all' => array(
+            'summary' => 'Downloads each available package from the default channel',
+            'function' => 'doDownloadAll',
+            'shortcut' => 'da',
+            'options' => array(
+                'channel' =>
+                    array(
+                    'shortopt' => 'c',
+                    'doc' => 'specify a channel other than the default channel',
+                    'arg' => 'CHAN',
+                    ),
+                ),
+            'doc' => '
+Requests a list of available packages from the default channel ({config default_channel})
+and downloads them to current working directory.  Note: only
+packages within preferred_state ({config preferred_state}) will be downloaded'
+            ),
+        );
+
+    /**
+     * PEAR_Command_Mirror constructor.
+     *
+     * @access public
+     * @param object PEAR_Frontend a reference to an frontend
+     * @param object PEAR_Config a reference to the configuration data
+     */
+    function PEAR_Command_Mirror(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    /**
+     * For unit-testing
+     */
+    function &factory($a)
+    {
+        $a = &PEAR_Command::factory($a, $this->config);
+        return $a;
+    }
+
+    /**
+    * retrieves a list of avaible Packages from master server
+    * and downloads them
+    *
+    * @access public
+    * @param string $command the command
+    * @param array $options the command options before the command
+    * @param array $params the stuff after the command name
+    * @return bool true if succesful
+    * @throw PEAR_Error
+    */
+    function doDownloadAll($command, $options, $params)
+    {
+        $savechannel = $this->config->get('default_channel');
+        $reg = &$this->config->getRegistry();
+        $channel = isset($options['channel']) ? $options['channel'] :
+            $this->config->get('default_channel');
+        if (!$reg->channelExists($channel)) {
+            $this->config->set('default_channel', $savechannel);
+            return $this->raiseError('Channel "' . $channel . '" does not exist');
+        }
+        $this->config->set('default_channel', $channel);
+
+        $this->ui->outputData('Using Channel ' . $this->config->get('default_channel'));
+        $chan = $reg->getChannel($channel);
+        if (PEAR::isError($chan)) {
+            return $this->raiseError($chan);
+        }
+
+        if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
+              $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
+            $rest = &$this->config->getREST('1.0', array());
+            $remoteInfo = array_flip($rest->listPackages($base, $channel));
+        }
+
+        if (PEAR::isError($remoteInfo)) {
+            return $remoteInfo;
+        }
+
+        $cmd = &$this->factory("download");
+        if (PEAR::isError($cmd)) {
+            return $cmd;
+        }
+
+        $this->ui->outputData('Using Preferred State of ' .
+            $this->config->get('preferred_state'));
+        $this->ui->outputData('Gathering release information, please wait...');
+
+        /**
+         * Error handling not necessary, because already done by
+         * the download command
+         */
+        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+        $err = $cmd->run('download', array('downloadonly' => true), array_keys($remoteInfo));
+        PEAR::staticPopErrorHandling();
+        $this->config->set('default_channel', $savechannel);
+        if (PEAR::isError($err)) {
+            $this->ui->outputData($err->getMessage());
+        }
+
+        return true;
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Config.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Config.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Config.xml	(revision 5146)
@@ -0,0 +1,92 @@
+<commands version="1.0">
+ <config-show>
+  <summary>Show All Settings</summary>
+  <function>doConfigShow</function>
+  <shortcut>csh</shortcut>
+  <options>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>show configuration variables for another channel</doc>
+    <arg>CHAN</arg>
+   </channel>
+  </options>
+  <doc>[layer]
+Displays all configuration values.  An optional argument
+may be used to tell which configuration layer to display.  Valid
+configuration layers are &quot;user&quot;, &quot;system&quot; and &quot;default&quot;. To display
+configurations for different channels, set the default_channel
+configuration variable and run config-show again.
+</doc>
+ </config-show>
+ <config-get>
+  <summary>Show One Setting</summary>
+  <function>doConfigGet</function>
+  <shortcut>cg</shortcut>
+  <options>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>show configuration variables for another channel</doc>
+    <arg>CHAN</arg>
+   </channel>
+  </options>
+  <doc>&lt;parameter&gt; [layer]
+Displays the value of one configuration parameter.  The
+first argument is the name of the parameter, an optional second argument
+may be used to tell which configuration layer to look in.  Valid configuration
+layers are &quot;user&quot;, &quot;system&quot; and &quot;default&quot;.  If no layer is specified, a value
+will be picked from the first layer that defines the parameter, in the order
+just specified.  The configuration value will be retrieved for the channel
+specified by the default_channel configuration variable.
+</doc>
+ </config-get>
+ <config-set>
+  <summary>Change Setting</summary>
+  <function>doConfigSet</function>
+  <shortcut>cs</shortcut>
+  <options>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>show configuration variables for another channel</doc>
+    <arg>CHAN</arg>
+   </channel>
+  </options>
+  <doc>&lt;parameter&gt; &lt;value&gt; [layer]
+Sets the value of one configuration parameter.  The first argument is
+the name of the parameter, the second argument is the new value.  Some
+parameters are subject to validation, and the command will fail with
+an error message if the new value does not make sense.  An optional
+third argument may be used to specify in which layer to set the
+configuration parameter.  The default layer is &quot;user&quot;.  The
+configuration value will be set for the current channel, which
+is controlled by the default_channel configuration variable.
+</doc>
+ </config-set>
+ <config-help>
+  <summary>Show Information About Setting</summary>
+  <function>doConfigHelp</function>
+  <shortcut>ch</shortcut>
+  <options />
+  <doc>[parameter]
+Displays help for a configuration parameter.  Without arguments it
+displays help for all configuration parameters.
+</doc>
+ </config-help>
+ <config-create>
+  <summary>Create a Default configuration file</summary>
+  <function>doConfigCreate</function>
+  <shortcut>coc</shortcut>
+  <options>
+   <windows>
+    <shortopt>w</shortopt>
+    <doc>create a config file for a windows install</doc>
+   </windows>
+  </options>
+  <doc>&lt;root path&gt; &lt;filename&gt;
+Create a default configuration file with all directory configuration
+variables set to subdirectories of &lt;root path&gt;, and save it as &lt;filename&gt;.
+This is useful especially for creating a configuration file for a remote
+PEAR installation (using the --remoteconfig option of install, upgrade,
+and uninstall).
+</doc>
+ </config-create>
+</commands>
Index: trunk/library/PEAR/PEAR/Command/Install.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Install.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Install.xml	(revision 5146)
@@ -0,0 +1,276 @@
+<commands version="1.0">
+ <install>
+  <summary>Install Package</summary>
+  <function>doInstall</function>
+  <shortcut>i</shortcut>
+  <options>
+   <force>
+    <shortopt>f</shortopt>
+    <doc>will overwrite newer installed packages</doc>
+   </force>
+   <loose>
+    <shortopt>l</shortopt>
+    <doc>do not check for recommended dependency version</doc>
+   </loose>
+   <nodeps>
+    <shortopt>n</shortopt>
+    <doc>ignore dependencies, install anyway</doc>
+   </nodeps>
+   <register-only>
+    <shortopt>r</shortopt>
+    <doc>do not install files, only register the package as installed</doc>
+   </register-only>
+   <soft>
+    <shortopt>s</shortopt>
+    <doc>soft install, fail silently, or upgrade if already installed</doc>
+   </soft>
+   <nobuild>
+    <shortopt>B</shortopt>
+    <doc>don&#039;t build C extensions</doc>
+   </nobuild>
+   <nocompress>
+    <shortopt>Z</shortopt>
+    <doc>request uncompressed files when downloading</doc>
+   </nocompress>
+   <installroot>
+    <shortopt>R</shortopt>
+    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT), use packagingroot for RPM</doc>
+    <arg>DIR</arg>
+   </installroot>
+   <packagingroot>
+    <shortopt>P</shortopt>
+    <doc>root directory used when packaging files, like RPM packaging</doc>
+    <arg>DIR</arg>
+   </packagingroot>
+   <ignore-errors>
+    <shortopt></shortopt>
+    <doc>force install even if there were errors</doc>
+   </ignore-errors>
+   <alldeps>
+    <shortopt>a</shortopt>
+    <doc>install all required and optional dependencies</doc>
+   </alldeps>
+   <onlyreqdeps>
+    <shortopt>o</shortopt>
+    <doc>install all required dependencies</doc>
+   </onlyreqdeps>
+   <offline>
+    <shortopt>O</shortopt>
+    <doc>do not attempt to download any urls or contact channels</doc>
+   </offline>
+   <pretend>
+    <shortopt>p</shortopt>
+    <doc>Only list the packages that would be downloaded</doc>
+   </pretend>
+  </options>
+  <doc>[channel/]&lt;package&gt; ...
+Installs one or more PEAR packages.  You can specify a package to
+install in four ways:
+
+&quot;Package-1.0.tgz&quot; : installs from a local file
+
+&quot;http://example.com/Package-1.0.tgz&quot; : installs from
+anywhere on the net.
+
+&quot;package.xml&quot; : installs the package described in
+package.xml.  Useful for testing, or for wrapping a PEAR package in
+another package manager such as RPM.
+
+&quot;Package[-version/state][.tar]&quot; : queries your default channel&#039;s server
+({config master_server}) and downloads the newest package with
+the preferred quality/state ({config preferred_state}).
+
+To retrieve Package version 1.1, use &quot;Package-1.1,&quot; to retrieve
+Package state beta, use &quot;Package-beta.&quot;  To retrieve an uncompressed
+file, append .tar (make sure there is no file by the same name first)
+
+To download a package from another channel, prefix with the channel name like
+&quot;channel/Package&quot;
+
+More than one package may be specified at once.  It is ok to mix these
+four ways of specifying packages.
+</doc>
+ </install>
+ <upgrade>
+  <summary>Upgrade Package</summary>
+  <function>doInstall</function>
+  <shortcut>up</shortcut>
+  <options>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>upgrade packages from a specific channel</doc>
+    <arg>CHAN</arg>
+   </channel>
+   <force>
+    <shortopt>f</shortopt>
+    <doc>overwrite newer installed packages</doc>
+   </force>
+   <loose>
+    <shortopt>l</shortopt>
+    <doc>do not check for recommended dependency version</doc>
+   </loose>
+   <nodeps>
+    <shortopt>n</shortopt>
+    <doc>ignore dependencies, upgrade anyway</doc>
+   </nodeps>
+   <register-only>
+    <shortopt>r</shortopt>
+    <doc>do not install files, only register the package as upgraded</doc>
+   </register-only>
+   <nobuild>
+    <shortopt>B</shortopt>
+    <doc>don&#039;t build C extensions</doc>
+   </nobuild>
+   <nocompress>
+    <shortopt>Z</shortopt>
+    <doc>request uncompressed files when downloading</doc>
+   </nocompress>
+   <installroot>
+    <shortopt>R</shortopt>
+    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT)</doc>
+    <arg>DIR</arg>
+   </installroot>
+   <ignore-errors>
+    <shortopt></shortopt>
+    <doc>force install even if there were errors</doc>
+   </ignore-errors>
+   <alldeps>
+    <shortopt>a</shortopt>
+    <doc>install all required and optional dependencies</doc>
+   </alldeps>
+   <onlyreqdeps>
+    <shortopt>o</shortopt>
+    <doc>install all required dependencies</doc>
+   </onlyreqdeps>
+   <offline>
+    <shortopt>O</shortopt>
+    <doc>do not attempt to download any urls or contact channels</doc>
+   </offline>
+   <pretend>
+    <shortopt>p</shortopt>
+    <doc>Only list the packages that would be downloaded</doc>
+   </pretend>
+  </options>
+  <doc>&lt;package&gt; ...
+Upgrades one or more PEAR packages.  See documentation for the
+&quot;install&quot; command for ways to specify a package.
+
+When upgrading, your package will be updated if the provided new
+package has a higher version number (use the -f option if you need to
+upgrade anyway).
+
+More than one package may be specified at once.
+</doc>
+ </upgrade>
+ <upgrade-all>
+  <summary>Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]</summary>
+  <function>doUpgradeAll</function>
+  <shortcut>ua</shortcut>
+  <options>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>upgrade packages from a specific channel</doc>
+    <arg>CHAN</arg>
+   </channel>
+   <nodeps>
+    <shortopt>n</shortopt>
+    <doc>ignore dependencies, upgrade anyway</doc>
+   </nodeps>
+   <register-only>
+    <shortopt>r</shortopt>
+    <doc>do not install files, only register the package as upgraded</doc>
+   </register-only>
+   <nobuild>
+    <shortopt>B</shortopt>
+    <doc>don&#039;t build C extensions</doc>
+   </nobuild>
+   <nocompress>
+    <shortopt>Z</shortopt>
+    <doc>request uncompressed files when downloading</doc>
+   </nocompress>
+   <installroot>
+    <shortopt>R</shortopt>
+    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT), use packagingroot for RPM</doc>
+    <arg>DIR</arg>
+   </installroot>
+   <ignore-errors>
+    <shortopt></shortopt>
+    <doc>force install even if there were errors</doc>
+   </ignore-errors>
+   <loose>
+    <shortopt></shortopt>
+    <doc>do not check for recommended dependency version</doc>
+   </loose>
+  </options>
+  <doc>
+WARNING: This function is deprecated in favor of using the upgrade command with no params
+
+Upgrades all packages that have a newer release available.  Upgrades are
+done only if there is a release available of the state specified in
+&quot;preferred_state&quot; (currently {config preferred_state}), or a state considered
+more stable.
+</doc>
+ </upgrade-all>
+ <uninstall>
+  <summary>Un-install Package</summary>
+  <function>doUninstall</function>
+  <shortcut>un</shortcut>
+  <options>
+   <nodeps>
+    <shortopt>n</shortopt>
+    <doc>ignore dependencies, uninstall anyway</doc>
+   </nodeps>
+   <register-only>
+    <shortopt>r</shortopt>
+    <doc>do not remove files, only register the packages as not installed</doc>
+   </register-only>
+   <installroot>
+    <shortopt>R</shortopt>
+    <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT)</doc>
+    <arg>DIR</arg>
+   </installroot>
+   <ignore-errors>
+    <shortopt></shortopt>
+    <doc>force install even if there were errors</doc>
+   </ignore-errors>
+   <offline>
+    <shortopt>O</shortopt>
+    <doc>do not attempt to uninstall remotely</doc>
+   </offline>
+  </options>
+  <doc>[channel/]&lt;package&gt; ...
+Uninstalls one or more PEAR packages.  More than one package may be
+specified at once.  Prefix with channel name to uninstall from a
+channel not in your default channel ({config default_channel})
+</doc>
+ </uninstall>
+ <bundle>
+  <summary>Unpacks a Pecl Package</summary>
+  <function>doBundle</function>
+  <shortcut>bun</shortcut>
+  <options>
+   <destination>
+    <shortopt>d</shortopt>
+    <doc>Optional destination directory for unpacking (defaults to current path or &quot;ext&quot; if exists)</doc>
+    <arg>DIR</arg>
+   </destination>
+   <force>
+    <shortopt>f</shortopt>
+    <doc>Force the unpacking even if there were errors in the package</doc>
+   </force>
+  </options>
+  <doc>&lt;package&gt;
+Unpacks a Pecl Package into the selected location. It will download the
+package if needed.
+</doc>
+ </bundle>
+ <run-scripts>
+  <summary>Run Post-Install Scripts bundled with a package</summary>
+  <function>doRunScripts</function>
+  <shortcut>rs</shortcut>
+  <options />
+  <doc>&lt;package&gt;
+Run post-installation scripts in package &lt;package&gt;, if any exist.
+</doc>
+ </run-scripts>
+</commands>
Index: trunk/library/PEAR/PEAR/Command/Test.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Test.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Test.php	(revision 5146)
@@ -0,0 +1,337 @@
+<?php
+/**
+ * PEAR_Command_Test (run-tests)
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Test.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR/Command/Common.php';
+
+/**
+ * PEAR commands for login/logout
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+
+class PEAR_Command_Test extends PEAR_Command_Common
+{
+    var $commands = array(
+        'run-tests' => array(
+            'summary' => 'Run Regression Tests',
+            'function' => 'doRunTests',
+            'shortcut' => 'rt',
+            'options' => array(
+                'recur' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'Run tests in child directories, recursively.  4 dirs deep maximum',
+                ),
+                'ini' => array(
+                    'shortopt' => 'i',
+                    'doc' => 'actual string of settings to pass to php in format " -d setting=blah"',
+                    'arg' => 'SETTINGS'
+                ),
+                'realtimelog' => array(
+                    'shortopt' => 'l',
+                    'doc' => 'Log test runs/results as they are run',
+                ),
+                'quiet' => array(
+                    'shortopt' => 'q',
+                    'doc' => 'Only display detail for failed tests',
+                ),
+                'simple' => array(
+                    'shortopt' => 's',
+                    'doc' => 'Display simple output for all tests',
+                ),
+                'package' => array(
+                    'shortopt' => 'p',
+                    'doc' => 'Treat parameters as installed packages from which to run tests',
+                ),
+                'phpunit' => array(
+                    'shortopt' => 'u',
+                    'doc' => 'Search parameters for AllTests.php, and use that to run phpunit-based tests
+If none is found, all .phpt tests will be tried instead.',
+                ),
+                'tapoutput' => array(
+                    'shortopt' => 't',
+                    'doc' => 'Output run-tests.log in TAP-compliant format',
+                ),
+                'cgi' => array(
+                    'shortopt' => 'c',
+                    'doc' => 'CGI php executable (needed for tests with POST/GET section)',
+                    'arg' => 'PHPCGI',
+                ),
+                'coverage' => array(
+                    'shortopt' => 'x',
+                    'doc'      => 'Generate a code coverage report (requires Xdebug 2.0.0+)',
+                ),
+            ),
+            'doc' => '[testfile|dir ...]
+Run regression tests with PHP\'s regression testing script (run-tests.php).',
+            ),
+        );
+
+    var $output;
+
+    /**
+     * PEAR_Command_Test constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Test(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    function doRunTests($command, $options, $params)
+    {
+        if (isset($options['phpunit']) && isset($options['tapoutput'])) {
+            return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time');
+        }
+
+        require_once 'PEAR/Common.php';
+        require_once 'System.php';
+        $log = new PEAR_Common;
+        $log->ui = &$this->ui; // slightly hacky, but it will work
+        $tests = array();
+        $depth = isset($options['recur']) ? 14 : 1;
+
+        if (!count($params)) {
+            $params[] = '.';
+        }
+
+        if (isset($options['package'])) {
+            $oldparams = $params;
+            $params = array();
+            $reg = &$this->config->getRegistry();
+            foreach ($oldparams as $param) {
+                $pname = $reg->parsePackageName($param, $this->config->get('default_channel'));
+                if (PEAR::isError($pname)) {
+                    return $this->raiseError($pname);
+                }
+
+                $package = &$reg->getPackage($pname['package'], $pname['channel']);
+                if (!$package) {
+                    return PEAR::raiseError('Unknown package "' .
+                        $reg->parsedPackageNameToString($pname) . '"');
+                }
+
+                $filelist = $package->getFilelist();
+                foreach ($filelist as $name => $atts) {
+                    if (isset($atts['role']) && $atts['role'] != 'test') {
+                        continue;
+                    }
+
+                    if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) {
+                        $params[] = $atts['installed_as'];
+                        continue;
+                    } elseif (!preg_match('/\.phpt\\z/', $name)) {
+                        continue;
+                    }
+                    $params[] = $atts['installed_as'];
+                }
+            }
+        }
+
+        foreach ($params as $p) {
+            if (is_dir($p)) {
+                if (isset($options['phpunit'])) {
+                    $dir = System::find(array($p, '-type', 'f',
+                                                '-maxdepth', $depth,
+                                                '-name', 'AllTests.php'));
+                    if (count($dir)) {
+                        foreach ($dir as $p) {
+                            $p = realpath($p);
+                            if (!count($tests) ||
+                                  (count($tests) && strlen($p) < strlen($tests[0]))) {
+                                // this is in a higher-level directory, use this one instead.
+                                $tests = array($p);
+                            }
+                        }
+                    }
+                    continue;
+                }
+
+                $args  = array($p, '-type', 'f', '-name', '*.phpt');
+            } else {
+                if (isset($options['phpunit'])) {
+                    if (preg_match('/AllTests\.php\\z/i', $p)) {
+                        $p = realpath($p);
+                        if (!count($tests) ||
+                              (count($tests) && strlen($p) < strlen($tests[0]))) {
+                            // this is in a higher-level directory, use this one instead.
+                            $tests = array($p);
+                        }
+                    }
+                    continue;
+                }
+
+                if (file_exists($p) && preg_match('/\.phpt$/', $p)) {
+                    $tests[] = $p;
+                    continue;
+                }
+
+                if (!preg_match('/\.phpt\\z/', $p)) {
+                    $p .= '.phpt';
+                }
+
+                $args  = array(dirname($p), '-type', 'f', '-name', $p);
+            }
+
+            if (!isset($options['recur'])) {
+                $args[] = '-maxdepth';
+                $args[] = 1;
+            }
+
+            $dir   = System::find($args);
+            $tests = array_merge($tests, $dir);
+        }
+
+        $ini_settings = '';
+        if (isset($options['ini'])) {
+            $ini_settings .= $options['ini'];
+        }
+
+        if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) {
+            $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}";
+        }
+
+        if ($ini_settings) {
+            $this->ui->outputData('Using INI settings: "' . $ini_settings . '"');
+        }
+
+        $skipped = $passed = $failed = array();
+        $tests_count = count($tests);
+        $this->ui->outputData('Running ' . $tests_count . ' tests', $command);
+        $start = time();
+        if (isset($options['realtimelog']) && file_exists('run-tests.log')) {
+            unlink('run-tests.log');
+        }
+
+        if (isset($options['tapoutput'])) {
+            $tap = '1..' . $tests_count . "\n";
+        }
+
+        require_once 'PEAR/RunTest.php';
+        $run = new PEAR_RunTest($log, $options);
+        $run->tests_count = $tests_count;
+
+        if (isset($options['coverage']) && extension_loaded('xdebug')){
+            $run->xdebug_loaded = true;
+        } else {
+            $run->xdebug_loaded = false;
+        }
+
+        $j = $i = 1;
+        foreach ($tests as $t) {
+            if (isset($options['realtimelog'])) {
+                $fp = @fopen('run-tests.log', 'a');
+                if ($fp) {
+                    fwrite($fp, "Running test [$i / $tests_count] $t...");
+                    fclose($fp);
+                }
+            }
+            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
+            if (isset($options['phpunit'])) {
+                $result = $run->runPHPUnit($t, $ini_settings);
+            } else {
+                $result = $run->run($t, $ini_settings, $j);
+            }
+            PEAR::staticPopErrorHandling();
+            if (PEAR::isError($result)) {
+                $this->ui->log($result->getMessage());
+                continue;
+            }
+
+            if (isset($options['tapoutput'])) {
+                $tap .= $result[0] . ' ' . $i . $result[1] . "\n";
+                continue;
+            }
+
+            if (isset($options['realtimelog'])) {
+                $fp = @fopen('run-tests.log', 'a');
+                if ($fp) {
+                    fwrite($fp, "$result\n");
+                    fclose($fp);
+                }
+            }
+
+            if ($result == 'FAILED') {
+                $failed[] = $t;
+            }
+            if ($result == 'PASSED') {
+                $passed[] = $t;
+            }
+            if ($result == 'SKIPPED') {
+                $skipped[] = $t;
+            }
+
+            $j++;
+        }
+
+        $total = date('i:s', time() - $start);
+        if (isset($options['tapoutput'])) {
+            $fp = @fopen('run-tests.log', 'w');
+            if ($fp) {
+                fwrite($fp, $tap, strlen($tap));
+                fclose($fp);
+                $this->ui->outputData('wrote TAP-format log to "' .realpath('run-tests.log') .
+                    '"', $command);
+            }
+        } else {
+            if (count($failed)) {
+                $output = "TOTAL TIME: $total\n";
+                $output .= count($passed) . " PASSED TESTS\n";
+                $output .= count($skipped) . " SKIPPED TESTS\n";
+                $output .= count($failed) . " FAILED TESTS:\n";
+                foreach ($failed as $failure) {
+                    $output .= $failure . "\n";
+                }
+
+                $mode = isset($options['realtimelog']) ? 'a' : 'w';
+                $fp   = @fopen('run-tests.log', $mode);
+
+                if ($fp) {
+                    fwrite($fp, $output, strlen($output));
+                    fclose($fp);
+                    $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command);
+                }
+            } elseif (file_exists('run-tests.log') && !is_dir('run-tests.log')) {
+                @unlink('run-tests.log');
+            }
+        }
+        $this->ui->outputData('TOTAL TIME: ' . $total);
+        $this->ui->outputData(count($passed) . ' PASSED TESTS', $command);
+        $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command);
+        if (count($failed)) {
+            $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command);
+            foreach ($failed as $failure) {
+                $this->ui->outputData($failure, $command);
+            }
+        }
+
+        return true;
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Pickle.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Pickle.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Pickle.xml	(revision 5146)
@@ -0,0 +1,36 @@
+<commands version="1.0">
+ <pickle>
+  <summary>Build PECL Package</summary>
+  <function>doPackage</function>
+  <shortcut>pi</shortcut>
+  <options>
+   <nocompress>
+    <shortopt>Z</shortopt>
+    <doc>Do not gzip the package file</doc>
+   </nocompress>
+   <showname>
+    <shortopt>n</shortopt>
+    <doc>Print the name of the packaged file.</doc>
+   </showname>
+  </options>
+  <doc>[descfile]
+Creates a PECL package from its package2.xml file.
+
+An automatic conversion will be made to a package.xml 1.0 and written out to
+disk in the current directory as &quot;package.xml&quot;.  Note that
+only simple package.xml 2.0 will be converted.  package.xml 2.0 with:
+
+ - dependency types other than required/optional PECL package/ext/php/pearinstaller
+ - more than one extsrcrelease or zendextsrcrelease
+ - zendextbinrelease, extbinrelease, phprelease, or bundle release type
+ - dependency groups
+ - ignore tags in release filelist
+ - tasks other than replace
+ - custom roles
+
+will cause pickle to fail, and output an error message.  If your package2.xml
+uses any of these features, you are best off using PEAR_PackageFileManager to
+generate both package.xml.
+</doc>
+ </pickle>
+</commands>
Index: trunk/library/PEAR/PEAR/Command/Registry.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Registry.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Registry.xml	(revision 5146)
@@ -0,0 +1,58 @@
+<commands version="1.0">
+ <list>
+  <summary>List Installed Packages In The Default Channel</summary>
+  <function>doList</function>
+  <shortcut>l</shortcut>
+  <options>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>list installed packages from this channel</doc>
+    <arg>CHAN</arg>
+   </channel>
+   <allchannels>
+    <shortopt>a</shortopt>
+    <doc>list installed packages from all channels</doc>
+   </allchannels>
+   <channelinfo>
+    <shortopt>i</shortopt>
+    <doc>output fully channel-aware data, even on failure</doc>
+   </channelinfo>
+  </options>
+  <doc>&lt;package&gt;
+If invoked without parameters, this command lists the PEAR packages
+installed in your php_dir ({config php_dir}).  With a parameter, it
+lists the files in a package.
+</doc>
+ </list>
+ <list-files>
+  <summary>List Files In Installed Package</summary>
+  <function>doFileList</function>
+  <shortcut>fl</shortcut>
+  <options />
+  <doc>&lt;package&gt;
+List the files in an installed package.
+</doc>
+ </list-files>
+ <shell-test>
+  <summary>Shell Script Test</summary>
+  <function>doShellTest</function>
+  <shortcut>st</shortcut>
+  <options />
+  <doc>&lt;package&gt; [[relation] version]
+Tests if a package is installed in the system. Will exit(1) if it is not.
+   &lt;relation&gt;   The version comparison operator. One of:
+                &lt;, lt, &lt;=, le, &gt;, gt, &gt;=, ge, ==, =, eq, !=, &lt;&gt;, ne
+   &lt;version&gt;    The version to compare with
+</doc>
+ </shell-test>
+ <info>
+  <summary>Display information about a package</summary>
+  <function>doInfo</function>
+  <shortcut>in</shortcut>
+  <options />
+  <doc>&lt;package&gt;
+Displays information about a package. The package argument may be a
+local package file, an URL to a package file, or the name of an
+installed package.</doc>
+ </info>
+</commands>
Index: trunk/library/PEAR/PEAR/Command/Mirror.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Mirror.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Mirror.xml	(revision 5146)
@@ -0,0 +1,18 @@
+<commands version="1.0">
+ <download-all>
+  <summary>Downloads each available package from the default channel</summary>
+  <function>doDownloadAll</function>
+  <shortcut>da</shortcut>
+  <options>
+   <channel>
+    <shortopt>c</shortopt>
+    <doc>specify a channel other than the default channel</doc>
+    <arg>CHAN</arg>
+   </channel>
+  </options>
+  <doc>
+Requests a list of available packages from the default channel ({config default_channel})
+and downloads them to current working directory.  Note: only
+packages within preferred_state ({config preferred_state}) will be downloaded</doc>
+ </download-all>
+</commands>
Index: trunk/library/PEAR/PEAR/Command/Common.php
===================================================================
--- trunk/library/PEAR/PEAR/Command/Common.php	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Common.php	(revision 5146)
@@ -0,0 +1,273 @@
+<?php
+/**
+ * PEAR_Command_Common base class
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR.php';
+
+/**
+ * PEAR commands base class
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 0.1
+ */
+class PEAR_Command_Common extends PEAR
+{
+    /**
+     * PEAR_Config object used to pass user system and configuration
+     * on when executing commands
+     *
+     * @var PEAR_Config
+     */
+    var $config;
+    /**
+     * @var PEAR_Registry
+     * @access protected
+     */
+    var $_registry;
+
+    /**
+     * User Interface object, for all interaction with the user.
+     * @var object
+     */
+    var $ui;
+
+    var $_deps_rel_trans = array(
+                                 'lt' => '<',
+                                 'le' => '<=',
+                                 'eq' => '=',
+                                 'ne' => '!=',
+                                 'gt' => '>',
+                                 'ge' => '>=',
+                                 'has' => '=='
+                                 );
+
+    var $_deps_type_trans = array(
+                                  'pkg' => 'package',
+                                  'ext' => 'extension',
+                                  'php' => 'PHP',
+                                  'prog' => 'external program',
+                                  'ldlib' => 'external library for linking',
+                                  'rtlib' => 'external runtime library',
+                                  'os' => 'operating system',
+                                  'websrv' => 'web server',
+                                  'sapi' => 'SAPI backend'
+                                  );
+
+    /**
+     * PEAR_Command_Common constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Common(&$ui, &$config)
+    {
+        parent::PEAR();
+        $this->config = &$config;
+        $this->ui = &$ui;
+    }
+
+    /**
+     * Return a list of all the commands defined by this class.
+     * @return array list of commands
+     * @access public
+     */
+    function getCommands()
+    {
+        $ret = array();
+        foreach (array_keys($this->commands) as $command) {
+            $ret[$command] = $this->commands[$command]['summary'];
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Return a list of all the command shortcuts defined by this class.
+     * @return array shortcut => command
+     * @access public
+     */
+    function getShortcuts()
+    {
+        $ret = array();
+        foreach (array_keys($this->commands) as $command) {
+            if (isset($this->commands[$command]['shortcut'])) {
+                $ret[$this->commands[$command]['shortcut']] = $command;
+            }
+        }
+
+        return $ret;
+    }
+
+    function getOptions($command)
+    {
+        $shortcuts = $this->getShortcuts();
+        if (isset($shortcuts[$command])) {
+            $command = $shortcuts[$command];
+        }
+
+        if (isset($this->commands[$command]) &&
+              isset($this->commands[$command]['options'])) {
+            return $this->commands[$command]['options'];
+        }
+
+        return null;
+    }
+
+    function getGetoptArgs($command, &$short_args, &$long_args)
+    {
+        $short_args = '';
+        $long_args = array();
+        if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) {
+            return;
+        }
+
+        reset($this->commands[$command]['options']);
+        while (list($option, $info) = each($this->commands[$command]['options'])) {
+            $larg = $sarg = '';
+            if (isset($info['arg'])) {
+                if ($info['arg']{0} == '(') {
+                    $larg = '==';
+                    $sarg = '::';
+                    $arg = substr($info['arg'], 1, -1);
+                } else {
+                    $larg = '=';
+                    $sarg = ':';
+                    $arg = $info['arg'];
+                }
+            }
+
+            if (isset($info['shortopt'])) {
+                $short_args .= $info['shortopt'] . $sarg;
+            }
+
+            $long_args[] = $option . $larg;
+        }
+    }
+
+    /**
+    * Returns the help message for the given command
+    *
+    * @param string $command The command
+    * @return mixed A fail string if the command does not have help or
+    *               a two elements array containing [0]=>help string,
+    *               [1]=> help string for the accepted cmd args
+    */
+    function getHelp($command)
+    {
+        $config = &PEAR_Config::singleton();
+        if (!isset($this->commands[$command])) {
+            return "No such command \"$command\"";
+        }
+
+        $help = null;
+        if (isset($this->commands[$command]['doc'])) {
+            $help = $this->commands[$command]['doc'];
+        }
+
+        if (empty($help)) {
+            // XXX (cox) Fallback to summary if there is no doc (show both?)
+            if (!isset($this->commands[$command]['summary'])) {
+                return "No help for command \"$command\"";
+            }
+            $help = $this->commands[$command]['summary'];
+        }
+
+        if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) {
+            foreach($matches[0] as $k => $v) {
+                $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help);
+            }
+        }
+
+        return array($help, $this->getHelpArgs($command));
+    }
+
+    /**
+     * Returns the help for the accepted arguments of a command
+     *
+     * @param  string $command
+     * @return string The help string
+     */
+    function getHelpArgs($command)
+    {
+        if (isset($this->commands[$command]['options']) &&
+            count($this->commands[$command]['options']))
+        {
+            $help = "Options:\n";
+            foreach ($this->commands[$command]['options'] as $k => $v) {
+                if (isset($v['arg'])) {
+                    if ($v['arg'][0] == '(') {
+                        $arg = substr($v['arg'], 1, -1);
+                        $sapp = " [$arg]";
+                        $lapp = "[=$arg]";
+                    } else {
+                        $sapp = " $v[arg]";
+                        $lapp = "=$v[arg]";
+                    }
+                } else {
+                    $sapp = $lapp = "";
+                }
+
+                if (isset($v['shortopt'])) {
+                    $s = $v['shortopt'];
+                    $help .= "  -$s$sapp, --$k$lapp\n";
+                } else {
+                    $help .= "  --$k$lapp\n";
+                }
+
+                $p = "        ";
+                $doc = rtrim(str_replace("\n", "\n$p", $v['doc']));
+                $help .= "        $doc\n";
+            }
+
+            return $help;
+        }
+
+        return null;
+    }
+
+    function run($command, $options, $params)
+    {
+        if (empty($this->commands[$command]['function'])) {
+            // look for shortcuts
+            foreach (array_keys($this->commands) as $cmd) {
+                if (isset($this->commands[$cmd]['shortcut']) && $this->commands[$cmd]['shortcut'] == $command) {
+                    if (empty($this->commands[$cmd]['function'])) {
+                        return $this->raiseError("unknown command `$command'");
+                    } else {
+                        $func = $this->commands[$cmd]['function'];
+                    }
+                    $command = $cmd;
+
+                    //$command = $this->commands[$cmd]['function'];
+                    break;
+                }
+            }
+        } else {
+            $func = $this->commands[$command]['function'];
+        }
+
+        return $this->$func($command, $options, $params);
+    }
+}
Index: trunk/library/PEAR/PEAR/Command/Test.xml
===================================================================
--- trunk/library/PEAR/PEAR/Command/Test.xml	(revision 5146)
+++ trunk/library/PEAR/PEAR/Command/Test.xml	(revision 5146)
@@ -0,0 +1,54 @@
+<commands version="1.0">
+ <run-tests>
+  <summary>Run Regression Tests</summary>
+  <function>doRunTests</function>
+  <shortcut>rt</shortcut>
+  <options>
+   <recur>
+    <shortopt>r</shortopt>
+    <doc>Run tests in child directories, recursively.  4 dirs deep maximum</doc>
+   </recur>
+   <ini>
+    <shortopt>i</shortopt>
+    <doc>actual string of settings to pass to php in format &quot; -d setting=blah&quot;</doc>
+    <arg>SETTINGS</arg>
+   </ini>
+   <realtimelog>
+    <shortopt>l</shortopt>
+    <doc>Log test runs/results as they are run</doc>
+   </realtimelog>
+   <quiet>
+    <shortopt>q</shortopt>
+    <doc>Only display detail for failed tests</doc>
+   </quiet>
+   <simple>
+    <shortopt>s</shortopt>
+    <doc>Display simple output for all tests</doc>
+   </simple>
+   <package>
+    <shortopt>p</shortopt>
+    <doc>Treat parameters as installed packages from which to run tests</doc>
+   </package>
+   <phpunit>
+    <shortopt>u</shortopt>
+    <doc>Search parameters for AllTests.php, and use that to run phpunit-based tests
+If none is found, all .phpt tests will be tried instead.</doc>
+   </phpunit>
+   <tapoutput>
+    <shortopt>t</shortopt>
+    <doc>Output run-tests.log in TAP-compliant format</doc>
+   </tapoutput>
+   <cgi>
+    <shortopt>c</shortopt>
+    <doc>CGI php executable (needed for tests with POST/GET section)</doc>
+    <arg>PHPCGI</arg>
+   </cgi>
+   <coverage>
+    <shortopt>x</shortopt>
+    <doc>Generate a code coverage report (requires Xdebug 2.0.0+)</doc>
+   </coverage>
+  </options>
+  <doc>[testfile|dir ...]
+Run regression tests with PHP&#039;s regression testing script (run-tests.php).</doc>
+ </run-tests>
+</commands>
Index: trunk/library/PEAR/scripts/peardev.sh
===================================================================
--- trunk/library/PEAR/scripts/peardev.sh	(revision 5146)
+++ trunk/library/PEAR/scripts/peardev.sh	(revision 5146)
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# first find which PHP binary to use
+if test "x$PHP_PEAR_PHP_BIN" != "x"; then
+  PHP="$PHP_PEAR_PHP_BIN"
+else
+  if test "@php_bin@" = '@'php_bin'@'; then
+    PHP=php
+  else
+    PHP="@php_bin@"
+  fi
+fi
+
+# then look for the right pear include dir
+if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
+  INCDIR=$PHP_PEAR_INSTALL_DIR
+  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
+else
+  if test "@php_dir@" = '@'php_dir'@'; then
+    INCDIR=`dirname $0`
+    INCARG=""
+  else
+    INCDIR="@php_dir@"
+    INCARG="-d include_path=@php_dir@"
+  fi
+fi
+
+exec $PHP -d date.timezone=UTC -d memory_limit="-1" -C -q $INCARG -d output_buffering=1 -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d variables_order=EGPCS -d auto_append_file="" $INCDIR/pearcmd.php "$@"
Index: trunk/library/PEAR/scripts/pear.sh
===================================================================
--- trunk/library/PEAR/scripts/pear.sh	(revision 5146)
+++ trunk/library/PEAR/scripts/pear.sh	(revision 5146)
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# first find which PHP binary to use
+if test "x$PHP_PEAR_PHP_BIN" != "x"; then
+  PHP="$PHP_PEAR_PHP_BIN"
+else
+  if test "@php_bin@" = '@'php_bin'@'; then
+    PHP=php
+  else
+    PHP="@php_bin@"
+  fi
+fi
+
+# then look for the right pear include dir
+if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
+  INCDIR=$PHP_PEAR_INSTALL_DIR
+  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
+else
+  if test "@php_dir@" = '@'php_dir'@'; then
+    INCDIR=`dirname $0`
+    INCARG=""
+  else
+    INCDIR="@php_dir@"
+    INCARG="-d include_path=@php_dir@"
+  fi
+fi
+
+exec $PHP -C -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" $INCDIR/pearcmd.php "$@"
Index: trunk/library/PEAR/scripts/pearcmd.php
===================================================================
--- trunk/library/PEAR/scripts/pearcmd.php	(revision 5146)
+++ trunk/library/PEAR/scripts/pearcmd.php	(revision 5146)
@@ -0,0 +1,448 @@
+<?php
+/**
+ * PEAR, the PHP Extension and Application Repository
+ *
+ * Command line interface
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: pearcmd.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ */
+
+ob_end_clean();
+if (!defined('PEAR_RUNTYPE')) {
+    // this is defined in peclcmd.php as 'pecl'
+    define('PEAR_RUNTYPE', 'pear');
+}
+define('PEAR_IGNORE_BACKTRACE', 1);
+/**
+ * @nodep Gtk
+ */
+if ('@include_path@' != '@'.'include_path'.'@') {
+    ini_set('include_path', '@include_path@');
+    $raw = false;
+} else {
+    // this is a raw, uninstalled pear, either a cvs checkout, or php distro
+    $raw = true;
+}
+@ini_set('allow_url_fopen', true);
+if (!ini_get('safe_mode')) {
+    @set_time_limit(0);
+}
+ob_implicit_flush(true);
+@ini_set('track_errors', true);
+@ini_set('html_errors', false);
+@ini_set('magic_quotes_runtime', false);
+$_PEAR_PHPDIR = '#$%^&*';
+set_error_handler('error_handler');
+
+$pear_package_version = "@pear_version@";
+
+require_once 'PEAR.php';
+require_once 'PEAR/Frontend.php';
+require_once 'PEAR/Config.php';
+require_once 'PEAR/Command.php';
+require_once 'Console/Getopt.php';
+
+
+PEAR_Command::setFrontendType('CLI');
+$all_commands = PEAR_Command::getCommands();
+
+// remove this next part when we stop supporting that crap-ass PHP 4.2
+if (!isset($_SERVER['argv']) && !isset($argv) && !isset($HTTP_SERVER_VARS['argv'])) {
+    echo 'ERROR: either use the CLI php executable, or set register_argc_argv=On in php.ini';
+    exit(1);
+}
+
+$argv = Console_Getopt::readPHPArgv();
+// fix CGI sapi oddity - the -- in pear.bat/pear is not removed
+if (php_sapi_name() != 'cli' && isset($argv[1]) && $argv[1] == '--') {
+    unset($argv[1]);
+    $argv = array_values($argv);
+}
+$progname = PEAR_RUNTYPE;
+array_shift($argv);
+$options = Console_Getopt::getopt2($argv, "c:C:d:D:Gh?sSqu:vV");
+if (PEAR::isError($options)) {
+    usage($options);
+}
+
+$opts = $options[0];
+
+$fetype = 'CLI';
+if ($progname == 'gpear' || $progname == 'pear-gtk') {
+    $fetype = 'Gtk';
+} else {
+    foreach ($opts as $opt) {
+        if ($opt[0] == 'G') {
+            $fetype = 'Gtk';
+        }
+    }
+}
+//Check if Gtk and PHP >= 5.1.0
+if ($fetype == 'Gtk' && version_compare(phpversion(), '5.1.0', '>=')) {
+    $fetype = 'Gtk2';
+}
+
+$pear_user_config = '';
+$pear_system_config = '';
+$store_user_config = false;
+$store_system_config = false;
+$verbose = 1;
+
+foreach ($opts as $opt) {
+    switch ($opt[0]) {
+        case 'c':
+            $pear_user_config = $opt[1];
+            break;
+        case 'C':
+            $pear_system_config = $opt[1];
+            break;
+    }
+}
+
+PEAR_Command::setFrontendType($fetype);
+$ui = &PEAR_Command::getFrontendObject();
+$config = &PEAR_Config::singleton($pear_user_config, $pear_system_config);
+
+if (PEAR::isError($config)) {
+    $_file = '';
+    if ($pear_user_config !== false) {
+       $_file .= $pear_user_config;
+    }
+    if ($pear_system_config !== false) {
+       $_file .= '/' . $pear_system_config;
+    }
+    if ($_file == '/') {
+        $_file = 'The default config file';
+    }
+    $config->getMessage();
+    $ui->outputData("ERROR: $_file is not a valid config file or is corrupted.");
+    // We stop, we have no idea where we are :)
+    exit(1);
+}
+
+// this is used in the error handler to retrieve a relative path
+$_PEAR_PHPDIR = $config->get('php_dir');
+$ui->setConfig($config);
+PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($ui, "displayFatalError"));
+if (ini_get('safe_mode')) {
+    $ui->outputData('WARNING: running in safe mode requires that all files created ' .
+        'be the same uid as the current script.  PHP reports this script is uid: ' .
+        @getmyuid() . ', and current user is: ' . @get_current_user());
+}
+
+$verbose = $config->get("verbose");
+$cmdopts = array();
+
+if ($raw) {
+    if (!$config->isDefinedLayer('user') && !$config->isDefinedLayer('system')) {
+        $found = false;
+        foreach ($opts as $opt) {
+            if ($opt[0] == 'd' || $opt[0] == 'D') {
+                $found = true; // the user knows what they are doing, and are setting config values
+            }
+        }
+        if (!$found) {
+            // no prior runs, try to install PEAR
+            if (strpos(dirname(__FILE__), 'scripts')) {
+                $packagexml = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'package2.xml';
+                $pearbase = dirname(dirname(__FILE__));
+            } else {
+                $packagexml = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'package2.xml';
+                $pearbase = dirname(__FILE__);
+            }
+            if (file_exists($packagexml)) {
+                $options[1] = array(
+                    'install',
+                    $packagexml
+                );
+                $config->set('php_dir', $pearbase . DIRECTORY_SEPARATOR . 'php');
+                $config->set('data_dir', $pearbase . DIRECTORY_SEPARATOR . 'data');
+                $config->set('doc_dir', $pearbase . DIRECTORY_SEPARATOR . 'docs');
+                $config->set('test_dir', $pearbase . DIRECTORY_SEPARATOR . 'tests');
+                $config->set('ext_dir', $pearbase . DIRECTORY_SEPARATOR . 'extensions');
+                $config->set('bin_dir', $pearbase);
+                $config->mergeConfigFile($pearbase . 'pear.ini', false);
+                $config->store();
+                $config->set('auto_discover', 1);
+            }
+        }
+    }
+}
+foreach ($opts as $opt) {
+    $param = !empty($opt[1]) ? $opt[1] : true;
+    switch ($opt[0]) {
+        case 'd':
+            if ($param === true) {
+                die('Invalid usage of "-d" option, expected -d config_value=value, ' .
+                    'received "-d"' . "\n");
+            }
+            $possible = explode('=', $param);
+            if (count($possible) != 2) {
+                die('Invalid usage of "-d" option, expected -d config_value=value, received "' .
+                    $param . '"' . "\n");
+            }
+            list($key, $value) = explode('=', $param);
+            $config->set($key, $value, 'user');
+            break;
+        case 'D':
+            if ($param === true) {
+                die('Invalid usage of "-d" option, expected -d config_value=value, ' .
+                    'received "-d"' . "\n");
+            }
+            $possible = explode('=', $param);
+            if (count($possible) != 2) {
+                die('Invalid usage of "-d" option, expected -d config_value=value, received "' .
+                    $param . '"' . "\n");
+            }
+            list($key, $value) = explode('=', $param);
+            $config->set($key, $value, 'system');
+            break;
+        case 's':
+            $store_user_config = true;
+            break;
+        case 'S':
+            $store_system_config = true;
+            break;
+        case 'u':
+            $config->remove($param, 'user');
+            break;
+        case 'v':
+            $config->set('verbose', $config->get('verbose') + 1);
+            break;
+        case 'q':
+            $config->set('verbose', $config->get('verbose') - 1);
+            break;
+        case 'V':
+            usage(null, 'version');
+        case 'c':
+        case 'C':
+            break;
+        default:
+            // all non pear params goes to the command
+            $cmdopts[$opt[0]] = $param;
+            break;
+    }
+}
+
+if ($store_system_config) {
+    $config->store('system');
+}
+
+if ($store_user_config) {
+    $config->store('user');
+}
+
+$command = (isset($options[1][0])) ? $options[1][0] : null;
+if (empty($command) && ($store_user_config || $store_system_config)) {
+    exit;
+}
+
+if ($fetype == 'Gtk' || $fetype == 'Gtk2') {
+    if (!$config->validConfiguration()) {
+        PEAR::raiseError('CRITICAL ERROR: no existing valid configuration files found in files ' .
+            "'$pear_user_config' or '$pear_system_config', please copy an existing configuration" .
+            'file to one of these locations, or use the -c and -s options to create one');
+    }
+    Gtk::main();
+} else do {
+    if ($command == 'help') {
+        usage(null, @$options[1][1]);
+    }
+
+    if (!$config->validConfiguration()) {
+        PEAR::raiseError('CRITICAL ERROR: no existing valid configuration files found in files ' .
+            "'$pear_user_config' or '$pear_system_config', please copy an existing configuration" .
+            'file to one of these locations, or use the -c and -s options to create one');
+    }
+
+    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+    $cmd = PEAR_Command::factory($command, $config);
+    PEAR::popErrorHandling();
+    if (PEAR::isError($cmd)) {
+        usage(null, @$options[1][0]);
+    }
+
+    $short_args = $long_args = null;
+    PEAR_Command::getGetoptArgs($command, $short_args, $long_args);
+    array_shift($options[1]);
+    $tmp = Console_Getopt::getopt2($options[1], $short_args, $long_args);
+
+    if (PEAR::isError($tmp)) {
+        break;
+    }
+
+    list($tmpopt, $params) = $tmp;
+    $opts = array();
+    foreach ($tmpopt as $foo => $tmp2) {
+        list($opt, $value) = $tmp2;
+        if ($value === null) {
+            $value = true; // options without args
+        }
+
+        if (strlen($opt) == 1) {
+            $cmdoptions = $cmd->getOptions($command);
+            foreach ($cmdoptions as $o => $d) {
+                if (isset($d['shortopt']) && $d['shortopt'] == $opt) {
+                    $opts[$o] = $value;
+                }
+            }
+        } else {
+            if (substr($opt, 0, 2) == '--') {
+                $opts[substr($opt, 2)] = $value;
+            }
+        }
+    }
+
+    $ok = $cmd->run($command, $opts, $params);
+    if ($ok === false) {
+        PEAR::raiseError("unknown command `$command'");
+    }
+
+    if (PEAR::isError($ok)) {
+        PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($ui, "displayFatalError"));
+        PEAR::raiseError($ok);
+    }
+} while (false);
+
+// {{{ usage()
+
+function usage($error = null, $helpsubject = null)
+{
+    global $progname, $all_commands;
+    $stdout = fopen('php://stdout', 'w');
+    if (PEAR::isError($error)) {
+        fputs($stdout, $error->getMessage() . "\n");
+    } elseif ($error !== null) {
+        fputs($stdout, "$error\n");
+    }
+
+    if ($helpsubject != null) {
+        $put = cmdHelp($helpsubject);
+    } else {
+        $put = "Commands:\n";
+        $maxlen = max(array_map("strlen", $all_commands));
+        $formatstr = "%-{$maxlen}s  %s\n";
+        ksort($all_commands);
+        foreach ($all_commands as $cmd => $class) {
+            $put .= sprintf($formatstr, $cmd, PEAR_Command::getDescription($cmd));
+        }
+        $put .=
+            "Usage: $progname [options] command [command-options] <parameters>\n".
+            "Type \"$progname help options\" to list all options.\n".
+            "Type \"$progname help shortcuts\" to list all command shortcuts.\n".
+            "Type \"$progname help <command>\" to get the help for the specified command.";
+    }
+    fputs($stdout, "$put\n");
+    fclose($stdout);
+
+    if ($error === null) {
+        exit(0);
+    }
+    exit(1);
+}
+
+function cmdHelp($command)
+{
+    global $progname, $all_commands, $config;
+    if ($command == "options") {
+        return
+        "Options:\n".
+        "     -v         increase verbosity level (default 1)\n".
+        "     -q         be quiet, decrease verbosity level\n".
+        "     -c file    find user configuration in `file'\n".
+        "     -C file    find system configuration in `file'\n".
+        "     -d foo=bar set user config variable `foo' to `bar'\n".
+        "     -D foo=bar set system config variable `foo' to `bar'\n".
+        "     -G         start in graphical (Gtk) mode\n".
+        "     -s         store user configuration\n".
+        "     -S         store system configuration\n".
+        "     -u foo     unset `foo' in the user configuration\n".
+        "     -h, -?     display help/usage (this message)\n".
+        "     -V         version information\n";
+    } elseif ($command == "shortcuts") {
+        $sc = PEAR_Command::getShortcuts();
+        $ret = "Shortcuts:\n";
+        foreach ($sc as $s => $c) {
+            $ret .= sprintf("     %-8s %s\n", $s, $c);
+        }
+        return $ret;
+
+    } elseif ($command == "version") {
+        return "PEAR Version: ".$GLOBALS['pear_package_version'].
+               "\nPHP Version: ".phpversion().
+               "\nZend Engine Version: ".zend_version().
+               "\nRunning on: ".php_uname();
+
+    } elseif ($help = PEAR_Command::getHelp($command)) {
+        if (is_string($help)) {
+            return "$progname $command [options] $help\n";
+        }
+
+        if ($help[1] === null) {
+            return "$progname $command $help[0]";
+        }
+
+        return "$progname $command [options] $help[0]\n$help[1]";
+    }
+
+    return "Command '$command' is not valid, try '$progname help'";
+}
+
+// }}}
+
+function error_handler($errno, $errmsg, $file, $line, $vars) {
+    if ((defined('E_STRICT') && $errno & E_STRICT) || (defined('E_DEPRECATED') &&
+          $errno & E_DEPRECATED) || !error_reporting()) {
+        if (defined('E_STRICT') && $errno & E_STRICT) {
+            return; // E_STRICT
+        }
+        if (defined('E_DEPRECATED') && $errno & E_DEPRECATED) {
+            return; // E_DEPRECATED
+        }
+        if ($GLOBALS['config']->get('verbose') < 4) {
+            return false; // @silenced error, show all if debug is high enough
+        }
+    }
+    $errortype = array (
+        E_ERROR   =>  "Error",
+        E_WARNING   =>  "Warning",
+        E_PARSE   =>  "Parsing Error",
+        E_NOTICE   =>  "Notice",
+        E_CORE_ERROR  =>  "Core Error",
+        E_CORE_WARNING  =>  "Core Warning",
+        E_COMPILE_ERROR  =>  "Compile Error",
+        E_COMPILE_WARNING =>  "Compile Warning",
+        E_USER_ERROR =>  "User Error",
+        E_USER_WARNING =>  "User Warning",
+        E_USER_NOTICE =>  "User Notice"
+    );
+    $prefix = $errortype[$errno];
+    global $_PEAR_PHPDIR;
+    if (stristr($file, $_PEAR_PHPDIR)) {
+        $file = substr($file, strlen($_PEAR_PHPDIR) + 1);
+    } else {
+        $file = basename($file);
+    }
+    print "\n$prefix: $errmsg in $file on line $line\n";
+    return false;
+}
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * mode: php
+ * End:
+ */
+// vim600:syn=php
Index: trunk/library/PEAR/scripts/pecl.bat
===================================================================
--- trunk/library/PEAR/scripts/pecl.bat	(revision 5146)
+++ trunk/library/PEAR/scripts/pecl.bat	(revision 5146)
@@ -0,0 +1,115 @@
+@ECHO OFF
+
+REM ----------------------------------------------------------------------
+REM PHP version 5
+REM ----------------------------------------------------------------------
+REM Copyright (c) 1997-2004 The PHP Group
+REM ----------------------------------------------------------------------
+REM  This source file is subject to version 3.0 of the PHP license,
+REM  that is bundled with this package in the file LICENSE, and is
+REM  available at through the world-wide-web at
+REM  http://www.php.net/license/3_0.txt.
+REM  If you did not receive a copy of the PHP license and are unable to
+REM  obtain it through the world-wide-web, please send a note to
+REM  license@php.net so we can mail you a copy immediately.
+REM ----------------------------------------------------------------------
+REM  Authors:     Alexander Merz (alexmerz@php.net)
+REM ----------------------------------------------------------------------
+REM
+REM  Last updated 02/08/2004 ($Id$ is not replaced if the file is binary)
+
+REM change this lines to match the paths of your system
+REM -------------------
+
+
+REM Test to see if this is a raw pear.bat (uninstalled version)
+SET TMPTMPTMPTMPT=@includ
+SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
+FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)
+
+REM Check PEAR global ENV, set them if they do not exist
+IF "%PHP_PEAR_INSTALL_DIR%"=="" SET "PHP_PEAR_INSTALL_DIR=@include_path@"
+IF "%PHP_PEAR_BIN_DIR%"=="" SET "PHP_PEAR_BIN_DIR=@bin_dir@"
+IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=@php_bin@"
+GOTO :INSTALLED
+
+:NOTINSTALLED
+ECHO WARNING: This is a raw, uninstalled pear.bat
+
+REM Check to see if we can grab the directory of this file (Windows NT+)
+IF %~n0 == pear (
+FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
+SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
+echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
+"%PHP_PEAR_PHP_BIN%" -v
+GOTO :NEXTTEST
+))
+GOTO :FAILAUTODETECT
+:NEXTTEST
+IF "%PHP_PEAR_PHP_BIN%" NEQ "" (
+
+REM We can use this PHP to run a temporary php file to get the dirname of pear
+
+echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
+"%PHP_PEAR_PHP_BIN%" ~~getloc.php
+set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
+DEL ~a.a
+DEL ~~getloc.php
+set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"
+
+REM Make sure there is a pearcmd.php at our disposal
+
+IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
+IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
+IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
+IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
+)
+)
+GOTO :INSTALLED
+) ELSE (
+REM Windows Me/98 cannot succeed, so allow the batch to fail
+)
+:FAILAUTODETECT
+echo WARNING: failed to auto-detect pear information
+:INSTALLED
+
+REM Check Folders and files
+IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%" GOTO PEAR_INSTALL_ERROR
+IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" GOTO PEAR_INSTALL_ERROR2
+IF NOT EXIST "%PHP_PEAR_BIN_DIR%" GOTO PEAR_BIN_ERROR
+IF NOT EXIST "%PHP_PEAR_PHP_BIN%" GOTO PEAR_PHPBIN_ERROR
+REM launch pearcmd
+GOTO RUN
+:PEAR_INSTALL_ERROR
+ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_INSTALL_DIR%
+GOTO END
+:PEAR_INSTALL_ERROR2
+ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
+ECHO pearcmd.php could not be found there.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_INSTALL_DIR%
+GOTO END
+:PEAR_BIN_ERROR
+ECHO PHP_PEAR_BIN_DIR is not set correctly.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_BIN_DIR%
+GOTO END
+:PEAR_PHPBIN_ERROR
+ECHO PHP_PEAR_PHP_BIN is not set correctly.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_PHP_BIN%
+GOTO END
+:RUN
+"%PHP_PEAR_PHP_BIN%" -C -n -d date.timezone=UTC -d output_buffering=1 -d safe_mode=0 -d "include_path='%PHP_PEAR_INSTALL_DIR%'" -d register_argc_argv="On" -d variables_order=EGPCS -f "%PHP_PEAR_INSTALL_DIR%\peclcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
+:END
+@ECHO ON
Index: trunk/library/PEAR/scripts/peardev.bat
===================================================================
--- trunk/library/PEAR/scripts/peardev.bat	(revision 5146)
+++ trunk/library/PEAR/scripts/peardev.bat	(revision 5146)
@@ -0,0 +1,115 @@
+@ECHO OFF
+
+REM ----------------------------------------------------------------------
+REM PHP version 5
+REM ----------------------------------------------------------------------
+REM Copyright (c) 1997-2004 The PHP Group
+REM ----------------------------------------------------------------------
+REM  This source file is subject to version 3.0 of the PHP license,
+REM  that is bundled with this package in the file LICENSE, and is
+REM  available at through the world-wide-web at
+REM  http://www.php.net/license/3_0.txt.
+REM  If you did not receive a copy of the PHP license and are unable to
+REM  obtain it through the world-wide-web, please send a note to
+REM  license@php.net so we can mail you a copy immediately.
+REM ----------------------------------------------------------------------
+REM  Authors:     Alexander Merz (alexmerz@php.net)
+REM ----------------------------------------------------------------------
+REM
+REM  $Id: peardev.bat,v 1.6 2007-09-03 03:00:17 cellog Exp $
+
+REM change this lines to match the paths of your system
+REM -------------------
+
+
+REM Test to see if this is a raw pear.bat (uninstalled version)
+SET TMPTMPTMPTMPT=@includ
+SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
+FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)
+
+REM Check PEAR global ENV, set them if they do not exist
+IF "%PHP_PEAR_INSTALL_DIR%"=="" SET "PHP_PEAR_INSTALL_DIR=@include_path@"
+IF "%PHP_PEAR_BIN_DIR%"=="" SET "PHP_PEAR_BIN_DIR=@bin_dir@"
+IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=@php_bin@"
+GOTO :INSTALLED
+
+:NOTINSTALLED
+ECHO WARNING: This is a raw, uninstalled pear.bat
+
+REM Check to see if we can grab the directory of this file (Windows NT+)
+IF %~n0 == pear (
+FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
+SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
+echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
+"%PHP_PEAR_PHP_BIN%" -v
+GOTO :NEXTTEST
+))
+GOTO :FAILAUTODETECT
+:NEXTTEST
+IF "%PHP_PEAR_PHP_BIN%" NEQ "" (
+
+REM We can use this PHP to run a temporary php file to get the dirname of pear
+
+echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
+"%PHP_PEAR_PHP_BIN%" ~~getloc.php
+set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
+DEL ~a.a
+DEL ~~getloc.php
+set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"
+
+REM Make sure there is a pearcmd.php at our disposal
+
+IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
+IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
+IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
+IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
+)
+)
+GOTO :INSTALLED
+) ELSE (
+REM Windows Me/98 cannot succeed, so allow the batch to fail
+)
+:FAILAUTODETECT
+echo WARNING: failed to auto-detect pear information
+:INSTALLED
+
+REM Check Folders and files
+IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%" GOTO PEAR_INSTALL_ERROR
+IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" GOTO PEAR_INSTALL_ERROR2
+IF NOT EXIST "%PHP_PEAR_BIN_DIR%" GOTO PEAR_BIN_ERROR
+IF NOT EXIST "%PHP_PEAR_PHP_BIN%" GOTO PEAR_PHPBIN_ERROR
+REM launch pearcmd
+GOTO RUN
+:PEAR_INSTALL_ERROR
+ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_INSTALL_DIR%
+GOTO END
+:PEAR_INSTALL_ERROR2
+ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
+ECHO pearcmd.php could not be found there.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_INSTALL_DIR%
+GOTO END
+:PEAR_BIN_ERROR
+ECHO PHP_PEAR_BIN_DIR is not set correctly.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_BIN_DIR%
+GOTO END
+:PEAR_PHPBIN_ERROR
+ECHO PHP_PEAR_PHP_BIN is not set correctly.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_PHP_BIN%
+GOTO END
+:RUN
+"%PHP_PEAR_PHP_BIN%" -C -d date.timezone=UTC -d memory_limit="-1" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" -d variables_order=EGPCS -d open_basedir="" -d output_buffering=1 -d "include_path='%PHP_PEAR_INSTALL_DIR%'" -f "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
+:END
+@ECHO ON
Index: trunk/library/PEAR/scripts/pear.bat
===================================================================
--- trunk/library/PEAR/scripts/pear.bat	(revision 5146)
+++ trunk/library/PEAR/scripts/pear.bat	(revision 5146)
@@ -0,0 +1,111 @@
+@ECHO OFF
+
+REM ----------------------------------------------------------------------
+REM PHP version 5
+REM ----------------------------------------------------------------------
+REM Copyright (c) 1997-2010 The Authors
+REM ----------------------------------------------------------------------
+REM http://opensource.org/licenses/bsd-license.php New BSD License
+REM ----------------------------------------------------------------------
+REM  Authors:     Alexander Merz (alexmerz@php.net)
+REM ----------------------------------------------------------------------
+REM
+REM  Last updated 12/29/2004 ($Id$ is not replaced if the file is binary)
+
+REM change this lines to match the paths of your system
+REM -------------------
+
+
+REM Test to see if this is a raw pear.bat (uninstalled version)
+SET TMPTMPTMPTMPT=@includ
+SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
+FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)
+
+REM Check PEAR global ENV, set them if they do not exist
+IF "%PHP_PEAR_INSTALL_DIR%"=="" SET "PHP_PEAR_INSTALL_DIR=@include_path@"
+IF "%PHP_PEAR_BIN_DIR%"=="" SET "PHP_PEAR_BIN_DIR=@bin_dir@"
+IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=@php_bin@"
+
+GOTO :INSTALLED
+
+:NOTINSTALLED
+ECHO WARNING: This is a raw, uninstalled pear.bat
+
+REM Check to see if we can grab the directory of this file (Windows NT+)
+IF %~n0 == pear (
+FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
+SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
+echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
+"%PHP_PEAR_PHP_BIN%" -v
+GOTO :NEXTTEST
+))
+GOTO :FAILAUTODETECT
+:NEXTTEST
+IF "%PHP_PEAR_PHP_BIN%" NEQ "" (
+
+REM We can use this PHP to run a temporary php file to get the dirname of pear
+
+echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
+"%PHP_PEAR_PHP_BIN%" ~~getloc.php
+set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
+DEL ~a.a
+DEL ~~getloc.php
+set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"
+
+REM Make sure there is a pearcmd.php at our disposal
+
+IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
+IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
+IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
+IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
+)
+)
+GOTO :INSTALLED
+) ELSE (
+REM Windows Me/98 cannot succeed, so allow the batch to fail
+)
+:FAILAUTODETECT
+echo WARNING: failed to auto-detect pear information
+:INSTALLED
+
+REM Check Folders and files
+IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%" GOTO PEAR_INSTALL_ERROR
+IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" GOTO PEAR_INSTALL_ERROR2
+IF NOT EXIST "%PHP_PEAR_BIN_DIR%" GOTO PEAR_BIN_ERROR
+IF NOT EXIST "%PHP_PEAR_PHP_BIN%" GOTO PEAR_PHPBIN_ERROR
+
+REM launch pearcmd
+GOTO RUN
+:PEAR_INSTALL_ERROR
+ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_INSTALL_DIR%
+GOTO END
+:PEAR_INSTALL_ERROR2
+ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
+ECHO pearcmd.php could not be found there.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_INSTALL_DIR%
+GOTO END
+:PEAR_BIN_ERROR
+ECHO PHP_PEAR_BIN_DIR is not set correctly.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_BIN_DIR%
+GOTO END
+:PEAR_PHPBIN_ERROR
+ECHO PHP_PEAR_PHP_BIN is not set correctly.
+ECHO Please fix it using your environment variable or modify
+ECHO the default value in pear.bat
+ECHO The current value is:
+ECHO %PHP_PEAR_PHP_BIN%
+GOTO END
+:RUN
+"%PHP_PEAR_PHP_BIN%" -C -d date.timezone=UTC -d output_buffering=1 -d safe_mode=0 -d open_basedir="" -d auto_prepend_file="" -d auto_append_file="" -d variables_order=EGPCS -d register_argc_argv="On" -d "include_path='%PHP_PEAR_INSTALL_DIR%'" -f "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
+:END
+@ECHO ON
Index: trunk/library/PEAR/scripts/pecl.sh
===================================================================
--- trunk/library/PEAR/scripts/pecl.sh	(revision 5146)
+++ trunk/library/PEAR/scripts/pecl.sh	(revision 5146)
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# first find which PHP binary to use
+if test "x$PHP_PEAR_PHP_BIN" != "x"; then
+  PHP="$PHP_PEAR_PHP_BIN"
+else
+  if test "@php_bin@" = '@'php_bin'@'; then
+    PHP=php
+  else
+    PHP="@php_bin@"
+  fi
+fi
+
+# then look for the right pear include dir
+if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
+  INCDIR=$PHP_PEAR_INSTALL_DIR
+  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
+else
+  if test "@php_dir@" = '@'php_dir'@'; then
+    INCDIR=`dirname $0`
+    INCARG=""
+  else
+    INCDIR="@php_dir@"
+    INCARG="-d include_path=@php_dir@"
+  fi
+fi
+
+exec $PHP -C -n -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d safe_mode=0 -d register_argc_argv="On" $INCDIR/peclcmd.php "$@"
Index: trunk/library/PEAR/scripts/peclcmd.php
===================================================================
--- trunk/library/PEAR/scripts/peclcmd.php	(revision 5146)
+++ trunk/library/PEAR/scripts/peclcmd.php	(revision 5146)
@@ -0,0 +1,41 @@
+<?php
+/**
+ * PEAR, the PHP Extension and Application Repository
+ *
+ * Command line interface
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: peclcmd.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ */
+
+/**
+ * @nodep Gtk
+ */
+if ('@include_path@' != '@'.'include_path'.'@') {
+    ini_set('include_path', '@include_path@');
+    $raw = false;
+} else {
+    // this is a raw, uninstalled pear, either a cvs checkout, or php distro
+    $raw = true;
+}
+define('PEAR_RUNTYPE', 'pecl');
+require_once 'pearcmd.php';
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * mode: php
+ * End:
+ */
+// vim600:syn=php
+
+?>
Index: trunk/library/PEAR/System.php
===================================================================
--- trunk/library/PEAR/System.php	(revision 5146)
+++ trunk/library/PEAR/System.php	(revision 5146)
@@ -0,0 +1,629 @@
+<?php
+/**
+ * File/Directory manipulation
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    System
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: System.php 313024 2011-07-06 19:51:24Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**
+ * base class
+ */
+require_once 'PEAR.php';
+require_once 'Console/Getopt.php';
+
+$GLOBALS['_System_temp_files'] = array();
+
+/**
+* System offers cross plattform compatible system functions
+*
+* Static functions for different operations. Should work under
+* Unix and Windows. The names and usage has been taken from its respectively
+* GNU commands. The functions will return (bool) false on error and will
+* trigger the error with the PHP trigger_error() function (you can silence
+* the error by prefixing a '@' sign after the function call, but this
+* is not recommended practice.  Instead use an error handler with
+* {@link set_error_handler()}).
+*
+* Documentation on this class you can find in:
+* http://pear.php.net/manual/
+*
+* Example usage:
+* if (!@System::rm('-r file1 dir1')) {
+*    print "could not delete file1 or dir1";
+* }
+*
+* In case you need to to pass file names with spaces,
+* pass the params as an array:
+*
+* System::rm(array('-r', $file1, $dir1));
+*
+* @category   pear
+* @package    System
+* @author     Tomas V.V. Cox <cox@idecnet.com>
+* @copyright  1997-2006 The PHP Group
+* @license    http://opensource.org/licenses/bsd-license.php New BSD License
+* @version    Release: 1.9.4
+* @link       http://pear.php.net/package/PEAR
+* @since      Class available since Release 0.1
+* @static
+*/
+class System
+{
+    /**
+     * returns the commandline arguments of a function
+     *
+     * @param    string  $argv           the commandline
+     * @param    string  $short_options  the allowed option short-tags
+     * @param    string  $long_options   the allowed option long-tags
+     * @return   array   the given options and there values
+     * @static
+     * @access private
+     */
+    function _parseArgs($argv, $short_options, $long_options = null)
+    {
+        if (!is_array($argv) && $argv !== null) {
+            // Find all items, quoted or otherwise
+            preg_match_all("/(?:[\"'])(.*?)(?:['\"])|([^\s]+)/", $argv, $av);
+            $argv = $av[1];
+            foreach ($av[2] as $k => $a) {
+                if (empty($a)) {
+                    continue;
+                }
+                $argv[$k] = trim($a) ;
+            }
+        }
+        return Console_Getopt::getopt2($argv, $short_options, $long_options);
+    }
+
+    /**
+     * Output errors with PHP trigger_error(). You can silence the errors
+     * with prefixing a "@" sign to the function call: @System::mkdir(..);
+     *
+     * @param mixed $error a PEAR error or a string with the error message
+     * @return bool false
+     * @static
+     * @access private
+     */
+    function raiseError($error)
+    {
+        if (PEAR::isError($error)) {
+            $error = $error->getMessage();
+        }
+        trigger_error($error, E_USER_WARNING);
+        return false;
+    }
+
+    /**
+     * Creates a nested array representing the structure of a directory
+     *
+     * System::_dirToStruct('dir1', 0) =>
+     *   Array
+     *    (
+     *    [dirs] => Array
+     *        (
+     *            [0] => dir1
+     *        )
+     *
+     *    [files] => Array
+     *        (
+     *            [0] => dir1/file2
+     *            [1] => dir1/file3
+     *        )
+     *    )
+     * @param    string  $sPath      Name of the directory
+     * @param    integer $maxinst    max. deep of the lookup
+     * @param    integer $aktinst    starting deep of the lookup
+     * @param    bool    $silent     if true, do not emit errors.
+     * @return   array   the structure of the dir
+     * @static
+     * @access   private
+     */
+    function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false)
+    {
+        $struct = array('dirs' => array(), 'files' => array());
+        if (($dir = @opendir($sPath)) === false) {
+            if (!$silent) {
+                System::raiseError("Could not open dir $sPath");
+            }
+            return $struct; // XXX could not open error
+        }
+
+        $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
+        $list = array();
+        while (false !== ($file = readdir($dir))) {
+            if ($file != '.' && $file != '..') {
+                $list[] = $file;
+            }
+        }
+
+        closedir($dir);
+        natsort($list);
+        if ($aktinst < $maxinst || $maxinst == 0) {
+            foreach ($list as $val) {
+                $path = $sPath . DIRECTORY_SEPARATOR . $val;
+                if (is_dir($path) && !is_link($path)) {
+                    $tmp    = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent);
+                    $struct = array_merge_recursive($struct, $tmp);
+                } else {
+                    $struct['files'][] = $path;
+                }
+            }
+        }
+
+        return $struct;
+    }
+
+    /**
+     * Creates a nested array representing the structure of a directory and files
+     *
+     * @param    array $files Array listing files and dirs
+     * @return   array
+     * @static
+     * @see System::_dirToStruct()
+     */
+    function _multipleToStruct($files)
+    {
+        $struct = array('dirs' => array(), 'files' => array());
+        settype($files, 'array');
+        foreach ($files as $file) {
+            if (is_dir($file) && !is_link($file)) {
+                $tmp    = System::_dirToStruct($file, 0);
+                $struct = array_merge_recursive($tmp, $struct);
+            } else {
+                if (!in_array($file, $struct['files'])) {
+                    $struct['files'][] = $file;
+                }
+            }
+        }
+        return $struct;
+    }
+
+    /**
+     * The rm command for removing files.
+     * Supports multiple files and dirs and also recursive deletes
+     *
+     * @param    string  $args   the arguments for rm
+     * @return   mixed   PEAR_Error or true for success
+     * @static
+     * @access   public
+     */
+    function rm($args)
+    {
+        $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-)
+        if (PEAR::isError($opts)) {
+            return System::raiseError($opts);
+        }
+        foreach ($opts[0] as $opt) {
+            if ($opt[0] == 'r') {
+                $do_recursive = true;
+            }
+        }
+        $ret = true;
+        if (isset($do_recursive)) {
+            $struct = System::_multipleToStruct($opts[1]);
+            foreach ($struct['files'] as $file) {
+                if (!@unlink($file)) {
+                    $ret = false;
+                }
+            }
+
+            rsort($struct['dirs']);
+            foreach ($struct['dirs'] as $dir) {
+                if (!@rmdir($dir)) {
+                    $ret = false;
+                }
+            }
+        } else {
+            foreach ($opts[1] as $file) {
+                $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
+                if (!@$delete($file)) {
+                    $ret = false;
+                }
+            }
+        }
+        return $ret;
+    }
+
+    /**
+     * Make directories.
+     *
+     * The -p option will create parent directories
+     * @param    string  $args    the name of the director(y|ies) to create
+     * @return   bool    True for success
+     * @static
+     * @access   public
+     */
+    function mkDir($args)
+    {
+        $opts = System::_parseArgs($args, 'pm:');
+        if (PEAR::isError($opts)) {
+            return System::raiseError($opts);
+        }
+
+        $mode = 0777; // default mode
+        foreach ($opts[0] as $opt) {
+            if ($opt[0] == 'p') {
+                $create_parents = true;
+            } elseif ($opt[0] == 'm') {
+                // if the mode is clearly an octal number (starts with 0)
+                // convert it to decimal
+                if (strlen($opt[1]) && $opt[1]{0} == '0') {
+                    $opt[1] = octdec($opt[1]);
+                } else {
+                    // convert to int
+                    $opt[1] += 0;
+                }
+                $mode = $opt[1];
+            }
+        }
+
+        $ret = true;
+        if (isset($create_parents)) {
+            foreach ($opts[1] as $dir) {
+                $dirstack = array();
+                while ((!file_exists($dir) || !is_dir($dir)) &&
+                        $dir != DIRECTORY_SEPARATOR) {
+                    array_unshift($dirstack, $dir);
+                    $dir = dirname($dir);
+                }
+
+                while ($newdir = array_shift($dirstack)) {
+                    if (!is_writeable(dirname($newdir))) {
+                        $ret = false;
+                        break;
+                    }
+
+                    if (!mkdir($newdir, $mode)) {
+                        $ret = false;
+                    }
+                }
+            }
+        } else {
+            foreach($opts[1] as $dir) {
+                if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) {
+                    $ret = false;
+                }
+            }
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Concatenate files
+     *
+     * Usage:
+     * 1) $var = System::cat('sample.txt test.txt');
+     * 2) System::cat('sample.txt test.txt > final.txt');
+     * 3) System::cat('sample.txt test.txt >> final.txt');
+     *
+     * Note: as the class use fopen, urls should work also (test that)
+     *
+     * @param    string  $args   the arguments
+     * @return   boolean true on success
+     * @static
+     * @access   public
+     */
+    function &cat($args)
+    {
+        $ret = null;
+        $files = array();
+        if (!is_array($args)) {
+            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
+        }
+
+        $count_args = count($args);
+        for ($i = 0; $i < $count_args; $i++) {
+            if ($args[$i] == '>') {
+                $mode = 'wb';
+                $outputfile = $args[$i+1];
+                break;
+            } elseif ($args[$i] == '>>') {
+                $mode = 'ab+';
+                $outputfile = $args[$i+1];
+                break;
+            } else {
+                $files[] = $args[$i];
+            }
+        }
+        $outputfd = false;
+        if (isset($mode)) {
+            if (!$outputfd = fopen($outputfile, $mode)) {
+                $err = System::raiseError("Could not open $outputfile");
+                return $err;
+            }
+            $ret = true;
+        }
+        foreach ($files as $file) {
+            if (!$fd = fopen($file, 'r')) {
+                System::raiseError("Could not open $file");
+                continue;
+            }
+            while ($cont = fread($fd, 2048)) {
+                if (is_resource($outputfd)) {
+                    fwrite($outputfd, $cont);
+                } else {
+                    $ret .= $cont;
+                }
+            }
+            fclose($fd);
+        }
+        if (is_resource($outputfd)) {
+            fclose($outputfd);
+        }
+        return $ret;
+    }
+
+    /**
+     * Creates temporary files or directories. This function will remove
+     * the created files when the scripts finish its execution.
+     *
+     * Usage:
+     *   1) $tempfile = System::mktemp("prefix");
+     *   2) $tempdir  = System::mktemp("-d prefix");
+     *   3) $tempfile = System::mktemp();
+     *   4) $tempfile = System::mktemp("-t /var/tmp prefix");
+     *
+     * prefix -> The string that will be prepended to the temp name
+     *           (defaults to "tmp").
+     * -d     -> A temporary dir will be created instead of a file.
+     * -t     -> The target dir where the temporary (file|dir) will be created. If
+     *           this param is missing by default the env vars TMP on Windows or
+     *           TMPDIR in Unix will be used. If these vars are also missing
+     *           c:\windows\temp or /tmp will be used.
+     *
+     * @param   string  $args  The arguments
+     * @return  mixed   the full path of the created (file|dir) or false
+     * @see System::tmpdir()
+     * @static
+     * @access  public
+     */
+    function mktemp($args = null)
+    {
+        static $first_time = true;
+        $opts = System::_parseArgs($args, 't:d');
+        if (PEAR::isError($opts)) {
+            return System::raiseError($opts);
+        }
+
+        foreach ($opts[0] as $opt) {
+            if ($opt[0] == 'd') {
+                $tmp_is_dir = true;
+            } elseif ($opt[0] == 't') {
+                $tmpdir = $opt[1];
+            }
+        }
+
+        $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
+        if (!isset($tmpdir)) {
+            $tmpdir = System::tmpdir();
+        }
+
+        if (!System::mkDir(array('-p', $tmpdir))) {
+            return false;
+        }
+
+        $tmp = tempnam($tmpdir, $prefix);
+        if (isset($tmp_is_dir)) {
+            unlink($tmp); // be careful possible race condition here
+            if (!mkdir($tmp, 0700)) {
+                return System::raiseError("Unable to create temporary directory $tmpdir");
+            }
+        }
+
+        $GLOBALS['_System_temp_files'][] = $tmp;
+        if (isset($tmp_is_dir)) {
+            //$GLOBALS['_System_temp_files'][] = dirname($tmp);
+        }
+
+        if ($first_time) {
+            PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
+            $first_time = false;
+        }
+
+        return $tmp;
+    }
+
+    /**
+     * Remove temporary files created my mkTemp. This function is executed
+     * at script shutdown time
+     *
+     * @static
+     * @access private
+     */
+    function _removeTmpFiles()
+    {
+        if (count($GLOBALS['_System_temp_files'])) {
+            $delete = $GLOBALS['_System_temp_files'];
+            array_unshift($delete, '-r');
+            System::rm($delete);
+            $GLOBALS['_System_temp_files'] = array();
+        }
+    }
+
+    /**
+     * Get the path of the temporal directory set in the system
+     * by looking in its environments variables.
+     * Note: php.ini-recommended removes the "E" from the variables_order setting,
+     * making unavaible the $_ENV array, that s why we do tests with _ENV
+     *
+     * @static
+     * @return string The temporary directory on the system
+     */
+    function tmpdir()
+    {
+        if (OS_WINDOWS) {
+            if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
+                return $var;
+            }
+            if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
+                return $var;
+            }
+            if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) {
+                return $var;
+            }
+            if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
+                return $var;
+            }
+            return getenv('SystemRoot') . '\temp';
+        }
+        if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
+            return $var;
+        }
+        return realpath('/tmp');
+    }
+
+    /**
+     * The "which" command (show the full path of a command)
+     *
+     * @param string $program The command to search for
+     * @param mixed  $fallback Value to return if $program is not found
+     *
+     * @return mixed A string with the full path or false if not found
+     * @static
+     * @author Stig Bakken <ssb@php.net>
+     */
+    function which($program, $fallback = false)
+    {
+        // enforce API
+        if (!is_string($program) || '' == $program) {
+            return $fallback;
+        }
+
+        // full path given
+        if (basename($program) != $program) {
+            $path_elements[] = dirname($program);
+            $program = basename($program);
+        } else {
+            // Honor safe mode
+            if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) {
+                $path = getenv('PATH');
+                if (!$path) {
+                    $path = getenv('Path'); // some OSes are just stupid enough to do this
+                }
+            }
+            $path_elements = explode(PATH_SEPARATOR, $path);
+        }
+
+        if (OS_WINDOWS) {
+            $exe_suffixes = getenv('PATHEXT')
+                                ? explode(PATH_SEPARATOR, getenv('PATHEXT'))
+                                : array('.exe','.bat','.cmd','.com');
+            // allow passing a command.exe param
+            if (strpos($program, '.') !== false) {
+                array_unshift($exe_suffixes, '');
+            }
+            // is_executable() is not available on windows for PHP4
+            $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file';
+        } else {
+            $exe_suffixes = array('');
+            $pear_is_executable = 'is_executable';
+        }
+
+        foreach ($exe_suffixes as $suff) {
+            foreach ($path_elements as $dir) {
+                $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
+                if (@$pear_is_executable($file)) {
+                    return $file;
+                }
+            }
+        }
+        return $fallback;
+    }
+
+    /**
+     * The "find" command
+     *
+     * Usage:
+     *
+     * System::find($dir);
+     * System::find("$dir -type d");
+     * System::find("$dir -type f");
+     * System::find("$dir -name *.php");
+     * System::find("$dir -name *.php -name *.htm*");
+     * System::find("$dir -maxdepth 1");
+     *
+     * Params implmented:
+     * $dir            -> Start the search at this directory
+     * -type d         -> return only directories
+     * -type f         -> return only files
+     * -maxdepth <n>   -> max depth of recursion
+     * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
+     *
+     * @param  mixed Either array or string with the command line
+     * @return array Array of found files
+     * @static
+     *
+     */
+    function find($args)
+    {
+        if (!is_array($args)) {
+            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
+        }
+        $dir = realpath(array_shift($args));
+        if (!$dir) {
+            return array();
+        }
+        $patterns = array();
+        $depth = 0;
+        $do_files = $do_dirs = true;
+        $args_count = count($args);
+        for ($i = 0; $i < $args_count; $i++) {
+            switch ($args[$i]) {
+                case '-type':
+                    if (in_array($args[$i+1], array('d', 'f'))) {
+                        if ($args[$i+1] == 'd') {
+                            $do_files = false;
+                        } else {
+                            $do_dirs = false;
+                        }
+                    }
+                    $i++;
+                    break;
+                case '-name':
+                    $name = preg_quote($args[$i+1], '#');
+                    // our magic characters ? and * have just been escaped,
+                    // so now we change the escaped versions to PCRE operators
+                    $name = strtr($name, array('\?' => '.', '\*' => '.*'));
+                    $patterns[] = '('.$name.')';
+                    $i++;
+                    break;
+                case '-maxdepth':
+                    $depth = $args[$i+1];
+                    break;
+            }
+        }
+        $path = System::_dirToStruct($dir, $depth, 0, true);
+        if ($do_files && $do_dirs) {
+            $files = array_merge($path['files'], $path['dirs']);
+        } elseif ($do_dirs) {
+            $files = $path['dirs'];
+        } else {
+            $files = $path['files'];
+        }
+        if (count($patterns)) {
+            $dsq = preg_quote(DIRECTORY_SEPARATOR, '#');
+            $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#';
+            $ret = array();
+            $files_count = count($files);
+            for ($i = 0; $i < $files_count; $i++) {
+                // only search in the part of the file below the current directory
+                $filepart = basename($files[$i]);
+                if (preg_match($pattern, $filepart)) {
+                    $ret[] = $files[$i];
+                }
+            }
+            return $ret;
+        }
+        return $files;
+    }
+}
Index: trunk/library/PEAR/PEAR.php
===================================================================
--- trunk/library/PEAR/PEAR.php	(revision 5146)
+++ trunk/library/PEAR/PEAR.php	(revision 5146)
@@ -0,0 +1,1063 @@
+<?php
+/**
+ * PEAR, the PHP Extension and Application Repository
+ *
+ * PEAR class and PEAR_Error class
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Sterling Hughes <sterling@php.net>
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2010 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: PEAR.php 313023 2011-07-06 19:17:11Z dufuz $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 0.1
+ */
+
+/**#@+
+ * ERROR constants
+ */
+define('PEAR_ERROR_RETURN',     1);
+define('PEAR_ERROR_PRINT',      2);
+define('PEAR_ERROR_TRIGGER',    4);
+define('PEAR_ERROR_DIE',        8);
+define('PEAR_ERROR_CALLBACK',  16);
+/**
+ * WARNING: obsolete
+ * @deprecated
+ */
+define('PEAR_ERROR_EXCEPTION', 32);
+/**#@-*/
+define('PEAR_ZE2', (function_exists('version_compare') &&
+                    version_compare(zend_version(), "2-dev", "ge")));
+
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+    define('OS_WINDOWS', true);
+    define('OS_UNIX',    false);
+    define('PEAR_OS',    'Windows');
+} else {
+    define('OS_WINDOWS', false);
+    define('OS_UNIX',    true);
+    define('PEAR_OS',    'Unix'); // blatant assumption
+}
+
+$GLOBALS['_PEAR_default_error_mode']     = PEAR_ERROR_RETURN;
+$GLOBALS['_PEAR_default_error_options']  = E_USER_NOTICE;
+$GLOBALS['_PEAR_destructor_object_list'] = array();
+$GLOBALS['_PEAR_shutdown_funcs']         = array();
+$GLOBALS['_PEAR_error_handler_stack']    = array();
+
+@ini_set('track_errors', true);
+
+/**
+ * Base class for other PEAR classes.  Provides rudimentary
+ * emulation of destructors.
+ *
+ * If you want a destructor in your class, inherit PEAR and make a
+ * destructor method called _yourclassname (same name as the
+ * constructor, but with a "_" prefix).  Also, in your constructor you
+ * have to call the PEAR constructor: $this->PEAR();.
+ * The destructor method will be called without parameters.  Note that
+ * at in some SAPI implementations (such as Apache), any output during
+ * the request shutdown (in which destructors are called) seems to be
+ * discarded.  If you need to get any debug information from your
+ * destructor, use error_log(), syslog() or something similar.
+ *
+ * IMPORTANT! To use the emulated destructors you need to create the
+ * objects by reference: $obj =& new PEAR_child;
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V. Cox <cox@idecnet.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2006 The PHP Group
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/package/PEAR
+ * @see        PEAR_Error
+ * @since      Class available since PHP 4.0.2
+ * @link        http://pear.php.net/manual/en/core.pear.php#core.pear.pear
+ */
+class PEAR
+{
+    /**
+     * Whether to enable internal debug messages.
+     *
+     * @var     bool
+     * @access  private
+     */
+    var $_debug = false;
+
+    /**
+     * Default error mode for this object.
+     *
+     * @var     int
+     * @access  private
+     */
+    var $_default_error_mode = null;
+
+    /**
+     * Default error options used for this object when error mode
+     * is PEAR_ERROR_TRIGGER.
+     *
+     * @var     int
+     * @access  private
+     */
+    var $_default_error_options = null;
+
+    /**
+     * Default error handler (callback) for this object, if error mode is
+     * PEAR_ERROR_CALLBACK.
+     *
+     * @var     string
+     * @access  private
+     */
+    var $_default_error_handler = '';
+
+    /**
+     * Which class to use for error objects.
+     *
+     * @var     string
+     * @access  private
+     */
+    var $_error_class = 'PEAR_Error';
+
+    /**
+     * An array of expected errors.
+     *
+     * @var     array
+     * @access  private
+     */
+    var $_expected_errors = array();
+
+    /**
+     * Constructor.  Registers this object in
+     * $_PEAR_destructor_object_list for destructor emulation if a
+     * destructor object exists.
+     *
+     * @param string $error_class  (optional) which class to use for
+     *        error objects, defaults to PEAR_Error.
+     * @access public
+     * @return void
+     */
+    function PEAR($error_class = null)
+    {
+        $classname = strtolower(get_class($this));
+        if ($this->_debug) {
+            print "PEAR constructor called, class=$classname\n";
+        }
+
+        if ($error_class !== null) {
+            $this->_error_class = $error_class;
+        }
+
+        while ($classname && strcasecmp($classname, "pear")) {
+            $destructor = "_$classname";
+            if (method_exists($this, $destructor)) {
+                global $_PEAR_destructor_object_list;
+                $_PEAR_destructor_object_list[] = &$this;
+                if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
+                    register_shutdown_function("_PEAR_call_destructors");
+                    $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
+                }
+                break;
+            } else {
+                $classname = get_parent_class($classname);
+            }
+        }
+    }
+
+    /**
+     * Destructor (the emulated type of...).  Does nothing right now,
+     * but is included for forward compatibility, so subclass
+     * destructors should always call it.
+     *
+     * See the note in the class desciption about output from
+     * destructors.
+     *
+     * @access public
+     * @return void
+     */
+    function _PEAR() {
+        if ($this->_debug) {
+            printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
+        }
+    }
+
+    /**
+    * If you have a class that's mostly/entirely static, and you need static
+    * properties, you can use this method to simulate them. Eg. in your method(s)
+    * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
+    * You MUST use a reference, or they will not persist!
+    *
+    * @access public
+    * @param  string $class  The calling classname, to prevent clashes
+    * @param  string $var    The variable to retrieve.
+    * @return mixed   A reference to the variable. If not set it will be
+    *                 auto initialised to NULL.
+    */
+    function &getStaticProperty($class, $var)
+    {
+        static $properties;
+        if (!isset($properties[$class])) {
+            $properties[$class] = array();
+        }
+
+        if (!array_key_exists($var, $properties[$class])) {
+            $properties[$class][$var] = null;
+        }
+
+        return $properties[$class][$var];
+    }
+
+    /**
+    * Use this function to register a shutdown method for static
+    * classes.
+    *
+    * @access public
+    * @param  mixed $func  The function name (or array of class/method) to call
+    * @param  mixed $args  The arguments to pass to the function
+    * @return void
+    */
+    function registerShutdownFunc($func, $args = array())
+    {
+        // if we are called statically, there is a potential
+        // that no shutdown func is registered.  Bug #6445
+        if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
+            register_shutdown_function("_PEAR_call_destructors");
+            $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
+        }
+        $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
+    }
+
+    /**
+     * Tell whether a value is a PEAR error.
+     *
+     * @param   mixed $data   the value to test
+     * @param   int   $code   if $data is an error object, return true
+     *                        only if $code is a string and
+     *                        $obj->getMessage() == $code or
+     *                        $code is an integer and $obj->getCode() == $code
+     * @access  public
+     * @return  bool    true if parameter is an error
+     */
+    function isError($data, $code = null)
+    {
+        if (!is_a($data, 'PEAR_Error')) {
+            return false;
+        }
+
+        if (is_null($code)) {
+            return true;
+        } elseif (is_string($code)) {
+            return $data->getMessage() == $code;
+        }
+
+        return $data->getCode() == $code;
+    }
+
+    /**
+     * Sets how errors generated by this object should be handled.
+     * Can be invoked both in objects and statically.  If called
+     * statically, setErrorHandling sets the default behaviour for all
+     * PEAR objects.  If called in an object, setErrorHandling sets
+     * the default behaviour for that object.
+     *
+     * @param int $mode
+     *        One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+     *        PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+     *        PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
+     *
+     * @param mixed $options
+     *        When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
+     *        of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+     *
+     *        When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
+     *        to be the callback function or method.  A callback
+     *        function is a string with the name of the function, a
+     *        callback method is an array of two elements: the element
+     *        at index 0 is the object, and the element at index 1 is
+     *        the name of the method to call in the object.
+     *
+     *        When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
+     *        a printf format string used when printing the error
+     *        message.
+     *
+     * @access public
+     * @return void
+     * @see PEAR_ERROR_RETURN
+     * @see PEAR_ERROR_PRINT
+     * @see PEAR_ERROR_TRIGGER
+     * @see PEAR_ERROR_DIE
+     * @see PEAR_ERROR_CALLBACK
+     * @see PEAR_ERROR_EXCEPTION
+     *
+     * @since PHP 4.0.5
+     */
+    function setErrorHandling($mode = null, $options = null)
+    {
+        if (isset($this) && is_a($this, 'PEAR')) {
+            $setmode     = &$this->_default_error_mode;
+            $setoptions  = &$this->_default_error_options;
+        } else {
+            $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
+            $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
+        }
+
+        switch ($mode) {
+            case PEAR_ERROR_EXCEPTION:
+            case PEAR_ERROR_RETURN:
+            case PEAR_ERROR_PRINT:
+            case PEAR_ERROR_TRIGGER:
+            case PEAR_ERROR_DIE:
+            case null:
+                $setmode = $mode;
+                $setoptions = $options;
+                break;
+
+            case PEAR_ERROR_CALLBACK:
+                $setmode = $mode;
+                // class/object method callback
+                if (is_callable($options)) {
+                    $setoptions = $options;
+                } else {
+                    trigger_error("invalid error callback", E_USER_WARNING);
+                }
+                break;
+
+            default:
+                trigger_error("invalid error mode", E_USER_WARNING);
+                break;
+        }
+    }
+
+    /**
+     * This method is used to tell which errors you expect to get.
+     * Expected errors are always returned with error mode
+     * PEAR_ERROR_RETURN.  Expected error codes are stored in a stack,
+     * and this method pushes a new element onto it.  The list of
+     * expected errors are in effect until they are popped off the
+     * stack with the popExpect() method.
+     *
+     * Note that this method can not be called statically
+     *
+     * @param mixed $code a single error code or an array of error codes to expect
+     *
+     * @return int     the new depth of the "expected errors" stack
+     * @access public
+     */
+    function expectError($code = '*')
+    {
+        if (is_array($code)) {
+            array_push($this->_expected_errors, $code);
+        } else {
+            array_push($this->_expected_errors, array($code));
+        }
+        return count($this->_expected_errors);
+    }
+
+    /**
+     * This method pops one element off the expected error codes
+     * stack.
+     *
+     * @return array   the list of error codes that were popped
+     */
+    function popExpect()
+    {
+        return array_pop($this->_expected_errors);
+    }
+
+    /**
+     * This method checks unsets an error code if available
+     *
+     * @param mixed error code
+     * @return bool true if the error code was unset, false otherwise
+     * @access private
+     * @since PHP 4.3.0
+     */
+    function _checkDelExpect($error_code)
+    {
+        $deleted = false;
+        foreach ($this->_expected_errors as $key => $error_array) {
+            if (in_array($error_code, $error_array)) {
+                unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
+                $deleted = true;
+            }
+
+            // clean up empty arrays
+            if (0 == count($this->_expected_errors[$key])) {
+                unset($this->_expected_errors[$key]);
+            }
+        }
+
+        return $deleted;
+    }
+
+    /**
+     * This method deletes all occurences of the specified element from
+     * the expected error codes stack.
+     *
+     * @param  mixed $error_code error code that should be deleted
+     * @return mixed list of error codes that were deleted or error
+     * @access public
+     * @since PHP 4.3.0
+     */
+    function delExpect($error_code)
+    {
+        $deleted = false;
+        if ((is_array($error_code) && (0 != count($error_code)))) {
+            // $error_code is a non-empty array here; we walk through it trying
+            // to unset all values
+            foreach ($error_code as $key => $error) {
+                $deleted =  $this->_checkDelExpect($error) ? true : false;
+            }
+
+            return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+        } elseif (!empty($error_code)) {
+            // $error_code comes alone, trying to unset it
+            if ($this->_checkDelExpect($error_code)) {
+                return true;
+            }
+
+            return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+        }
+
+        // $error_code is empty
+        return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
+    }
+
+    /**
+     * This method is a wrapper that returns an instance of the
+     * configured error class with this object's default error
+     * handling applied.  If the $mode and $options parameters are not
+     * specified, the object's defaults are used.
+     *
+     * @param mixed $message a text error message or a PEAR error object
+     *
+     * @param int $code      a numeric error code (it is up to your class
+     *                  to define these if you want to use codes)
+     *
+     * @param int $mode      One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+     *                  PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+     *                  PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
+     *
+     * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
+     *                  specifies the PHP-internal error level (one of
+     *                  E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+     *                  If $mode is PEAR_ERROR_CALLBACK, this
+     *                  parameter specifies the callback function or
+     *                  method.  In other error modes this parameter
+     *                  is ignored.
+     *
+     * @param string $userinfo If you need to pass along for example debug
+     *                  information, this parameter is meant for that.
+     *
+     * @param string $error_class The returned error object will be
+     *                  instantiated from this class, if specified.
+     *
+     * @param bool $skipmsg If true, raiseError will only pass error codes,
+     *                  the error message parameter will be dropped.
+     *
+     * @access public
+     * @return object   a PEAR error object
+     * @see PEAR::setErrorHandling
+     * @since PHP 4.0.5
+     */
+    function &raiseError($message = null,
+                         $code = null,
+                         $mode = null,
+                         $options = null,
+                         $userinfo = null,
+                         $error_class = null,
+                         $skipmsg = false)
+    {
+        // The error is yet a PEAR error object
+        if (is_object($message)) {
+            $code        = $message->getCode();
+            $userinfo    = $message->getUserInfo();
+            $error_class = $message->getType();
+            $message->error_message_prefix = '';
+            $message     = $message->getMessage();
+        }
+
+        if (
+            isset($this) &&
+            isset($this->_expected_errors) &&
+            count($this->_expected_errors) > 0 &&
+            count($exp = end($this->_expected_errors))
+        ) {
+            if ($exp[0] == "*" ||
+                (is_int(reset($exp)) && in_array($code, $exp)) ||
+                (is_string(reset($exp)) && in_array($message, $exp))
+            ) {
+                $mode = PEAR_ERROR_RETURN;
+            }
+        }
+
+        // No mode given, try global ones
+        if ($mode === null) {
+            // Class error handler
+            if (isset($this) && isset($this->_default_error_mode)) {
+                $mode    = $this->_default_error_mode;
+                $options = $this->_default_error_options;
+            // Global error handler
+            } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
+                $mode    = $GLOBALS['_PEAR_default_error_mode'];
+                $options = $GLOBALS['_PEAR_default_error_options'];
+            }
+        }
+
+        if ($error_class !== null) {
+            $ec = $error_class;
+        } elseif (isset($this) && isset($this->_error_class)) {
+            $ec = $this->_error_class;
+        } else {
+            $ec = 'PEAR_Error';
+        }
+
+        if (intval(PHP_VERSION) < 5) {
+            // little non-eval hack to fix bug #12147
+            include 'PEAR/FixPHP5PEARWarnings.php';
+            return $a;
+        }
+
+        if ($skipmsg) {
+            $a = new $ec($code, $mode, $options, $userinfo);
+        } else {
+            $a = new $ec($message, $code, $mode, $options, $userinfo);
+        }
+
+        return $a;
+    }
+
+    /**
+     * Simpler form of raiseError with fewer options.  In most cases
+     * message, code and userinfo are enough.
+     *
+     * @param mixed $message a text error message or a PEAR error object
+     *
+     * @param int $code      a numeric error code (it is up to your class
+     *                  to define these if you want to use codes)
+     *
+     * @param string $userinfo If you need to pass along for example debug
+     *                  information, this parameter is meant for that.
+     *
+     * @access public
+     * @return object   a PEAR error object
+     * @see PEAR::raiseError
+     */
+    function &throwError($message = null, $code = null, $userinfo = null)
+    {
+        if (isset($this) && is_a($this, 'PEAR')) {
+            $a = &$this->raiseError($message, $code, null, null, $userinfo);
+            return $a;
+        }
+
+        $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
+        return $a;
+    }
+
+    function staticPushErrorHandling($mode, $options = null)
+    {
+        $stack       = &$GLOBALS['_PEAR_error_handler_stack'];
+        $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
+        $def_options = &$GLOBALS['_PEAR_default_error_options'];
+        $stack[] = array($def_mode, $def_options);
+        switch ($mode) {
+            case PEAR_ERROR_EXCEPTION:
+            case PEAR_ERROR_RETURN:
+            case PEAR_ERROR_PRINT:
+            case PEAR_ERROR_TRIGGER:
+            case PEAR_ERROR_DIE:
+            case null:
+                $def_mode = $mode;
+                $def_options = $options;
+                break;
+
+            case PEAR_ERROR_CALLBACK:
+                $def_mode = $mode;
+                // class/object method callback
+                if (is_callable($options)) {
+                    $def_options = $options;
+                } else {
+                    trigger_error("invalid error callback", E_USER_WARNING);
+                }
+                break;
+
+            default:
+                trigger_error("invalid error mode", E_USER_WARNING);
+                break;
+        }
+        $stack[] = array($mode, $options);
+        return true;
+    }
+
+    function staticPopErrorHandling()
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
+        $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
+        array_pop($stack);
+        list($mode, $options) = $stack[sizeof($stack) - 1];
+        array_pop($stack);
+        switch ($mode) {
+            case PEAR_ERROR_EXCEPTION:
+            case PEAR_ERROR_RETURN:
+            case PEAR_ERROR_PRINT:
+            case PEAR_ERROR_TRIGGER:
+            case PEAR_ERROR_DIE:
+            case null:
+                $setmode = $mode;
+                $setoptions = $options;
+                break;
+
+            case PEAR_ERROR_CALLBACK:
+                $setmode = $mode;
+                // class/object method callback
+                if (is_callable($options)) {
+                    $setoptions = $options;
+                } else {
+                    trigger_error("invalid error callback", E_USER_WARNING);
+                }
+                break;
+
+            default:
+                trigger_error("invalid error mode", E_USER_WARNING);
+                break;
+        }
+        return true;
+    }
+
+    /**
+     * Push a new error handler on top of the error handler options stack. With this
+     * you can easily override the actual error handler for some code and restore
+     * it later with popErrorHandling.
+     *
+     * @param mixed $mode (same as setErrorHandling)
+     * @param mixed $options (same as setErrorHandling)
+     *
+     * @return bool Always true
+     *
+     * @see PEAR::setErrorHandling
+     */
+    function pushErrorHandling($mode, $options = null)
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        if (isset($this) && is_a($this, 'PEAR')) {
+            $def_mode    = &$this->_default_error_mode;
+            $def_options = &$this->_default_error_options;
+        } else {
+            $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
+            $def_options = &$GLOBALS['_PEAR_default_error_options'];
+        }
+        $stack[] = array($def_mode, $def_options);
+
+        if (isset($this) && is_a($this, 'PEAR')) {
+            $this->setErrorHandling($mode, $options);
+        } else {
+            PEAR::setErrorHandling($mode, $options);
+        }
+        $stack[] = array($mode, $options);
+        return true;
+    }
+
+    /**
+    * Pop the last error handler used
+    *
+    * @return bool Always true
+    *
+    * @see PEAR::pushErrorHandling
+    */
+    function popErrorHandling()
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        array_pop($stack);
+        list($mode, $options) = $stack[sizeof($stack) - 1];
+        array_pop($stack);
+        if (isset($this) && is_a($this, 'PEAR')) {
+            $this->setErrorHandling($mode, $options);
+        } else {
+            PEAR::setErrorHandling($mode, $options);
+        }
+        return true;
+    }
+
+    /**
+    * OS independant PHP extension load. Remember to take care
+    * on the correct extension name for case sensitive OSes.
+    *
+    * @param string $ext The extension name
+    * @return bool Success or not on the dl() call
+    */
+    function loadExtension($ext)
+    {
+        if (extension_loaded($ext)) {
+            return true;
+        }
+
+        // if either returns true dl() will produce a FATAL error, stop that
+        if (
+            function_exists('dl') === false ||
+            ini_get('enable_dl') != 1 ||
+            ini_get('safe_mode') == 1
+        ) {
+            return false;
+        }
+
+        if (OS_WINDOWS) {
+            $suffix = '.dll';
+        } elseif (PHP_OS == 'HP-UX') {
+            $suffix = '.sl';
+        } elseif (PHP_OS == 'AIX') {
+            $suffix = '.a';
+        } elseif (PHP_OS == 'OSX') {
+            $suffix = '.bundle';
+        } else {
+            $suffix = '.so';
+        }
+
+        return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
+    }
+}
+
+if (PEAR_ZE2) {
+    include_once 'PEAR5.php';
+}
+
+function _PEAR_call_destructors()
+{
+    global $_PEAR_destructor_object_list;
+    if (is_array($_PEAR_destructor_object_list) &&
+        sizeof($_PEAR_destructor_object_list))
+    {
+        reset($_PEAR_destructor_object_list);
+        if (PEAR_ZE2) {
+            $destructLifoExists = PEAR5::getStaticProperty('PEAR', 'destructlifo');
+        } else {
+            $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo');
+        }
+
+        if ($destructLifoExists) {
+            $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
+        }
+
+        while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
+            $classname = get_class($objref);
+            while ($classname) {
+                $destructor = "_$classname";
+                if (method_exists($objref, $destructor)) {
+                    $objref->$destructor();
+                    break;
+                } else {
+                    $classname = get_parent_class($classname);
+                }
+            }
+        }
+        // Empty the object list to ensure that destructors are
+        // not called more than once.
+        $_PEAR_destructor_object_list = array();
+    }
+
+    // Now call the shutdown functions
+    if (
+        isset($GLOBALS['_PEAR_shutdown_funcs']) &&
+        is_array($GLOBALS['_PEAR_shutdown_funcs']) &&
+        !empty($GLOBALS['_PEAR_shutdown_funcs'])
+    ) {
+        foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
+            call_user_func_array($value[0], $value[1]);
+        }
+    }
+}
+
+/**
+ * Standard PEAR error class for PHP 4
+ *
+ * This class is supserseded by {@link PEAR_Exception} in PHP 5
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Stig Bakken <ssb@php.net>
+ * @author     Tomas V.V. Cox <cox@idecnet.com>
+ * @author     Gregory Beaver <cellog@php.net>
+ * @copyright  1997-2006 The PHP Group
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: 1.9.4
+ * @link       http://pear.php.net/manual/en/core.pear.pear-error.php
+ * @see        PEAR::raiseError(), PEAR::throwError()
+ * @since      Class available since PHP 4.0.2
+ */
+class PEAR_Error
+{
+    var $error_message_prefix = '';
+    var $mode                 = PEAR_ERROR_RETURN;
+    var $level                = E_USER_NOTICE;
+    var $code                 = -1;
+    var $message              = '';
+    var $userinfo             = '';
+    var $backtrace            = null;
+
+    /**
+     * PEAR_Error constructor
+     *
+     * @param string $message  message
+     *
+     * @param int $code     (optional) error code
+     *
+     * @param int $mode     (optional) error mode, one of: PEAR_ERROR_RETURN,
+     * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
+     * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
+     *
+     * @param mixed $options   (optional) error level, _OR_ in the case of
+     * PEAR_ERROR_CALLBACK, the callback function or object/method
+     * tuple.
+     *
+     * @param string $userinfo (optional) additional user/debug info
+     *
+     * @access public
+     *
+     */
+    function PEAR_Error($message = 'unknown error', $code = null,
+                        $mode = null, $options = null, $userinfo = null)
+    {
+        if ($mode === null) {
+            $mode = PEAR_ERROR_RETURN;
+        }
+        $this->message   = $message;
+        $this->code      = $code;
+        $this->mode      = $mode;
+        $this->userinfo  = $userinfo;
+
+        if (PEAR_ZE2) {
+            $skiptrace = PEAR5::getStaticProperty('PEAR_Error', 'skiptrace');
+        } else {
+            $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace');
+        }
+
+        if (!$skiptrace) {
+            $this->backtrace = debug_backtrace();
+            if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
+                unset($this->backtrace[0]['object']);
+            }
+        }
+
+        if ($mode & PEAR_ERROR_CALLBACK) {
+            $this->level = E_USER_NOTICE;
+            $this->callback = $options;
+        } else {
+            if ($options === null) {
+                $options = E_USER_NOTICE;
+            }
+
+            $this->level = $options;
+            $this->callback = null;
+        }
+
+        if ($this->mode & PEAR_ERROR_PRINT) {
+            if (is_null($options) || is_int($options)) {
+                $format = "%s";
+            } else {
+                $format = $options;
+            }
+
+            printf($format, $this->getMessage());
+        }
+
+        if ($this->mode & PEAR_ERROR_TRIGGER) {
+            trigger_error($this->getMessage(), $this->level);
+        }
+
+        if ($this->mode & PEAR_ERROR_DIE) {
+            $msg = $this->getMessage();
+            if (is_null($options) || is_int($options)) {
+                $format = "%s";
+                if (substr($msg, -1) != "\n") {
+                    $msg .= "\n";
+                }
+            } else {
+                $format = $options;
+            }
+            die(sprintf($format, $msg));
+        }
+
+        if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) {
+            call_user_func($this->callback, $this);
+        }
+
+        if ($this->mode & PEAR_ERROR_EXCEPTION) {
+            trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
+            eval('$e = new Exception($this->message, $this->code);throw($e);');
+        }
+    }
+
+    /**
+     * Get the error mode from an error object.
+     *
+     * @return int error mode
+     * @access public
+     */
+    function getMode()
+    {
+        return $this->mode;
+    }
+
+    /**
+     * Get the callback function/method from an error object.
+     *
+     * @return mixed callback function or object/method array
+     * @access public
+     */
+    function getCallback()
+    {
+        return $this->callback;
+    }
+
+    /**
+     * Get the error message from an error object.
+     *
+     * @return  string  full error message
+     * @access public
+     */
+    function getMessage()
+    {
+        return ($this->error_message_prefix . $this->message);
+    }
+
+    /**
+     * Get error code from an error object
+     *
+     * @return int error code
+     * @access public
+     */
+     function getCode()
+     {
+        return $this->code;
+     }
+
+    /**
+     * Get the name of this error/exception.
+     *
+     * @return string error/exception name (type)
+     * @access public
+     */
+    function getType()
+    {
+        return get_class($this);
+    }
+
+    /**
+     * Get additional user-supplied information.
+     *
+     * @return string user-supplied information
+     * @access public
+     */
+    function getUserInfo()
+    {
+        return $this->userinfo;
+    }
+
+    /**
+     * Get additional debug information supplied by the application.
+     *
+     * @return string debug information
+     * @access public
+     */
+    function getDebugInfo()
+    {
+        return $this->getUserInfo();
+    }
+
+    /**
+     * Get the call backtrace from where the error was generated.
+     * Supported with PHP 4.3.0 or newer.
+     *
+     * @param int $frame (optional) what frame to fetch
+     * @return array Backtrace, or NULL if not available.
+     * @access public
+     */
+    function getBacktrace($frame = null)
+    {
+        if (defined('PEAR_IGNORE_BACKTRACE')) {
+            return null;
+        }
+        if ($frame === null) {
+            return $this->backtrace;
+        }
+        return $this->backtrace[$frame];
+    }
+
+    function addUserInfo($info)
+    {
+        if (empty($this->userinfo)) {
+            $this->userinfo = $info;
+        } else {
+            $this->userinfo .= " ** $info";
+        }
+    }
+
+    function __toString()
+    {
+        return $this->getMessage();
+    }
+
+    /**
+     * Make a string representation of this object.
+     *
+     * @return string a string with an object summary
+     * @access public
+     */
+    function toString()
+    {
+        $modes = array();
+        $levels = array(E_USER_NOTICE  => 'notice',
+                        E_USER_WARNING => 'warning',
+                        E_USER_ERROR   => 'error');
+        if ($this->mode & PEAR_ERROR_CALLBACK) {
+            if (is_array($this->callback)) {
+                $callback = (is_object($this->callback[0]) ?
+                    strtolower(get_class($this->callback[0])) :
+                    $this->callback[0]) . '::' .
+                    $this->callback[1];
+            } else {
+                $callback = $this->callback;
+            }
+            return sprintf('[%s: message="%s" code=%d mode=callback '.
+                           'callback=%s prefix="%s" info="%s"]',
+                           strtolower(get_class($this)), $this->message, $this->code,
+                           $callback, $this->error_message_prefix,
+                           $this->userinfo);
+        }
+        if ($this->mode & PEAR_ERROR_PRINT) {
+            $modes[] = 'print';
+        }
+        if ($this->mode & PEAR_ERROR_TRIGGER) {
+            $modes[] = 'trigger';
+        }
+        if ($this->mode & PEAR_ERROR_DIE) {
+            $modes[] = 'die';
+        }
+        if ($this->mode & PEAR_ERROR_RETURN) {
+            $modes[] = 'return';
+        }
+        return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
+                       'prefix="%s" info="%s"]',
+                       strtolower(get_class($this)), $this->message, $this->code,
+                       implode("|", $modes), $levels[$this->level],
+                       $this->error_message_prefix,
+                       $this->userinfo);
+    }
+}
+
+/*
+ * Local Variables:
+ * mode: php
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
Index: trunk/library/PEAR/README
===================================================================
--- trunk/library/PEAR/README	(revision 5146)
+++ trunk/library/PEAR/README	(revision 5146)
@@ -0,0 +1,32 @@
+PEAR - The PEAR Installer
+=========================
+
+What is the PEAR Installer?  What is PEAR?
+
+PEAR is the PHP Extension and Application Repository, found at
+http://pear.php.net.  The PEAR Installer is this software, which
+contains executable files and PHP code that is used to download
+and install PEAR code from pear.php.net.
+
+PEAR contains useful software libraries and applications such as
+MDB2 (database abstraction), HTML_QuickForm (HTML forms management),
+PhpDocumentor (auto-documentation generator), DB_DataObject
+(Data Access Abstraction), and many hundreds more.  Browse all
+available packages at http://pear.php.net, the list is constantly
+growing and updating to reflect improvements in the PHP language.
+
+DOCUMENTATION
+=============
+
+Documentation for PEAR can be found at http://pear.php.net/manual/.
+Installation documentation can be found in the INSTALL file included
+in this tarball.
+
+WARNING: DO NOT RUN PEAR WITHOUT INSTALLING IT - if you downloaded this
+tarball manually, you MUST install it.  Read the instructions in INSTALL
+prior to use.
+
+
+Happy PHPing, we hope PEAR will be a great tool for your development work!
+
+$Id: README 313023 2011-07-06 19:17:11Z dufuz $
Index: trunk/library/iCalcreator/iCalUtilityFunctions.class.php
===================================================================
--- trunk/library/iCalcreator/iCalUtilityFunctions.class.php	(revision 5146)
+++ trunk/library/iCalcreator/iCalUtilityFunctions.class.php	(revision 5146)
@@ -0,0 +1,1340 @@
+<?php
+/**
+ * iCalcreator class v2.8
+ * copyright (c) 2007-2011 Kjell-Inge Gustafsson kigkonsult
+ * www.kigkonsult.se/iCalcreator/index.php
+ * ical@kigkonsult.se
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/**
+ * moving all utility (static) functions to a utility class
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.22 - 2010-09-25
+ *
+ */
+class iCalUtilityFunctions {
+  // Store the single instance of iCalUtilityFunctions
+  private static $m_pInstance;
+
+  // Private constructor to limit object instantiation to within the class
+  private function __construct() {
+    $m_pInstance = FALSE;
+  }
+
+  // Getter method for creating/returning the single instance of this class
+  public static function getInstance() {
+    if (!self::$m_pInstance)
+      self::$m_pInstance = new iCalUtilityFunctions();
+
+    return self::$m_pInstance;
+  }
+/**
+ * check a date(-time) for an opt. timezone and if it is a DATE-TIME or DATE
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-10-25
+ * @param array $date, date to check
+ * @param int $parno, no of date parts (i.e. year, month.. .)
+ * @return array $params, property parameters
+ */
+  public static function _chkdatecfg( $theDate, & $parno, & $params ) {
+    if( isset( $params['TZID'] ))
+      $parno = 6;
+    elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))
+      $parno = 3;
+    else {
+      if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] ))
+        $parno = 7;
+      if( is_array( $theDate )) {
+        if( isset( $theDate['timestamp'] ))
+          $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null;
+        else
+          $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null;
+        if( !empty( $tzid )) {
+          $parno = 7;
+          if( !iCalUtilityFunctions::_isOffset( $tzid ))
+            $params['TZID'] = $tzid; // save only timezone
+        }
+        elseif( !$parno && ( 3 == count( $theDate )) &&
+          ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )))
+          $parno = 3;
+        else
+          $parno = 6;
+      }
+      else { // string
+        $date = trim( $theDate );
+        if( 'Z' == substr( $date, -1 ))
+          $parno = 7; // UTC DATE-TIME
+        elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) &&
+          ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' ))))
+          $parno = 3; // DATE
+        $date = iCalUtilityFunctions::_date_time_string( $date, $parno );
+        if( !empty( $date['tz'] )) {
+          $parno = 7;
+          if( !iCalUtilityFunctions::_isOffset( $date['tz'] ))
+            $params['TZID'] = $date['tz']; // save only timezone
+        }
+        elseif( empty( $parno ))
+          $parno = 6;
+      }
+      if( isset( $params['TZID'] ))
+        $parno = 6;
+    }
+  }
+/**
+ * convert date/datetime to timestamp
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-30
+ * @param array  $datetime  datetime/(date)
+ * @param string $tz        timezone
+ * @return timestamp
+ */
+  public static function _date2timestamp( $datetime, $tz=null ) {
+    $output = null;
+    if( !isset( $datetime['hour'] )) $datetime['hour'] = '0';
+    if( !isset( $datetime['min'] ))  $datetime['min']  = '0';
+    if( !isset( $datetime['sec'] ))  $datetime['sec']  = '0';
+    foreach( $datetime as $dkey => $dvalue ) {
+      if( 'tz' != $dkey )
+        $datetime[$dkey] = (integer) $dvalue;
+    }
+    if( $tz )
+      $datetime['tz'] = $tz;
+    $offset = ( isset( $datetime['tz'] ) && ( '' < trim ( $datetime['tz'] ))) ? iCalUtilityFunctions::_tz2offset( $datetime['tz'] ) : 0;
+    $output = mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] + $offset), $datetime['month'], $datetime['day'], $datetime['year'] );
+    return $output;
+  }
+/**
+ * ensures internal date-time/date format for input date-time/date in array format
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 0.3.0 - 2006-08-15
+ * @param array $datetime
+ * @param int $parno optional, default FALSE
+ * @return array
+ */
+  public static function _date_time_array( $datetime, $parno=FALSE ) {
+    $output = array();
+    foreach( $datetime as $dateKey => $datePart ) {
+      switch ( $dateKey ) {
+        case '0': case 'year':   $output['year']  = $datePart; break;
+        case '1': case 'month':  $output['month'] = $datePart; break;
+        case '2': case 'day':    $output['day']   = $datePart; break;
+      }
+      if( 3 != $parno ) {
+        switch ( $dateKey ) {
+          case '0':
+          case '1':
+          case '2': break;
+          case '3': case 'hour': $output['hour']  = $datePart; break;
+          case '4': case 'min' : $output['min']   = $datePart; break;
+          case '5': case 'sec' : $output['sec']   = $datePart; break;
+          case '6': case 'tz'  : $output['tz']    = $datePart; break;
+        }
+      }
+    }
+    if( 3 != $parno ) {
+      if( !isset( $output['hour'] ))
+        $output['hour'] = 0;
+      if( !isset( $output['min']  ))
+        $output['min'] = 0;
+      if( !isset( $output['sec']  ))
+        $output['sec'] = 0;
+    }
+    return $output;
+  }
+/**
+ * ensures internal date-time/date format for input date-time/date in string fromat
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.35 - 2010-12-03
+ * @param array $datetime
+ * @param int $parno optional, default FALSE
+ * @return array
+ */
+  public static function _date_time_string( $datetime, $parno=FALSE ) {
+    $datetime = (string) trim( $datetime );
+    $tz  = null;
+    $len = strlen( $datetime ) - 1;
+    if( 'Z' == substr( $datetime, -1 )) {
+      $tz = 'Z';
+      $datetime = trim( substr( $datetime, 0, $len ));
+    }
+    elseif( ( ctype_digit( substr( $datetime, -2, 2 ))) && // time or date
+                  ( '-' == substr( $datetime, -3, 1 )) ||
+                  ( ':' == substr( $datetime, -3, 1 )) ||
+                  ( '.' == substr( $datetime, -3, 1 ))) {
+      $continue = TRUE;
+    }
+    elseif( ( ctype_digit( substr( $datetime, -4, 4 ))) && // 4 pos offset
+            ( ' +' == substr( $datetime, -6, 2 )) ||
+            ( ' -' == substr( $datetime, -6, 2 ))) {
+      $tz = substr( $datetime, -5, 5 );
+      $datetime = substr( $datetime, 0, ($len - 5));
+    }
+    elseif( ( ctype_digit( substr( $datetime, -6, 6 ))) && // 6 pos offset
+            ( ' +' == substr( $datetime, -8, 2 )) ||
+            ( ' -' == substr( $datetime, -8, 2 ))) {
+      $tz = substr( $datetime, -7, 7 );
+      $datetime = substr( $datetime, 0, ($len - 7));
+    }
+    elseif( ( 6 < $len ) && ( ctype_digit( substr( $datetime, -6, 6 )))) {
+      $continue = TRUE;
+    }
+    elseif( 'T' ==  substr( $datetime, -7, 1 )) {
+      $continue = TRUE;
+    }
+    else {
+      $cx  = $tx = 0;    //  19970415T133000 US-Eastern
+      for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) {
+        $char = substr( $datetime, $cx, 1 );
+        if(( ' ' == $char) || ctype_digit( $char))
+          break; // if exists, tz ends here.. . ?
+        else
+           $tx--; // tz length counter
+      }
+      if( 0 > $tx ) {
+        $tz = substr( $datetime, $tx );
+        $datetime = trim( substr( $datetime, 0, $len + $tx + 1 ));
+      }
+    }
+    if( 0 < substr_count( $datetime, '-' )) {
+      $datetime = str_replace( '-', '/', $datetime );
+    }
+    elseif( ctype_digit( substr( $datetime, 0, 8 )) &&
+           ( 'T' ==      substr( $datetime, 8, 1 )) &&
+            ctype_digit( substr( $datetime, 9, 6 ))) {
+      $datetime = substr( $datetime,  4, 2 )
+             .'/'.substr( $datetime,  6, 2 )
+             .'/'.substr( $datetime,  0, 4 )
+             .' '.substr( $datetime,  9, 2 )
+             .':'.substr( $datetime, 11, 2 )
+             .':'.substr( $datetime, 13);
+    }
+    $datestring = date( 'Y-m-d H:i:s', strtotime( $datetime ));
+    $tz                = trim( $tz );
+    $output            = array();
+    $output['year']    = substr( $datestring, 0, 4 );
+    $output['month']   = substr( $datestring, 5, 2 );
+    $output['day']     = substr( $datestring, 8, 2 );
+    if(( 6 == $parno ) || ( 7 == $parno ) || ( !$parno && ( 'Z' == $tz ))) {
+      $output['hour']  = substr( $datestring, 11, 2 );
+      $output['min']   = substr( $datestring, 14, 2 );
+      $output['sec']   = substr( $datestring, 17, 2 );
+      if( !empty( $tz ))
+        $output['tz']  = $tz;
+    }
+    elseif( 3 != $parno ) {
+      if(( '00' < substr( $datestring, 11, 2 )) ||
+         ( '00' < substr( $datestring, 14, 2 )) ||
+         ( '00' < substr( $datestring, 17, 2 ))) {
+        $output['hour']  = substr( $datestring, 11, 2 );
+        $output['min']   = substr( $datestring, 14, 2 );
+        $output['sec']   = substr( $datestring, 17, 2 );
+      }
+      if( !empty( $tz ))
+        $output['tz']  = $tz;
+    }
+    return $output;
+  }
+/**
+ * convert local startdate/enddate (Ymd[His]) to duration array
+ *
+ * uses this component dates if missing input dates
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.11 - 2010-10-21
+ * @param array $startdate
+ * @param array $duration
+ * @return array duration
+ */
+  function _date2duration( $startdate, $enddate ) {
+    $startWdate  = mktime( 0, 0, 0, $startdate['month'], $startdate['day'], $startdate['year'] );
+    $endWdate    = mktime( 0, 0, 0, $enddate['month'],   $enddate['day'],   $enddate['year'] );
+    $wduration   = $endWdate - $startWdate;
+    $dur         = array();
+    $dur['week'] = (int) floor( $wduration / ( 7 * 24 * 60 * 60 ));
+    $wduration   =              $wduration % ( 7 * 24 * 60 * 60 );
+    $dur['day']  = (int) floor( $wduration / ( 24 * 60 * 60 ));
+    $wduration   =              $wduration % ( 24 * 60 * 60 );
+    $dur['hour'] = (int) floor( $wduration / ( 60 * 60 ));
+    $wduration   =              $wduration % ( 60 * 60 );
+    $dur['min']  = (int) floor( $wduration / ( 60 ));
+    $dur['sec']  = (int)        $wduration % ( 60 );
+    return $dur;
+  }
+/**
+ * ensures internal duration format for input in array format
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.1.1 - 2007-06-24
+ * @param array $duration
+ * @return array
+ */
+  public static function _duration_array( $duration ) {
+    $output = array();
+    if(    is_array( $duration )        &&
+       ( 1 == count( $duration ))       &&
+              isset( $duration['sec'] ) &&
+              ( 60 < $duration['sec'] )) {
+      $durseconds  = $duration['sec'];
+      $output['week'] = floor( $durseconds / ( 60 * 60 * 24 * 7 ));
+      $durseconds  =           $durseconds % ( 60 * 60 * 24 * 7 );
+      $output['day']  = floor( $durseconds / ( 60 * 60 * 24 ));
+      $durseconds  =           $durseconds % ( 60 * 60 * 24 );
+      $output['hour'] = floor( $durseconds / ( 60 * 60 ));
+      $durseconds  =           $durseconds % ( 60 * 60 );
+      $output['min']  = floor( $durseconds / ( 60 ));
+      $output['sec']  =      ( $durseconds % ( 60 ));
+    }
+    else {
+      foreach( $duration as $durKey => $durValue ) {
+        if( empty( $durValue )) continue;
+        switch ( $durKey ) {
+          case '0': case 'week': $output['week']  = $durValue; break;
+          case '1': case 'day':  $output['day']   = $durValue; break;
+          case '2': case 'hour': $output['hour']  = $durValue; break;
+          case '3': case 'min':  $output['min']   = $durValue; break;
+          case '4': case 'sec':  $output['sec']   = $durValue; break;
+        }
+      }
+    }
+    if( isset( $output['week'] ) && ( 0 < $output['week'] )) {
+      unset( $output['day'], $output['hour'], $output['min'], $output['sec'] );
+      return $output;
+    }
+    unset( $output['week'] );
+    if( empty( $output['day'] ))
+      unset( $output['day'] );
+    if ( isset( $output['hour'] ) || isset( $output['min'] ) || isset( $output['sec'] )) {
+      if( !isset( $output['hour'] )) $output['hour'] = 0;
+      if( !isset( $output['min']  )) $output['min']  = 0;
+      if( !isset( $output['sec']  )) $output['sec']  = 0;
+      if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] ))
+        unset( $output['hour'], $output['min'], $output['sec'] );
+    }
+    return $output;
+  }
+/**
+ * ensures internal duration format for input in string format
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.0.5 - 2007-03-14
+ * @param string $duration
+ * @return array
+ */
+  public static function _duration_string( $duration ) {
+    $duration = (string) trim( $duration );
+    while( 'P' != strtoupper( substr( $duration, 0, 1 ))) {
+      if( 0 < strlen( $duration ))
+        $duration = substr( $duration, 1 );
+      else
+        return false; // no leading P !?!?
+    }
+    $duration = substr( $duration, 1 ); // skip P
+    $duration = str_replace ( 't', 'T', $duration );
+    $duration = str_replace ( 'T', '', $duration );
+    $output = array();
+    $val    = null;
+    for( $ix=0; $ix < strlen( $duration ); $ix++ ) {
+      switch( strtoupper( substr( $duration, $ix, 1 ))) {
+       case 'W':
+         $output['week'] = $val;
+         $val            = null;
+         break;
+       case 'D':
+         $output['day']  = $val;
+         $val            = null;
+         break;
+       case 'H':
+         $output['hour'] = $val;
+         $val            = null;
+         break;
+       case 'M':
+         $output['min']  = $val;
+         $val            = null;
+         break;
+       case 'S':
+         $output['sec']  = $val;
+         $val            = null;
+         break;
+       default:
+         if( !ctype_digit( substr( $duration, $ix, 1 )))
+           return false; // unknown duration controll character  !?!?
+         else
+           $val .= substr( $duration, $ix, 1 );
+      }
+    }
+    return iCalUtilityFunctions::_duration_array( $output );
+  }
+/**
+ * convert duration to date in array format
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.23 - 2010-10-23
+ * @param array $startdate
+ * @param array $duration
+ * @return array, date format
+ */
+  function _duration2date( $startdate=null, $duration=null ) {
+    if( empty( $startdate )) return FALSE;
+    if( empty( $duration ))  return FALSE;
+    $dateOnly          = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE;
+    $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0;
+    $startdate['min']  = ( isset( $startdate['min'] ))  ? $startdate['min']  : 0;
+    $startdate['sec']  = ( isset( $startdate['sec'] ))  ? $startdate['sec']  : 0;
+    $dtend = 0;
+    if(    isset( $duration['week'] ))
+      $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 );
+    if(    isset( $duration['day'] ))
+      $dtend += ( $duration['day'] * 24 * 60 * 60 );
+    if(    isset( $duration['hour'] ))
+      $dtend += ( $duration['hour'] * 60 *60 );
+    if(    isset( $duration['min'] ))
+      $dtend += ( $duration['min'] * 60 );
+    if(    isset( $duration['sec'] ))
+      $dtend +=   $duration['sec'];
+    if(( 24 * 60 * 60 ) < $dtend )
+      $dtend -= ( 24 * 60 * 60 ); // exclude start day
+    $dtend += mktime( $startdate['hour'], $startdate['min'], $startdate['sec'], $startdate['month'], $startdate['day'], $startdate['year'] );
+    $dtend2 = array();
+    $dtend2['year']   = date('Y', $dtend );
+    $dtend2['month']  = date('m', $dtend );
+    $dtend2['day']    = date('d', $dtend );
+    $dtend2['hour']   = date('H', $dtend );
+    $dtend2['min']    = date('i', $dtend );
+    $dtend2['sec']    = date('s', $dtend );
+    if( isset( $startdate['tz'] ))
+      $dtend2['tz']   = $startdate['tz'];
+    if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] )))
+      unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] );
+    return $dtend2;
+  }
+/**
+ * if not preSet, if exist, remove key with expected value from array and return hit value else return elseValue
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-11-08
+ * @param array $array
+ * @param string $expkey, expected key
+ * @param string $expval, expected value
+ * @param int $hitVal optional, return value if found
+ * @param int $elseVal optional, return value if not found
+ * @param int $preSet optional, return value if already preset
+ * @return int
+ */
+  public static function _existRem( &$array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) {
+    if( $preSet )
+      return $preSet;
+    if( !is_array( $array ) || ( 0 == count( $array )))
+      return $elseVal;
+    foreach( $array as $key => $value ) {
+      if( strtoupper( $expkey ) == strtoupper( $key )) {
+        if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) {
+          unset( $array[$key] );
+          return $hitVal;
+        }
+      }
+    }
+    return $elseVal;
+  }
+/**
+ * creates formatted output for calendar component property data value type date/date-time
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-30
+ * @param array   $datetime
+ * @param int     $parno, optional, default 6
+ * @return string
+ */
+  public static function _format_date_time( $datetime, $parno=6 ) {
+    if( !isset( $datetime['year'] )  &&
+        !isset( $datetime['month'] ) &&
+        !isset( $datetime['day'] )   &&
+        !isset( $datetime['hour'] )  &&
+        !isset( $datetime['min'] )   &&
+        !isset( $datetime['sec'] ))
+      return ;
+    $output = null;
+    // if( !isset( $datetime['day'] )) { $o=''; foreach($datetime as $k=>$v) {if(is_array($v)) $v=implode('-',$v);$o.=" $k=>$v";} echo " day SAKNAS : $o <br />\n"; }
+    foreach( $datetime as $dkey => & $dvalue )
+      if( 'tz' != $dkey ) $dvalue = (integer) $dvalue;
+    $output = date('Ymd', mktime( 0, 0, 0, $datetime['month'], $datetime['day'], $datetime['year']));
+    if( isset( $datetime['hour'] )  ||
+        isset( $datetime['min'] )   ||
+        isset( $datetime['sec'] )   ||
+        isset( $datetime['tz'] )) {
+      if( isset( $datetime['tz'] )  &&
+         !isset( $datetime['hour'] ))
+        $datetime['hour'] = 0;
+      if( isset( $datetime['hour'] )  &&
+         !isset( $datetime['min'] ))
+        $datetime['min'] = 0;
+      if( isset( $datetime['hour'] )  &&
+          isset( $datetime['min'] )   &&
+         !isset( $datetime['sec'] ))
+        $datetime['sec'] = 0;
+      $date = mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year']);
+      $output .= date('\THis', $date );
+      if( isset( $datetime['tz'] ) && ( '' < trim ( $datetime['tz'] ))) {
+        $datetime['tz'] = trim( $datetime['tz'] );
+        if( 'Z' == $datetime['tz'] )
+          $output .= 'Z';
+        $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] );
+        if( 0 != $offset ) {
+          $date = mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] + $offset), $datetime['month'], $datetime['day'], $datetime['year']);
+          $output    = date( 'Ymd\THis\Z', $date );
+        }
+      }
+      elseif( 7 == $parno )
+        $output .= 'Z';
+    }
+    return $output;
+  }
+/**
+ * creates formatted output for calendar component property data value type duration
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.10 - 2010-11-28
+ * @param array $duration ( week, day, hour, min, sec )
+ * @return string
+ */
+  public static function _format_duration( $duration ) {
+    if( isset( $duration['week'] ) ||
+        isset( $duration['day'] )  ||
+        isset( $duration['hour'] ) ||
+        isset( $duration['min'] )  ||
+        isset( $duration['sec'] ))
+       $ok = TRUE;
+    else
+      return;
+    if( isset( $duration['week'] ) && ( 0 < $duration['week'] ))
+      return 'P'.$duration['week'].'W';
+    $output = 'P';
+    if( isset($duration['day'] ) && ( 0 < $duration['day'] ))
+      $output .= $duration['day'].'D';
+    if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ||
+       ( isset( $duration['min'])  && ( 0 < $duration['min'] ))  ||
+       ( isset( $duration['sec'])  && ( 0 < $duration['sec'] )))
+      $output .= 'T';
+    $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : '';
+    $output .= ( isset( $duration['min'])  && ( 0 < $duration['min'] ))  ? $duration['min']. 'M' : '';
+    $output .= ( isset( $duration['sec'])  && ( 0 < $duration['sec'] ))  ? $duration['sec']. 'S' : '';
+    return $output;
+  }
+/**
+ * checks if input array contains a date
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-10-25
+ * @param array $input
+ * @return bool
+ */
+  public static function _isArrayDate( $input ) {
+    if( isset( $input['week'] ) || ( !in_array( count( $input ), array( 3, 6, 7 ))))
+      return FALSE;
+    if( 7 == count( $input ))
+      return TRUE;
+    if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
+      return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
+    if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] ))
+      return FALSE;
+    if( in_array( 0, $input ))
+      return FALSE;
+    if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] ))
+      return FALSE;
+    if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) &&
+         checkdate( (int) $input[1], (int) $input[2], (int) $input[0] ))
+      return TRUE;
+    $input = iCalUtilityFunctions::_date_time_string( $input[1].'/'.$input[2].'/'.$input[0], 3 ); //  m - d - Y
+    if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
+      return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
+    return FALSE;
+  }
+/**
+ * checks if input array contains a timestamp date
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-10-18
+ * @param array $input
+ * @return bool
+ */
+  public static function _isArrayTimestampDate( $input ) {
+    return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ;
+  }
+/**
+ * controll if input string contains trailing UTC offset
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-10-19
+ * @param string $input
+ * @return bool
+ */
+  public static function _isOffset( $input ) {
+    $input         = trim( (string) $input );
+    if( 'Z' == substr( $input, -1 ))
+      return TRUE;
+    elseif((   5 <= strlen( $input )) &&
+       ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) &&
+       (   '0000'  < substr( $input, -4 )) && (   '9999' >= substr( $input, -4 )))
+      return TRUE;
+    elseif((    7 <= strlen( $input )) &&
+       ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) &&
+       ( '000000'  < substr( $input, -6 )) && ( '999999' >= substr( $input, -6 )))
+      return TRUE;
+    return FALSE;
+
+  }
+/**
+ * remakes a recur pattern to an array of dates
+ *
+ * if missing, UNTIL is set 1 year from startdate (emergency break)
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.23 - 2010-10-24
+ * @param array $result, array to update, array([timestamp] => timestamp)
+ * @param array $recur, pattern for recurrency (only value part, params ignored)
+ * @param array $wdate, component start date
+ * @param array $startdate, start date
+ * @param array $enddate, optional
+ * @return array of recurrence (start-)dates as index
+ * @todo BYHOUR, BYMINUTE, BYSECOND, ev. BYSETPOS due to ambiguity, WEEKLY at year end/start
+ */
+  public static function _recur2date( & $result, $recur, $wdate, $startdate, $enddate=FALSE ) {
+    foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v;
+    $wdatets     = iCalUtilityFunctions::_date2timestamp( $wdate );
+    $startdatets = iCalUtilityFunctions::_date2timestamp( $startdate );
+    if( !$enddate ) {
+      $enddate = $startdate;
+      $enddate['year'] += 1;
+// echo "recur __in_ ".implode('-',$startdate)." period start ".implode('-',$wdate)." period end ".implode('-',$enddate)."<br />\n";print_r($recur);echo "<br />\n";//test###
+    }
+    $endDatets = iCalUtilityFunctions::_date2timestamp( $enddate ); // fix break
+    if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] ))
+      $recur['UNTIL'] = $enddate; // create break
+    if( isset( $recur['UNTIL'] )) {
+      $tdatets = iCalUtilityFunctions::_date2timestamp( $recur['UNTIL'] );
+      if( $endDatets > $tdatets ) {
+        $endDatets = $tdatets; // emergency break
+        $enddate   = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 );
+      }
+      else
+        $recur['UNTIL'] = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 );
+    }
+    if( $wdatets > $endDatets ) {
+     //echo "recur out of date ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
+      return array(); // nothing to do.. .
+    }
+    if( !isset( $recur['FREQ'] )) // "MUST be specified.. ."
+      $recur['FREQ'] = 'DAILY'; // ??
+    $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ??
+    if( !isset( $recur['INTERVAL'] ))
+      $recur['INTERVAL'] = 1;
+    $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence
+            /* find out how to step up dates and set index for interval count */
+    $step = array();
+    if( 'YEARLY' == $recur['FREQ'] )
+      $step['year']  = 1;
+    elseif( 'MONTHLY' == $recur['FREQ'] )
+      $step['month'] = 1;
+    elseif( 'WEEKLY' == $recur['FREQ'] )
+      $step['day']   = 7;
+    else
+      $step['day']   = 1;
+    if( isset( $step['year'] ) && isset( $recur['BYMONTH'] ))
+      $step = array( 'month' => 1 );
+    if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ??
+      $step = array( 'day' => 7 );
+    if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] ))
+      $step = array( 'day' => 1 );
+    $intervalarr = array();
+    if( 1 < $recur['INTERVAL'] ) {
+      $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
+      $intervalarr = array( $intervalix => 0 );
+    }
+    if( isset( $recur['BYSETPOS'] )) { // save start date + weekno
+      $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array();
+      $bysetposWold = (int) date( 'W', ( $wdatets + $wkst ));
+      $bysetposYold = $wdate['year'];
+      $bysetposMold = $wdate['month'];
+      $bysetposDold = $wdate['day'];
+      if( is_array( $recur['BYSETPOS'] )) {
+        foreach( $recur['BYSETPOS'] as $bix => $bval )
+          $recur['BYSETPOS'][$bix] = (int) $bval;
+      }
+      else
+        $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] );
+      iCalUtilityFunctions::_stepdate( $enddate, $endDatets, $step); // make sure to count whole last period
+    }
+    iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
+    $year_old     = null;
+    $daynames     = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
+             /* MAIN LOOP */
+     // echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."<br />\n";//test
+    while( TRUE ) {
+      if( isset( $endDatets ) && ( $wdatets > $endDatets ))
+        break;
+      if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
+        break;
+      if( $year_old != $wdate['year'] ) {
+        $year_old   = $wdate['year'];
+        $daycnts    = array();
+        $yeardays   = $weekno = 0;
+        $yeardaycnt = array();
+        for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters
+          $daycnts[$m] = array();
+          $weekdaycnt = array();
+          foreach( $daynames as $dn )
+            $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
+          $mcnt     = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
+          for( $d   = 1; $d <= $mcnt; $d++ ) {
+            $daycnts[$m][$d] = array();
+            if( isset( $recur['BYYEARDAY'] )) {
+              $yeardays++;
+              $daycnts[$m][$d]['yearcnt_up'] = $yeardays;
+            }
+            if( isset( $recur['BYDAY'] )) {
+              $day    = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] ));
+              $day    = $daynames[$day];
+              $daycnts[$m][$d]['DAY'] = $day;
+              $weekdaycnt[$day]++;
+              $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day];
+              $yeardaycnt[$day]++;
+              $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day];
+            }
+            if(  isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
+              $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year']));
+          }
+        }
+        $daycnt = 0;
+        $yeardaycnt = array();
+        if(  isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) {
+          $weekno = null;
+          for( $d=31; $d > 25; $d-- ) { // get last weekno for year
+            if( !$weekno )
+              $weekno = $daycnts[12][$d]['weekno_up'];
+            elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) {
+              $weekno = $daycnts[12][$d]['weekno_up'];
+              break;
+            }
+          }
+        }
+        for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters
+          $weekdaycnt = array();
+          foreach( $daynames as $dn )
+            $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
+          $monthcnt = 0;
+          $mcnt     = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
+          for( $d   = $mcnt; $d > 0; $d-- ) {
+            if( isset( $recur['BYYEARDAY'] )) {
+              $daycnt -= 1;
+              $daycnts[$m][$d]['yearcnt_down'] = $daycnt;
+            }
+            if( isset( $recur['BYMONTHDAY'] )) {
+              $monthcnt -= 1;
+              $daycnts[$m][$d]['monthcnt_down'] = $monthcnt;
+            }
+            if( isset( $recur['BYDAY'] )) {
+              $day  = $daycnts[$m][$d]['DAY'];
+              $weekdaycnt[$day] -= 1;
+              $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day];
+              $yeardaycnt[$day] -= 1;
+              $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day];
+            }
+            if(  isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
+              $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1);
+          }
+        }
+      }
+            /* check interval */
+      if( 1 < $recur['INTERVAL'] ) {
+            /* create interval index */
+        $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
+            /* check interval */
+        $currentKey = array_keys( $intervalarr );
+        $currentKey = end( $currentKey ); // get last index
+        if( $currentKey != $intervalix )
+          $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 ));
+        if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) &&
+           ( 0 != $intervalarr[$intervalix] )) {
+            /* step up date */
+    //echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
+          iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
+          continue;
+        }
+        else // continue within the selected interval
+          $intervalarr[$intervalix] = 0;
+   //echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
+      }
+      $updateOK = TRUE;
+      if( $updateOK && isset( $recur['BYMONTH'] ))
+        $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH']
+                                           , $wdate['month']
+                                           ,($wdate['month'] - 13));
+      if( $updateOK && isset( $recur['BYWEEKNO'] ))
+        $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO']
+                                           , $daycnts[$wdate['month']][$wdate['day']]['weekno_up']
+                                           , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] );
+      if( $updateOK && isset( $recur['BYYEARDAY'] ))
+        $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY']
+                                           , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up']
+                                           , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] );
+      if( $updateOK && isset( $recur['BYMONTHDAY'] ))
+        $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY']
+                                           , $wdate['day']
+                                           , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] );
+    //echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n";//test###
+      if( $updateOK && isset( $recur['BYDAY'] )) {
+        $updateOK = FALSE;
+        $m = $wdate['month'];
+        $d = $wdate['day'];
+        if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no
+          $daynoexists = $daynosw = $daynamesw =  FALSE;
+          if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] )
+            $daynamesw = TRUE;
+          if( isset( $recur['BYDAY'][0] )) {
+            $daynoexists = TRUE;
+            if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] ))
+              $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
+                                                , $daycnts[$m][$d]['monthdayno_up']
+                                                , $daycnts[$m][$d]['monthdayno_down'] );
+            elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
+              $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
+                                                , $daycnts[$m][$d]['yeardayno_up']
+                                                , $daycnts[$m][$d]['yeardayno_down'] );
+          }
+          if((  $daynoexists &&  $daynosw && $daynamesw ) ||
+             ( !$daynoexists && !$daynosw && $daynamesw )) {
+            $updateOK = TRUE;
+          }
+        //echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
+        }
+        else {
+          foreach( $recur['BYDAY'] as $bydayvalue ) {
+            $daynoexists = $daynosw = $daynamesw = FALSE;
+            if( isset( $bydayvalue['DAY'] ) &&
+                     ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] ))
+              $daynamesw = TRUE;
+            if( isset( $bydayvalue[0] )) {
+              $daynoexists = TRUE;
+              if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) ||
+                   isset( $recur['BYMONTH'] ))
+                $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
+                                                  , $daycnts[$m][$d]['monthdayno_up']
+                                                  , $daycnts[$m][$d]['monthdayno_down'] );
+              elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
+                $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
+                                                  , $daycnts[$m][$d]['yeardayno_up']
+                                                  , $daycnts[$m][$d]['yeardayno_down'] );
+            }
+        //echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
+            if((  $daynoexists &&  $daynosw && $daynamesw ) ||
+               ( !$daynoexists && !$daynosw && $daynamesw )) {
+              $updateOK = TRUE;
+              break;
+            }
+          }
+        }
+      }
+      //echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n"; // test ###
+            /* check BYSETPOS */
+      if( $updateOK ) {
+        if( isset( $recur['BYSETPOS'] ) &&
+          ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) {
+          if( isset( $recur['WEEKLY'] )) {
+            if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] )
+              $bysetposw1[] = $wdatets;
+            else
+              $bysetposw2[] = $wdatets;
+          }
+          else {
+            if(( isset( $recur['FREQ'] ) && ( 'YEARLY'      == $recur['FREQ'] )  &&
+                                            ( $bysetposYold == $wdate['year'] ))   ||
+               ( isset( $recur['FREQ'] ) && ( 'MONTHLY'     == $recur['FREQ'] )  &&
+                                           (( $bysetposYold == $wdate['year'] )  &&
+                                            ( $bysetposMold == $wdate['month'] ))) ||
+               ( isset( $recur['FREQ'] ) && ( 'DAILY'       == $recur['FREQ'] )  &&
+                                           (( $bysetposYold == $wdate['year'] )  &&
+                                            ( $bysetposMold == $wdate['month'])  &&
+                                            ( $bysetposDold == $wdate['day'] ))))
+              $bysetposymd1[] = $wdatets;
+            else
+              $bysetposymd2[] = $wdatets;
+          }
+        }
+        else {
+            /* update result array if BYSETPOS is set */
+          $countcnt++;
+          if( $startdatets <= $wdatets ) { // only output within period
+            $result[$wdatets] = TRUE;
+          //echo "recur ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
+          }
+         //else echo "recur undate ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))." okdatstart ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$startdatets),6))."<br />\n";//test
+          $updateOK = FALSE;
+        }
+      }
+            /* step up date */
+      iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
+            /* check if BYSETPOS is set for updating result array */
+      if( $updateOK && isset( $recur['BYSETPOS'] )) {
+        $bysetpos       = FALSE;
+        if( isset( $recur['FREQ'] ) && ( 'YEARLY'  == $recur['FREQ'] ) &&
+          ( $bysetposYold != $wdate['year'] )) {
+          $bysetpos     = TRUE;
+          $bysetposYold = $wdate['year'];
+        }
+        elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] &&
+         (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) {
+          $bysetpos     = TRUE;
+          $bysetposYold = $wdate['year'];
+          $bysetposMold = $wdate['month'];
+        }
+        elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY'  == $recur['FREQ'] )) {
+          $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year']));
+          if( $bysetposWold != $weekno ) {
+            $bysetposWold = $weekno;
+            $bysetpos     = TRUE;
+          }
+        }
+        elseif( isset( $recur['FREQ'] ) && ( 'DAILY'   == $recur['FREQ'] ) &&
+         (( $bysetposYold != $wdate['year'] )  ||
+          ( $bysetposMold != $wdate['month'] ) ||
+          ( $bysetposDold != $wdate['day'] ))) {
+          $bysetpos     = TRUE;
+          $bysetposYold = $wdate['year'];
+          $bysetposMold = $wdate['month'];
+          $bysetposDold = $wdate['day'];
+        }
+        if( $bysetpos ) {
+          if( isset( $recur['BYWEEKNO'] )) {
+            $bysetposarr1 = & $bysetposw1;
+            $bysetposarr2 = & $bysetposw2;
+          }
+          else {
+            $bysetposarr1 = & $bysetposymd1;
+            $bysetposarr2 = & $bysetposymd2;
+          }
+          foreach( $recur['BYSETPOS'] as $ix ) {
+            if( 0 > $ix ) // both positive and negative BYSETPOS allowed
+              $ix = ( count( $bysetposarr1 ) + $ix + 1);
+            $ix--;
+            if( isset( $bysetposarr1[$ix] )) {
+              if( $startdatets <= $bysetposarr1[$ix] ) { // only output within period
+                $result[$bysetposarr1[$ix]] = TRUE;
+       //echo "recur ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$bysetposarr1[$ix]),6))."<br />\n";//test
+              }
+              $countcnt++;
+            }
+            if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
+              break;
+          }
+          $bysetposarr1 = $bysetposarr2;
+          $bysetposarr2 = array();
+        }
+      }
+    }
+  }
+  public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) {
+    if( is_array( $BYvalue ) &&
+      ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue )))
+      return TRUE;
+    elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue ))
+      return TRUE;
+    else
+      return FALSE;
+  }
+  public static function _recurIntervalIx( $freq, $date, $wkst ) {
+            /* create interval index */
+    switch( $freq ) {
+      case 'YEARLY':
+        $intervalix = $date['year'];
+        break;
+      case 'MONTHLY':
+        $intervalix = $date['year'].'-'.$date['month'];
+        break;
+      case 'WEEKLY':
+        $wdatets    = iCalUtilityFunctions::_date2timestamp( $date );
+        $intervalix = (int) date( 'W', ( $wdatets + $wkst ));
+       break;
+      case 'DAILY':
+           default:
+        $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day'];
+        break;
+    }
+    return $intervalix;
+  }
+/**
+ * convert input format for exrule and rrule to internal format
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.23 - 2010-12-07
+ * @param array $rexrule
+ * @return array
+ */
+  public static function _setRexrule( $rexrule ) {
+    $input          = array();
+    if( empty( $rexrule ))
+      return $input;
+    foreach( $rexrule as $rexrulelabel => $rexrulevalue ) {
+      $rexrulelabel = strtoupper( $rexrulelabel );
+      if( 'UNTIL'  != $rexrulelabel )
+        $input[$rexrulelabel]   = $rexrulevalue;
+      else {
+        if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp date
+          $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 6 );
+        elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) // date-time
+          $input[$rexrulelabel] = iCalUtilityFunctions::_date_time_array( $rexrulevalue, 6 );
+        elseif( 8 <= strlen( trim( $rexrulevalue ))) // ex. 2006-08-03 10:12:18
+          $input[$rexrulelabel] = iCalUtilityFunctions::_date_time_string( $rexrulevalue );
+        if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] ))
+          $input[$rexrulelabel]['tz'] = 'Z';
+      }
+    }
+            /* set recurrence rule specification in rfc2445 order */
+    $input2 = array();
+    if( isset( $input['FREQ'] ))
+      $input2['FREQ']       = $input['FREQ'];
+    if( isset( $input['UNTIL'] ))
+      $input2['UNTIL']      = $input['UNTIL'];
+    elseif( isset( $input['COUNT'] ))
+      $input2['COUNT']      = $input['COUNT'];
+    if( isset( $input['INTERVAL'] ))
+      $input2['INTERVAL']   = $input['INTERVAL'];
+    if( isset( $input['BYSECOND'] ))
+      $input2['BYSECOND']   = $input['BYSECOND'];
+    if( isset( $input['BYMINUTE'] ))
+      $input2['BYMINUTE']   = $input['BYMINUTE'];
+    if( isset( $input['BYHOUR'] ))
+      $input2['BYHOUR']     = $input['BYHOUR'];
+    if( isset( $input['BYDAY'] ))
+      $input2['BYDAY']      = $input['BYDAY'];
+    if( isset( $input['BYMONTHDAY'] ))
+      $input2['BYMONTHDAY'] = $input['BYMONTHDAY'];
+    if( isset( $input['BYYEARDAY'] ))
+      $input2['BYYEARDAY']  = $input['BYYEARDAY'];
+    if( isset( $input['BYWEEKNO'] ))
+      $input2['BYWEEKNO']   = $input['BYWEEKNO'];
+    if( isset( $input['BYMONTH'] ))
+      $input2['BYMONTH']    = $input['BYMONTH'];
+    if( isset( $input['BYSETPOS'] ))
+      $input2['BYSETPOS']   = $input['BYSETPOS'];
+    if( isset( $input['WKST'] ))
+      $input2['WKST']       = $input['WKST'];
+    return $input2;
+  }
+/**
+ * convert format for input date to internal date with parameters
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.22 - 2010-09-25
+ * @param mixed $year
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param string $tz optional
+ * @param array $params optional
+ * @param string $caller optional
+ * @param string $objName optional
+ * @return array
+ */
+  public static function _setDate( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE, $caller=null, $objName=null ) {
+    $input = $parno = null;
+    $localtime = (( 'dtstart' == $caller ) && in_array( $objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
+    if( iCalUtilityFunctions::_isArrayDate( $year )) {
+      if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
+      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
+      if( isset( $input['params']['TZID'] )) {
+        $input['params']['VALUE'] = 'DATE-TIME';
+        unset( $year['tz'] );
+      }
+      $hitval          = (( !empty( $year['tz'] ) || !empty( $year[6] ))) ? 7 : 6;
+      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval );
+      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $year ), $parno );
+      $input['value']  = iCalUtilityFunctions::_date_time_array( $year, $parno );
+    }
+    elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
+      if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
+      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
+      if( isset( $input['params']['TZID'] )) {
+        $input['params']['VALUE'] = 'DATE-TIME';
+        unset( $year['tz'] );
+      }
+      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
+      $hitval          = ( isset( $year['tz'] )) ? 7 : 6;
+      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno );
+      $input['value']  = iCalUtilityFunctions::_timestamp2date( $year, $parno );
+    }
+    elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
+      if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
+      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
+      if( isset( $input['params']['TZID'] )) {
+        $input['params']['VALUE'] = 'DATE-TIME';
+        $parno = 6;
+      }
+      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno );
+      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno );
+      $input['value']  = iCalUtilityFunctions::_date_time_string( $year, $parno );
+    }
+    else {
+      if( is_array( $params )) {
+        if( $localtime ) unset ( $params['VALUE'], $params['TZID'] );
+        $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
+      }
+      elseif( is_array( $tz )) {
+        $input['params'] = iCalUtilityFunctions::_setParams( $tz,     array( 'VALUE' => 'DATE-TIME' ));
+        $tz = FALSE;
+      }
+      elseif( is_array( $hour )) {
+        $input['params'] = iCalUtilityFunctions::_setParams( $hour,   array( 'VALUE' => 'DATE-TIME' ));
+        $hour = $min = $sec = $tz = FALSE;
+      }
+      if( isset( $input['params']['TZID'] )) {
+        $tz            = null;
+        $input['params']['VALUE'] = 'DATE-TIME';
+      }
+      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
+      $hitval          = ( !empty( $tz )) ? 7 : 6;
+      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno );
+      $input['value']  = array( 'year'  => $year, 'month' => $month, 'day'   => $day );
+      if( 3 != $parno ) {
+        $input['value']['hour'] = ( $hour ) ? $hour : '0';
+        $input['value']['min']  = ( $min )  ? $min  : '0';
+        $input['value']['sec']  = ( $sec )  ? $sec  : '0';
+        if( !empty( $tz ))
+          $input['value']['tz'] = $tz;
+      }
+    }
+    if( 3 == $parno ) {
+      $input['params']['VALUE'] = 'DATE';
+      unset( $input['value']['tz'] );
+      unset( $input['params']['TZID'] );
+    }
+    elseif( isset( $input['params']['TZID'] ))
+      unset( $input['value']['tz'] );
+    if( $localtime ) unset( $input['value']['tz'], $input['params']['TZID'] );
+    if( isset( $input['value']['tz'] ))
+      $input['value']['tz'] = (string) $input['value']['tz'];
+    if( !empty( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) &&
+      ( !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )))
+      $input['params']['TZID'] = $input['value']['tz'];
+    return $input;
+  }
+/**
+ * convert format for input date (UTC) to internal date with parameters
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.17 - 2008-10-31
+ * @param mixed $year
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param array $params optional
+ * @return array
+ */
+  public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
+    $input = null;
+    if( iCalUtilityFunctions::_isArrayDate( $year )) {
+      $input['value']  = iCalUtilityFunctions::_date_time_array( $year, 7 );
+      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) );
+    }
+    elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
+      $input['value']  = iCalUtilityFunctions::_timestamp2date( $year, 7 );
+      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) );
+    }
+    elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
+      $input['value']  = iCalUtilityFunctions::_date_time_string( $year, 7 );
+      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) );
+    }
+    else {
+      $input['value']  = array( 'year'  => $year
+                              , 'month' => $month
+                              , 'day'   => $day
+                              , 'hour'  => $hour
+                              , 'min'   => $min
+                              , 'sec'   => $sec );
+      $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
+    }
+    $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default
+    if( !isset( $input['value']['hour'] ))
+      $input['value']['hour'] = 0;
+    if( !isset( $input['value']['min'] ))
+      $input['value']['min'] = 0;
+    if( !isset( $input['value']['sec'] ))
+      $input['value']['sec'] = 0;
+    if( !isset( $input['value']['tz'] ) || !iCalUtilityFunctions::_isOffset( $input['value']['tz'] ))
+      $input['value']['tz'] = 'Z';
+    return $input;
+  }
+/**
+ * check index and set (an indexed) content in multiple value array
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.12 - 2011-01-03
+ * @param array $valArr
+ * @param mixed $value
+ * @param array $params
+ * @param array $defaults
+ * @param int $index
+ * @return void
+ */
+  public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) {
+    if( !is_array( $valArr )) $valArr = array();
+    if( $index )
+      $index = $index - 1;
+    elseif( 0 < count( $valArr )) {
+      $keys  = array_keys( $valArr );
+      $index = end( $keys ) + 1;
+    }
+    else
+      $index = 0;
+    $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults ));
+    ksort( $valArr );
+  }
+/**
+ * set input (formatted) parameters- component property attributes
+ *
+ * default parameters can be set, if missing
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 1.x.x - 2007-05-01
+ * @param array $params
+ * @param array $defaults
+ * @return array
+ */
+  public static function _setParams( $params, $defaults=FALSE ) {
+    if( !is_array( $params))
+      $params = array();
+    $input = array();
+    foreach( $params as $paramKey => $paramValue ) {
+      if( is_array( $paramValue )) {
+        foreach( $paramValue as $pkey => $pValue ) {
+          if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 )))
+            $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 ));
+        }
+      }
+      elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 )))
+        $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 ));
+      if( 'VALUE' == strtoupper( $paramKey ))
+        $input['VALUE']                 = strtoupper( $paramValue );
+      else
+        $input[strtoupper( $paramKey )] = $paramValue;
+    }
+    if( is_array( $defaults )) {
+      foreach( $defaults as $paramKey => $paramValue ) {
+        if( !isset( $input[$paramKey] ))
+          $input[$paramKey] = $paramValue;
+      }
+    }
+    return (0 < count( $input )) ? $input : null;
+  }
+/**
+ * step date, return updated date, array and timpstamp
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-10-18
+ * @param array $date, date to step
+ * @param int $timestamp
+ * @param array $step, default array( 'day' => 1 )
+ * @return void
+ */
+  public static function _stepdate( &$date, &$timestamp, $step=array( 'day' => 1 )) {
+    foreach( $step as $stepix => $stepvalue )
+      $date[$stepix] += $stepvalue;
+    $timestamp  = iCalUtilityFunctions::_date2timestamp( $date );
+    $date       = iCalUtilityFunctions::_timestamp2date( $timestamp, 6 );
+    foreach( $date as $k => $v ) {
+      if( ctype_digit( $v ))
+        $date[$k] = (int) $v;
+    }
+  }
+/**
+ * convert timestamp to date array
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-11-01
+ * @param mixed $timestamp
+ * @param int $parno
+ * @return array
+ */
+  public static function _timestamp2date( $timestamp, $parno=6 ) {
+    if( is_array( $timestamp )) {
+      if(( 7 == $parno ) && !empty( $timestamp['tz'] ))
+        $tz = $timestamp['tz'];
+      $timestamp = $timestamp['timestamp'];
+    }
+    $output = array( 'year'  => date( 'Y', $timestamp )
+                   , 'month' => date( 'm', $timestamp )
+                   , 'day'   => date( 'd', $timestamp ));
+    if( 3 != $parno ) {
+             $output['hour'] =  date( 'H', $timestamp );
+             $output['min']  =  date( 'i', $timestamp );
+             $output['sec']  =  date( 's', $timestamp );
+      if( isset( $tz ))
+        $output['tz'] = $tz;
+    }
+    return $output;
+  }
+/**
+ * convert timestamp to duration in array format
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.23 - 2010-10-23
+ * @param int $timestamp
+ * @return array, duration format
+ */
+  function _timestamp2duration( $timestamp ) {
+    $dur         = array();
+    $dur['week'] = (int) floor( $timestamp / ( 7 * 24 * 60 * 60 ));
+    $timestamp   =              $timestamp % ( 7 * 24 * 60 * 60 );
+    $dur['day']  = (int) floor( $timestamp / ( 24 * 60 * 60 ));
+    $timestamp   =              $timestamp % ( 24 * 60 * 60 );
+    $dur['hour'] = (int) floor( $timestamp / ( 60 * 60 ));
+    $timestamp   =              $timestamp % ( 60 * 60 );
+    $dur['min']  = (int) floor( $timestamp / ( 60 ));
+    $dur['sec']  = (int)        $timestamp % ( 60 );
+    return $dur;
+  }
+/**
+ * convert (numeric) local time offset to seconds correcting localtime to GMT
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-10-19
+ * @param string $offset
+ * @return integer
+ */
+  public static function _tz2offset( $tz ) {
+    $tz           = trim( (string) $tz );
+    $offset       = 0;
+    if(((     5  != strlen( $tz )) && ( 7  != strlen( $tz ))) ||
+       ((    '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) ||
+       (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) ||
+           (( 7  == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 ))))
+      return $offset;
+    $hours2sec    = (int) substr( $tz, 1, 2 ) * 3600;
+    $min2sec      = (int) substr( $tz, 3, 2 ) *   60;
+    $sec          = ( 7  == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00';
+    $offset       = $hours2sec + $min2sec + $sec;
+    $offset       = ('-' == substr( $tz, 0, 1 )) ? $offset : -1 * $offset;
+    return $offset;
+  }
+}
+?>
Index: trunk/library/iCalcreator/summary.html
===================================================================
--- trunk/library/iCalcreator/summary.html	(revision 5146)
+++ trunk/library/iCalcreator/summary.html	(revision 5146)
@@ -0,0 +1,263 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
+   "http://www.w3.org/TR/html4/frameset.dtd">
+<html>
+<head>
+<title>iCalcreator 2.8 summary</title>
+<meta name="author"      content="Kjell-Inge Gustafsson - kigkonsult" />
+<meta name="copyright"   content="2007-2011 Kjell-Inge Gustafsson - kigkonsult" />
+<meta name="keywords"    content="ical, calendar, calender, xcal, xml, icalender, rfc2445, vcalender, php, create" />
+<meta name="description" content="iCalcreator" />
+<style type="text/css">
+body {
+  FONT-FAMILY     : "Lucida Grande","Lucida Sans Unicode", "Bitstream Vera Sans", Lucida, Arial, Geneva, Helvetica, sans-serif;
+  FONT-SIZE       : small;
+  MARGIN          : 10px;
+  WIDTH           : 800px;
+}
+h1 {
+  FONT-FAMILY     : "Lucida Grande","Lucida Sans Unicode", "Bitstream Vera Sans", Lucida, Arial, Geneva, Helvetica, sans-serif;
+  FONT-SIZE       : large;
+}
+h2 {
+  FONT-FAMILY     : "Lucida Grande","Lucida Sans Unicode", "Bitstream Vera Sans", Lucida, Arial, Geneva, Helvetica, sans-serif;
+  FONT-SIZE       : large;
+}
+h4 {
+  FONT-FAMILY     : "Lucida Grande","Lucida Sans Unicode", "Bitstream Vera Sans", Lucida, Arial, Geneva, Helvetica, sans-serif;
+  FONT-SIZE       : small;
+  FONT-WEIGHT     : bold;
+}
+.code {
+  FONT-FAMILY     : monospace;
+  FONT-SIZE       : medium;
+  WHITE-SPACE     : pre;
+}
+.comment {
+  FONT-FAMILY     : arial;
+  FONT-SIZE       : small;
+  FONT-STYLE      : italic;
+}
+</style>
+</head>
+<body>
+<h1>iCalcreator 2.8</h1>
+iCalcreator class v2.8<br />
+copyright (c) 2007-2011 Kjell-Inge Gustafsson, kigkonsult<br />
+<a href="http://www.kigkonsult.se/iCalcreator/index.php" title="www.kigkonsult.se/iCalcreator" target="_blank">www.kigkonsult.se/iCalcreator</a><br />
+ical@kigkonsult.se<br />
+<br />
+iCalcreator is a PHP class managing iCal formatted files for non-calendar
+systems like CMS, project management systems and other applications able
+to process calendar information like agendas, tasks, reports, totos,
+journaling data and for communication with calendar systems and applications.
+<br /><br />
+This is a <b>short summary</b> how to use iCalcreator; create, parse, edit, select and output functionality.
+<br /><br />
+iCalcreator is built of a class file with support of a function class file and are calendar component property oriented.
+Development environment is PHP version 5.x but coding is done to meet 4.x backward compatibility and may work.
+<h4>iCal</h4>
+A short iCal description is found at <a href="http://en.wikipedia.org/wiki/ICalendar#Core_object" title="iCalendar From Wikipedia, the free encyclopedia" target="_blank">Wikipedia</a>. If You are not familiar with iCal, read this first!<br />
+Knowledge of calendar protocol rfc2445/rfc2446  is to recommend;<br />
+<a href="http://www.kigkonsult.se/downloads/dl.php?f=rfc2445" title="RFC2445" target="_blank">rfc2445</a>
+ - Internet Calendaring and Scheduling Core Object Specification (iCalendar)<br />
+<a href="http://www.kigkonsult.se/downloads/dl.php?f=rfc2446" title="RFC2446" target="_blank">rfc2446</a>
+ - iCalendar Transport-Independent Interoperability Protocol (iTIP) Scheduling Events, BusyTime, To-dos and Journal Entries <br />
+All functions calls are made as simple as possible BUT (, !!!,) read these rfc's properly!<br />
+<h4>Downloads</h4>
+Download
+<a href="http://www.kigkonsult.se/downloads/index.php#iCalcreator" title="iCalcreator complete manual" target="_blank"><b>complete manual</b></a>
+and
+<a href="http://www.kigkonsult.se/downloads/index.php#iCalcreator" title="iCalcreator coding samples" target="_blank"><b>coding samples</b></a>.
+from <a href="http://www.kigkonsult.se/iCalcreator/index.php" title="iCalcreator" target="_blank">www.kigkonsult.se/iCalcreator</a>.
+<h4>INSTALL</h4>
+Unpack to any folder<br />
+- add this folder to your include-path<br />
+- or unpack to your application-(include)-folder<br />
+Add &quot;require_once [folder/]iCalcreator.class.php;&quot; to your php-script.
+<br />
+<br />
+If using php version 5.1 or higher, the default timezone need to be set/altered, now "Europe/Stockholm",
+line 50 in the iCalcreator.class.php file.
+<br />
+When creating a new calendar/component instance, review config settings.
+
+<h2>CREATE</h2>
+
+<p class="code">require_once( 'iCalcreator.class.php' );
+$config = array( 'unique_id' => 'icaldomain.com' );        // <span class="comment">set Your unique id</span>
+$v = new vcalendar( $config );                             // <span class="comment">create a new calendar instance</span>
+
+$v->setProperty( 'method', 'PUBLISH' );                    // <span class="comment">required of some calendar software</span>
+$v->setProperty( "x-wr-calname", "Calendar Sample" );      // <span class="comment">required of some calendar software</span>
+$v->setProperty( "X-WR-CALDESC", "Calendar Description" ); // <span class="comment">required of some calendar software</span>
+$v->setProperty( "X-WR-TIMEZONE", "Europe/Stockholm" );    // <span class="comment">required of some calendar software</span>
+.. .
+$vevent = & $v->newComponent( 'vevent' );                  // <span class="comment">create an event calendar component</span>
+$vevent->setProperty( 'dtstart', array( 'year'=>2007, 'month'=>4, 'day'=>1, 'hour'=>19, 'min'=>0,  'sec'=>0 ));
+$vevent->setProperty( 'dtend',   array( 'year'=>2007, 'month'=>4, 'day'=>1, 'hour'=>22, 'min'=>30, 'sec'=>0 ));
+$vevent->setProperty( 'LOCATION', 'Central Placa' );       // <span class="comment">property name - case independent</span>
+$vevent->setProperty( 'summary', 'PHP summit' );
+$vevent->setProperty( 'description', 'This is a description' );
+$vevent->setProperty( 'comment', 'This is a comment' );
+$vevent->setProperty( 'attendee', 'attendee1@icaldomain.net' );
+.. .
+$vevent = & $v->newComponent( 'vevent' );                  // <span class="comment">create next event calendar component</span>
+$vevent->setProperty( 'dtstart', '20070401', array('VALUE' => 'DATE'));// <span class="comment">alt. date format, now for an all-day event</span>
+$vevent->setProperty( "organizer" , 'boss@icaldomain.com' );
+$vevent->setProperty( 'summary', 'ALL-DAY event' );
+$vevent->setProperty( 'description', 'This is a description for an all-day event' );
+$vevent->setProperty( 'resources', 'COMPUTER PROJECTOR' );
+$vevent->setProperty( 'rrule', array( 'FREQ' => 'WEEKLY', 'count' => 4));// <span class="comment">weekly, four occasions</span>
+$vevent->parse( 'LOCATION:1CP Conference Room 4350' );     // <span class="comment">supporting parse of strict rfc2445 formatted text</span>
+.. .
+.. .// <span class="comment">all calendar components are described in <a href="http://www.kigkonsult.se/downloads/dl.php?f=rfc2445" title="RFC2445" target="_blank">rfc2445</a></span>
+.. .// <span class="comment">a complete iCalcreator function list (ex. setProperty) in <a href="http://www.kigkonsult.se/downloads/index.php#iCalcreator" title="iCalcreator complete manual" target="_blank">iCalcreator manual</a></span>
+.. .
+$v->returnCalendar();                                      // <span class="comment">redirect calendar file to browser</span>
+</p>
+<h2>PARSE</h2>
+<p class="code">require_once( 'iCalcreator.class.php' );
+$config = array( 'unique_id', 'icaldomain.com' );          // <span class="comment">set Your unique id, required if any component UID is missing</span>
+$v = new vcalendar( $config );                             // <span class="comment">create a new calendar instance</span>
+
+ /* start parse of local file */
+$config = array( 'directory' => 'calendar', 'filename' => 'file.ics' );
+$v->setConfig( $config );                                  // <span class="comment">set directory and file name</span>
+$v->parse();
+
+ /* start parse of remote file */
+$v->setConfig( 'url', 'http://www.aDomain.net/file.ics' ); // <span class="comment">iCalcreator also support parse from or write to remote files</span>
+$v->parse();
+
+$v->setProperty( 'method', 'PUBLISH' );                    // <span class="comment">required of some calendar software</span>
+$v->setProperty( "x-wr-calname", "Calendar Sample" );      // <span class="comment">required of some calendar software</span>
+$v->setProperty( "X-WR-CALDESC", "Calendar Description" ); // <span class="comment">required of some calendar software</span>
+$v->setProperty( "X-WR-TIMEZONE", "Europe/Stockholm" );    // <span class="comment">required of some calendar software</span>
+
+.. .
+$v->sort();                                                // <span class="comment">ensure start date order</span>
+.. .
+</p>
+<h2>EDIT</h2>
+<p class="code">require_once( 'iCalcreator.class.php' );
+$config = array( 'unique_id' => 'icaldomain.com', 'directory' => 'calendar', 'filename' => 'file.ics' );
+                                                           // <span class="comment">set Your unique id, import directory and file name</span>
+$v = new vcalendar( $config );                             // <span class="comment">create a new calendar instance</span>
+
+$v->parse();
+
+$v->setProperty( 'method', 'PUBLISH' );                    // <span class="comment">required of some calendar software</span>
+$v->setProperty( "x-wr-calname", "Calendar Sample" );      // <span class="comment">required of some calendar software</span>
+$v->setProperty( "X-WR-CALDESC", "Calendar Description" ); // <span class="comment">required of some calendar software</span>
+$v->setProperty( "X-WR-TIMEZONE", "Europe/Stockholm" );    // <span class="comment">required of some calendar software</span>
+
+while( $vevent = $v->getComponent( 'vevent' )) {           // <span class="comment">read events, one by one</span>
+  $uid = $vevent->getProperty( 'uid' );                    // <span class="comment">uid required, one occurence (unique id/key for component)</span>
+  .. .
+  $dtstart = $vevent->getProperty( 'dtstart' );            // <span class="comment">dtstart required, one occurence</span>
+  .. .
+  if( $description = $vevent->getProperty( 'description', 1 )) { // <span class="comment">description optional, first occurence</span>
+    .. .                                                   // <span class="comment">edit the description</span>
+    $vevent->setProperty( 'description', $description, FALSE, 1 ); // <span class="comment">update/replace the description</span>
+  }
+  while( $comment = $vevent->getProperty( 'comment' )) {   // <span class="comment">comment optional, may occur more than once </span>
+    .. .                                                   // <span class="comment">manage comments</span>
+  }
+  .. .
+  while( $vevent->deleteProperty( 'attendee' ))
+    continue;                                              // <span class="comment">remove all ATTENDEE properties .. .</span>
+  .. .
+  $v->setComponent ( $vevent, $uid );                      // <span class="comment">update/replace event in calendar with <b>uid</b> as key </span>
+}
+.. .
+.. .// <span class="comment">a complete iCalcreator function list (ex. getProperty, deleteProperty) in <a href="http://www.kigkonsult.se/downloads/index.php" title="iCalcreator complete manual" target="_blank">iCalcreator manual</a></span>
+.. .
+</p>
+<h2>SELECT</h2>
+<p class="code">require_once( 'iCalcreator.class.php' );
+$config = array( 'unique_id' => 'icaldomain.com' );        // <span class="comment">set Your unique id</span>
+$v = new vcalendar( $config );                             // <span class="comment">create a new calendar instance</span>
+
+$v->setConfig( 'url', 'http://www.aDomain.net/file.ics' ); // <span class="comment">iCalcreator also support remote files</span>
+$v->parse();
+$v->sort();                                                // <span class="comment">ensure start date order</span>
+
+$v->setProperty( 'method', 'PUBLISH' );                    // <span class="comment">required of some calendar software</span>
+$v->setProperty( "x-wr-calname", "Calendar Sample" );      // <span class="comment">required of some calendar software</span>
+$v->setProperty( "X-WR-CALDESC", "Calendar Description" ); // <span class="comment">required of some calendar software</span>
+$v->setProperty( "X-WR-TIMEZONE", "Europe/Stockholm" );    // <span class="comment">required of some calendar software</span>
+
+$eventArray = $v->selectComponents();                      // <span class="comment">select components occuring <b>today</b></span>
+                                                           // <span class="comment">(including components with recurrence pattern)</span>
+foreach( $eventArray as $year => $yearArray) {
+ foreach( $yearArray as $month => $monthArray ) {
+  foreach( $monthArray as $day => $dailyEventsArray ) {
+   foreach( $dailyEventsArray as $vevent ) {
+    $currddate = $event->getProperty( 'x-current-dtstart' );
+                                                           // <span class="comment">if member of a recurrence set (2nd occurence etc)</span>
+                                                           // <span class="comment">returns array( 'x-current-dtstart'</span>
+                                                           // <span class="comment">                  , &lt;(string) date(&quot;Y-m-d [H:i:s][timezone/UTC offset]&quot;)&gt;)</span>
+    $dtstart = $vevent->getProperty( 'dtstart' );          // <span class="comment">dtstart required, one occurence, (orig. start date)</span>
+    $summary = $vevent->getProperty( 'summary' );
+    $description = $vevent->getProperty( 'description' );
+    .. .
+    .. .
+</p>
+<h2>OUTPUT</h2>
+<p class="code">require_once( 'iCalcreator.class.php' );
+$config = array( 'unique_id' => 'icaldomain.com' );        // <span class="comment">set Your unique id</span>
+$v = new vcalendar( $config );                             // <span class="comment">create a new calendar instance</span>
+
+$v->setProperty( 'method', 'PUBLISH' );                    // <span class="comment">required of some calendar software</span>
+$v->setProperty( "x-wr-calname", "Calendar Sample" );      // <span class="comment">required of some calendar software</span>
+$v->setProperty( "X-WR-CALDESC", "Calendar Description" ); // <span class="comment">required of some calendar software</span>
+$v->setProperty( "X-WR-TIMEZONE", "Europe/Stockholm" );    // <span class="comment">required of some calendar software</span>
+.. .
+.. .// <span class="comment">parse calendar file(s) and/or edit/create calendar components.. .</span>
+.. .
+</p>
+<h4> // opt 1</h4>
+<p class="code">.. .
+$v->returnCalendar();                                      // <span class="comment">redirect calendar file to browser</span>
+</p>
+<h4> // opt 2</h4>
+<p class="code">.. .
+$config = array( 'directory' => 'depot', 'filename' => 'calendar.ics' );
+$v->setConfig( $config );                                  // <span class="comment">set output directory and file name</span>
+$v->saveCalendar();                                        // <span class="comment">save calendar to (local) file</span>
+</p>
+<h4> // opt 3</h4>
+<p class="code">.. .
+$config = array( 'url' => 'http://www.aDomain.net/file.ics' );
+$v->setConfig( $config );                                  // <span class="comment">set output url</span>
+$v->saveCalendar();                                        // <span class="comment">save calendar to remote url</span>
+</p>
+
+<h2>COPYRIGHT AND LICENSE</h2>
+
+<h4>Copyright</h4>
+iCalcreator class<br />
+copyright (c) 2007-2011 Kjell-Inge Gustafsson, kigkonsult<br />
+<a href="http://www.kigkonsult.se/iCalcreator/index.php" title="www.kigkonsult.se/iCalcreator" target="_blank">www.kigkonsult.se/iCalcreator</a><br />
+ical@kigkonsult.se<br />
+
+<h4>License</h4>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+<br /><br />
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+<br /><br />
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+or download it <a href="http://www.kigkonsult.se/downloads/dl.php?f=LGPL" target="_blank">here</a>.
+<br />
+<br />
+</body>
+</html>
Index: trunk/library/iCalcreator/iCalcreator.class.php
===================================================================
--- trunk/library/iCalcreator/iCalcreator.class.php	(revision 5146)
+++ trunk/library/iCalcreator/iCalcreator.class.php	(revision 5146)
@@ -0,0 +1,6529 @@
+<?php
+/*********************************************************************************/
+/**
+ * iCalcreator class v2.8
+ * copyright (c) 2007-2011 Kjell-Inge Gustafsson kigkonsult
+ * www.kigkonsult.se/iCalcreator/index.php
+ * ical@kigkonsult.se
+ *
+ * Description:
+ * This file is a PHP implementation of RFC 2445.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/*********************************************************************************/
+/*********************************************************************************/
+/*         A little setup                                                        */
+/*********************************************************************************/
+            /* your local language code */
+// define( 'ICAL_LANG', 'sv' );
+            // alt. autosetting
+/*
+$langstr     = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
+$pos         = strpos( $langstr, ';' );
+if ($pos   !== false) {
+  $langstr   = substr( $langstr, 0, $pos );
+  $pos       = strpos( $langstr, ',' );
+  if ($pos !== false) {
+    $pos     = strpos( $langstr, ',' );
+    $langstr = substr( $langstr, 0, $pos );
+  }
+  define( 'ICAL_LANG', $langstr );
+}
+*/
+/*********************************************************************************/
+/*         only for phpversion 5.1 and later,                                    */
+/*         date management, default timezone setting                             */
+/*         since 2.6.36 - 2010-12-31 */
+if( substr( phpversion(), 0, 3) >= '5.1' )
+  // && ( 'UTC' == date_default_timezone_get()))
+  date_default_timezone_set( 'America/Sao_paulo' );
+/*********************************************************************************/
+/*         since 2.6.22 - 2010-09-25, do NOT remove!!                            */
+require_once ( $_SESSION['rootPath'].'/library/iCalcreator/iCalUtilityFunctions.class.php' );
+/*********************************************************************************/
+/*         version string, do NOT remove!!                                       */
+define( 'ICALCREATOR_VERSION', 'iCalcreator 2.8' );
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ * vcalendar class
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.2.13 - 2007-12-30
+ */
+class vcalendar {
+            //  calendar property variables
+  var $calscale;
+  var $method;
+  var $prodid;
+  var $version;
+  var $xprop;
+            //  container for calendar components
+  var $components;
+            //  component config variables
+  var $allowEmpty;
+  var $unique_id;
+  var $language;
+  var $directory;
+  var $filename;
+  var $url;
+  var $delimiter;
+  var $nl;
+  var $format;
+            //  component internal variables
+  var $attributeDelimiter;
+  var $valueInit;
+            //  component xCal declaration container
+  var $xcaldecl;
+/*
+ * constructor for calendar object
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-11-20
+ * @param array $config
+ * @return void
+ */
+  function vcalendar ( $config = array()) {
+    $this->_makeVersion();
+    $this->calscale   = null;
+    $this->method     = null;
+    $this->_makeUnique_id();
+    $this->prodid     = null;
+    $this->xprop      = array();
+    $this->language   = null;
+    $this->directory  = null;
+    $this->filename   = null;
+    $this->url        = null;
+/**
+ *   language = <Text identifying a language, as defined in [RFC 1766]>
+ */
+    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
+                                          $config['language']   = ICAL_LANG;
+    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
+    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
+    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
+    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
+    $this->setConfig( $config );
+
+    $this->xcaldecl   = array();
+    $this->components = array();
+  }
+/*********************************************************************************/
+/**
+ * Property Name: CALSCALE
+ */
+/**
+ * creates formatted output for calendar property calscale
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createCalscale() {
+    if( empty( $this->calscale )) return FALSE;
+    switch( $this->format ) {
+      case 'xcal':
+        return ' calscale="'.$this->calscale.'"'.$this->nl;
+        break;
+      default:
+        return 'CALSCALE:'.$this->calscale.$this->nl;
+        break;
+    }
+  }
+/**
+ * set calendar property calscale
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @param string $value
+ * @return void
+ */
+  function setCalscale( $value ) {
+    if( empty( $value )) return FALSE;
+    $this->calscale = $value;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: METHOD
+ */
+/**
+ * creates formatted output for calendar property method
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 0.9.7 - 2006-11-20
+ * @return string
+ */
+  function createMethod() {
+    if( empty( $this->method )) return FALSE;
+    switch( $this->format ) {
+      case 'xcal':
+        return ' method="'.$this->method.'"'.$this->nl;
+        break;
+      default:
+        return 'METHOD:'.$this->method.$this->nl;
+        break;
+    }
+  }
+/**
+ * set calendar property method
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-20-23
+ * @param string $value
+ * @return bool
+ */
+  function setMethod( $value ) {
+    if( empty( $value )) return FALSE;
+    $this->method = $value;
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: PRODID
+ *
+ *  The identifier is RECOMMENDED to be the identical syntax to the
+ * [RFC 822] addr-spec. A good method to assure uniqueness is to put the
+ * domain name or a domain literal IP address of the host on which.. .
+ */
+/**
+ * creates formatted output for calendar property prodid
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 0.9.7 - 2006-11-20
+ * @return string
+ */
+  function createProdid() {
+    if( !isset( $this->prodid ))
+      $this->_makeProdid();
+    switch( $this->format ) {
+      case 'xcal':
+        return ' prodid="'.$this->prodid.'"'.$this->nl;
+        break;
+      default:
+        return 'PRODID:'.$this->prodid.$this->nl;
+        break;
+    }
+  }
+/**
+ * make default value for calendar prodid
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.8 - 2009-12-30
+ * @return void
+ */
+  function _makeProdid() {
+    $this->prodid  = '-//'.$this->unique_id.'//NONSGML kigkonsult.se '.ICALCREATOR_VERSION.'//'.strtoupper( $this->language );
+  }
+/**
+ * Conformance: The property MUST be specified once in an iCalendar object.
+ * Description: The vendor of the implementation SHOULD assure that this
+ * is a globally unique identifier; using some technique such as an FPI
+ * value, as defined in [ISO 9070].
+ */
+/**
+ * make default unique_id for calendar prodid
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 0.3.0 - 2006-08-10
+ * @return void
+ */
+  function _makeUnique_id() {
+    $this->unique_id  = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
+  }
+/*********************************************************************************/
+/**
+ * Property Name: VERSION
+ *
+ * Description: A value of "2.0" corresponds to this memo.
+ */
+/**
+ * creates formatted output for calendar property version
+
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 0.9.7 - 2006-11-20
+ * @return string
+ */
+  function createVersion() {
+    if( empty( $this->version ))
+      $this->_makeVersion();
+    switch( $this->format ) {
+      case 'xcal':
+        return ' version="'.$this->version.'"'.$this->nl;
+        break;
+      default:
+        return 'VERSION:'.$this->version.$this->nl;
+        break;
+    }
+  }
+/**
+ * set default calendar version
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 0.3.0 - 2006-08-10
+ * @return void
+ */
+  function _makeVersion() {
+    $this->version = '2.0';
+  }
+/**
+ * set calendar version
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @param string $value
+ * @return void
+ */
+  function setVersion( $value ) {
+    if( empty( $value )) return FALSE;
+    $this->version = $value;
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: x-prop
+ */
+/**
+ * creates formatted output for calendar property x-prop, iCal format only
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.11 - 2008-11-03
+ * @return string
+ */
+  function createXprop() {
+    if( 'xcal' == $this->format )
+      return false;
+    if( 0 >= count( $this->xprop ))
+      return;
+    $output = null;
+    $toolbox = new calendarComponent();
+    $toolbox->setConfig( 'language', $this->getConfig( 'language' ));
+    $toolbox->setConfig( 'nl',       $this->getConfig( 'nl' ));
+    $toolbox->_createFormat(         $this->getConfig( 'format' ));
+    foreach( $this->xprop as $label => $xpropPart ) {
+      if( empty( $xpropPart['value'] )) {
+        $output  .= $toolbox->_createElement( $label );
+        continue;
+      }
+      $attributes = $toolbox->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
+      if( is_array( $xpropPart['value'] )) {
+        foreach( $xpropPart['value'] as $pix => $theXpart )
+          $xpropPart['value'][$pix] = $toolbox->_strrep( $theXpart );
+        $xpropPart['value']  = implode( ',', $xpropPart['value'] );
+      }
+      else
+        $xpropPart['value'] = $toolbox->_strrep( $xpropPart['value'] );
+      $output    .= $toolbox->_createElement( $label, $attributes, $xpropPart['value'] );
+    }
+    return $output;
+  }
+/**
+ * set calendar property x-prop
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.22 - 2010-09-25
+ * @param string $label
+ * @param string $value
+ * @param array $params optional
+ * @return bool
+ */
+  function setXprop( $label, $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    if( empty( $label )) return FALSE;
+    $xprop           = array( 'value' => $value );
+    $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
+    if( !is_array( $this->xprop )) $this->xprop = array();
+    $this->xprop[strtoupper( $label )] = $xprop;
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * delete calendar property value
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.4 - 2011-01-05
+ * @param mixed $propName, bool FALSE => X-property
+ * @param int $propix, optional, if specific property is wanted in case of multiply occurences
+ * @return bool, if successfull delete
+ */
+  function deleteProperty( $propName=FALSE, $propix=FALSE ) {
+    $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
+    if( !$propix )
+      $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
+    $this->propdelix[$propName] = --$propix;
+    $return = FALSE;
+    switch( $propName ) {
+      case 'CALSCALE':
+        if( isset( $this->calscale )) {
+          $this->calscale = null;
+          $return = TRUE;
+        }
+        break;
+      case 'METHOD':
+        if( isset( $this->method )) {
+          $this->method   = null;
+          $return = TRUE;
+        }
+        break;
+      default:
+        $reduced = array();
+        if( $propName != 'X-PROP' ) {
+          if( !isset( $this->xprop[$propName] )) return FALSE;
+          foreach( $this->xprop as $k => $a ) {
+            if(( $k != $propName ) && !empty( $a ))
+              $reduced[$k] = $a;
+          }
+        }
+        else {
+          if( count( $this->xprop ) <= $propix )  return FALSE;
+          $xpropno = 0;
+          foreach( $this->xprop as $xpropkey => $xpropvalue ) {
+            if( $propix != $xpropno )
+              $reduced[$xpropkey] = $xpropvalue;
+            $xpropno++;
+          }
+        }
+        $this->xprop = $reduced;
+        if( empty( $this->xprop ))
+          return FALSE;
+        return TRUE;
+    }
+    return $return;
+  }
+/**
+ * get calendar property value/params
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-02
+ * @param string $propName, optional
+ * @param int $propix, optional, if specific property is wanted in case of multiply occurences
+ * @param bool $inclParam=FALSE
+ * @return mixed
+ */
+  function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) {
+    $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
+    if( 'X-PROP' == $propName ) {
+      if( !$propix )
+        $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
+      $this->propix[$propName] = --$propix;
+    }
+    switch( $propName ) {
+      case 'CALSCALE':
+        return ( !empty( $this->calscale )) ? $this->calscale : null;
+        break;
+      case 'METHOD':
+        return ( !empty( $this->method )) ? $this->method : null;
+        break;
+      case 'PRODID':
+        if( empty( $this->prodid ))
+          $this->_makeProdid();
+        return $this->prodid;
+        break;
+      case 'VERSION':
+        return ( !empty( $this->version )) ? $this->version : null;
+        break;
+      default:
+        if( $propName != 'X-PROP' ) {
+          if( !isset( $this->xprop[$propName] )) return FALSE;
+          return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
+                                : array( $propName, $this->xprop[$propName]['value'] );
+        }
+        else {
+          if( empty( $this->xprop )) return FALSE;
+          $xpropno = 0;
+          foreach( $this->xprop as $xpropkey => $xpropvalue ) {
+            if( $propix == $xpropno )
+              return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
+                                    : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
+            else
+              $xpropno++;
+          }
+          return FALSE; // not found ??
+        }
+    }
+    return FALSE;
+  }
+/**
+ * general vcalendar property setting
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.2.13 - 2007-11-04
+ * @param mixed $args variable number of function arguments,
+ *                    first argument is ALWAYS component name,
+ *                    second ALWAYS component value!
+ * @return bool
+ */
+  function setProperty () {
+    $numargs    = func_num_args();
+    if( 1 > $numargs )
+      return FALSE;
+    $arglist    = func_get_args();
+    $arglist[0] = strtoupper( $arglist[0] );
+    switch( $arglist[0] ) {
+      case 'CALSCALE':
+        return $this->setCalscale( $arglist[1] );
+      case 'METHOD':
+        return $this->setMethod( $arglist[1] );
+      case 'VERSION':
+        return $this->setVersion( $arglist[1] );
+      default:
+        if( !isset( $arglist[1] )) $arglist[1] = null;
+        if( !isset( $arglist[2] )) $arglist[2] = null;
+        return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
+    }
+    return FALSE;
+  }
+/*********************************************************************************/
+/**
+ * get vcalendar config values or * calendar components
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-11-27
+ * @param mixed $config
+ * @return value
+ */
+  function getConfig( $config = FALSE ) {
+    if( !$config ) {
+      $return = array();
+      $return['ALLOWEMPTY']  = $this->getConfig( 'ALLOWEMPTY' );
+      $return['DELIMITER']   = $this->getConfig( 'DELIMITER' );
+      $return['DIRECTORY']   = $this->getConfig( 'DIRECTORY' );
+      $return['FILENAME']    = $this->getConfig( 'FILENAME' );
+      $return['DIRFILE']     = $this->getConfig( 'DIRFILE' );
+      $return['FILESIZE']    = $this->getConfig( 'FILESIZE' );
+      $return['FORMAT']      = $this->getConfig( 'FORMAT' );
+      if( FALSE !== ( $lang  = $this->getConfig( 'LANGUAGE' )))
+        $return['LANGUAGE']  = $lang;
+      $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
+      $return['UNIQUE_ID']   = $this->getConfig( 'UNIQUE_ID' );
+      if( FALSE !== ( $url   = $this->getConfig( 'URL' )))
+        $return['URL']       = $url;
+      return $return;
+    }
+    switch( strtoupper( $config )) {
+      case 'ALLOWEMPTY':
+        return $this->allowEmpty;
+        break;
+      case 'COMPSINFO':
+        unset( $this->compix );
+        $info = array();
+        foreach( $this->components as $cix => $component ) {
+          if( empty( $component )) continue;
+          unset( $component->propix );
+          $info[$cix]['ordno'] = $cix + 1;
+          $info[$cix]['type']  = $component->objName;
+          $info[$cix]['uid']   = $component->getProperty( 'uid' );
+          $info[$cix]['props'] = $component->getConfig( 'propinfo' );
+          $info[$cix]['sub']   = $component->getConfig( 'compsinfo' );
+          unset( $component->propix );
+        }
+        return $info;
+        break;
+      case 'DELIMITER':
+        return $this->delimiter;
+        break;
+      case 'DIRECTORY':
+        if( empty( $this->directory ))
+          $this->directory = '.';
+        return $this->directory;
+        break;
+      case 'DIRFILE':
+        return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' );
+        break;
+      case 'FILEINFO':
+        return array( $this->getConfig( 'directory' )
+                    , $this->getConfig( 'filename' )
+                    , $this->getConfig( 'filesize' ));
+        break;
+      case 'FILENAME':
+        if( empty( $this->filename )) {
+          if( 'xcal' == $this->format )
+            $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. .
+          else
+            $this->filename = date( 'YmdHis' ).'.ics';
+        }
+        return $this->filename;
+        break;
+      case 'FILESIZE':
+        $size    = 0;
+        if( empty( $this->url )) {
+          $dirfile = $this->getConfig( 'dirfile' );
+          if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile ))))
+            $size = 0;
+          clearstatcache();
+        }
+        return $size;
+        break;
+      case 'FORMAT':
+        return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal';
+        break;
+      case 'LANGUAGE':
+         /* get language for calendar component as defined in [RFC 1766] */
+        return $this->language;
+        break;
+      case 'NL':
+      case 'NEWLINECHAR':
+        return $this->nl;
+        break;
+      case 'UNIQUE_ID':
+        return $this->unique_id;
+        break;
+      case 'URL':
+        if( !empty( $this->url ))
+          return $this->url;
+        else
+          return FALSE;
+        break;
+    }
+  }
+/**
+ * general vcalendar config setting
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-12-12
+ * @param mixed  $config
+ * @param string $value
+ * @return void
+ */
+  function setConfig( $config, $value = FALSE) {
+    if( is_array( $config )) {
+      foreach( $config as $cKey => $cValue ) {
+        if( FALSE === $this->setConfig( $cKey, $cValue ))
+          return FALSE;
+      }
+      return TRUE;
+    }
+    $res = FALSE;
+    switch( strtoupper( $config )) {
+      case 'ALLOWEMPTY':
+        $this->allowEmpty = $value;
+        $subcfg  = array( 'ALLOWEMPTY' => $value );
+        $res = TRUE;
+        break;
+      case 'DELIMITER':
+        $this->delimiter = $value;
+        return TRUE;
+        break;
+      case 'DIRECTORY':
+        $value   = trim( $value );
+        $del     = $this->getConfig('delimiter');
+        if( $del == substr( $value, ( 0 - strlen( $del ))))
+          $value = substr( $value, 0, ( strlen( $value ) - strlen( $del )));
+        if( is_dir( $value )) {
+            /* local directory */
+          clearstatcache();
+          $this->directory = $value;
+          $this->url       = null;
+          return TRUE;
+        }
+        else
+          return FALSE;
+        break;
+      case 'FILENAME':
+        $value   = trim( $value );
+        if( !empty( $this->url )) {
+            /* remote directory+file -> URL */
+          $this->filename = $value;
+          return TRUE;
+        }
+        $dirfile = $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$value;
+        if( file_exists( $dirfile )) {
+            /* local file exists */
+          if( is_readable( $dirfile ) || is_writable( $dirfile )) {
+            clearstatcache();
+            $this->filename = $value;
+            return TRUE;
+          }
+          else
+            return FALSE;
+        }
+        elseif( is_readable($this->getConfig( 'directory' ) ) || is_writable( $this->getConfig( 'directory' ) )) {
+            /* read- or writable directory */
+          $this->filename = $value;
+          return TRUE;
+        }
+        else
+          return FALSE;
+        break;
+      case 'FORMAT':
+        $value   = trim( strtolower( $value ));
+        if( 'xcal' == $value ) {
+          $this->format             = 'xcal';
+          $this->attributeDelimiter = $this->nl;
+          $this->valueInit          = null;
+        }
+        else {
+          $this->format             = null;
+          $this->attributeDelimiter = ';';
+          $this->valueInit          = ':';
+        }
+        $subcfg  = array( 'FORMAT' => $value );
+        $res = TRUE;
+        break;
+      case 'LANGUAGE':
+         // set language for calendar component as defined in [RFC 1766]
+        $value   = trim( $value );
+        $this->language = $value;
+        $subcfg  = array( 'LANGUAGE' => $value );
+        $res = TRUE;
+        break;
+      case 'NL':
+      case 'NEWLINECHAR':
+        $this->nl = $value;
+        $subcfg  = array( 'NL' => $value );
+        $res = TRUE;
+        break;
+      case 'UNIQUE_ID':
+        $value   = trim( $value );
+        $this->unique_id = $value;
+        $subcfg  = array( 'UNIQUE_ID' => $value );
+        $res = TRUE;
+        break;
+      case 'URL':
+            /* remote file - URL */
+        $value     = trim( $value );
+        $value     = str_replace( 'HTTP://',   'http://', $value );
+        $value     = str_replace( 'WEBCAL://', 'http://', $value );
+        $value     = str_replace( 'webcal://', 'http://', $value );
+        $this->url = $value;
+        $this->directory = null;
+        $parts     = pathinfo( $value );
+        return $this->setConfig( 'filename',  $parts['basename'] );
+        break;
+      default:  // any unvalid config key.. .
+        return TRUE;
+    }
+    if( !$res ) return FALSE;
+    if( isset( $subcfg ) && !empty( $this->components )) {
+      foreach( $subcfg as $cfgkey => $cfgvalue ) {
+        foreach( $this->components as $cix => $component ) {
+          $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE );
+          if( !$res )
+            break 2;
+          $this->components[$cix] = $component->copy(); // PHP4 compliant
+        }
+      }
+    }
+    return $res;
+  }
+/*********************************************************************************/
+/**
+ * add calendar component to container
+ *
+ * alias to setComponent
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 1.x.x - 2007-04-24
+ * @param object $component calendar component
+ * @return void
+ */
+  function addComponent( $component ) {
+    $this->setComponent( $component );
+  }
+/**
+ * delete calendar component from container
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.10 - 2008-08-05
+ * @param mixed $arg1 ordno / component type / component uid
+ * @param mixed $arg2 optional, ordno if arg1 = component type
+ * @return void
+ */
+  function deleteComponent( $arg1, $arg2=FALSE  ) {
+    $argType = $index = null;
+    if ( ctype_digit( (string) $arg1 )) {
+      $argType = 'INDEX';
+      $index   = (int) $arg1 - 1;
+    }
+    elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
+      $argType = strtolower( $arg1 );
+      $index   = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
+    }
+    $cix1dC = 0;
+    foreach ( $this->components as $cix => $component) {
+      if( empty( $component )) continue;
+      unset( $component->propix );
+      if(( 'INDEX' == $argType ) && ( $index == $cix )) {
+        unset( $this->components[$cix] );
+        return TRUE;
+      }
+      elseif( $argType == $component->objName ) {
+        if( $index == $cix1dC ) {
+          unset( $this->components[$cix] );
+          return TRUE;
+        }
+        $cix1dC++;
+      }
+      elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
+        unset( $this->components[$cix] );
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+/**
+ * get calendar component from container
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.10 - 2008-08-06
+ * @param mixed $arg1 optional, ordno/component type/ component uid
+ * @param mixed $arg2 optional, ordno if arg1 = component type
+ * @return object
+ */
+  function getComponent( $arg1=FALSE, $arg2=FALSE ) {
+    $index = $argType = null;
+    if ( !$arg1 ) {
+      $argType = 'INDEX';
+      $index   = $this->compix['INDEX'] =
+        ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
+    }
+    elseif ( ctype_digit( (string) $arg1 )) {
+      $argType = 'INDEX';
+      $index   = (int) $arg1;
+      unset( $this->compix );
+    }
+    elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
+      unset( $this->compix['INDEX'] );
+      $argType = strtolower( $arg1 );
+      if( !$arg2 )
+        $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
+      else
+        $index = (int) $arg2;
+    }
+    $index  -= 1;
+    $ckeys =  array_keys( $this->components );
+    if( !empty( $index) && ( $index > end(  $ckeys )))
+      return FALSE;
+    $cix1gC = 0;
+    foreach ( $this->components as $cix => $component) {
+      if( empty( $component )) continue;
+      unset( $component->propix );
+      if(( 'INDEX' == $argType ) && ( $index == $cix ))
+        return $component->copy();
+      elseif( $argType == $component->objName ) {
+         if( $index == $cix1gC )
+           return $component->copy();
+         $cix1gC++;
+      }
+      elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
+        unset( $component->propix );
+        return $component->copy();
+      }
+    }
+            /* not found.. . */
+    unset( $this->compix );
+    return FALSE;
+  }
+/**
+ * create new calendar component, already included within calendar
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.33 - 2011-01-03
+ * @param string $compType component type
+ * @return object (reference)
+ */
+  function & newComponent( $compType ) {
+    $config = $this->getConfig();
+    $keys   = array_keys( $this->components );
+    $ix     = end( $keys) + 1;
+    switch( strtoupper( $compType )) {
+      case 'EVENT':
+      case 'VEVENT':
+        $this->components[$ix] = new vevent( $config );
+        break;
+      case 'TODO':
+      case 'VTODO':
+        $this->components[$ix] = new vtodo( $config );
+        break;
+      case 'JOURNAL':
+      case 'VJOURNAL':
+        $this->components[$ix] = new vjournal( $config );
+        break;
+      case 'FREEBUSY':
+      case 'VFREEBUSY':
+        $this->components[$ix] = new vfreebusy( $config );
+        break;
+      case 'TIMEZONE':
+      case 'VTIMEZONE':
+        array_unshift( $this->components, new vtimezone( $config ));
+        $ix = 0;
+        break;
+      default:
+        return FALSE;
+    }
+    return $this->components[$ix];
+  }
+/**
+ * select components from calendar on date basis
+ *
+ * Ensure DTSTART is set for every component.
+ * No date controls occurs.
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.22 - 2010-10-21
+ * @param int $startY optional,  start Year, default current Year
+ * @param int $startM optional,  start Month, default current Month
+ * @param int $startD optional,  start Day, default current Day
+ * @param int $endY optional,    end Year, default $startY
+ * @param int $endY optional,    end Month, default $startM
+ * @param int $endY optional,    end Day, default $startD
+ * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s)
+ * @param bool $flat optional,   FALSE (default) => output : array[Year][Month][Day][]
+ *                               TRUE => output : array[] (ignores split)
+ * @param bool $any optional,    TRUE (default) - select component that take place within period
+ *                               FALSE - only components that starts within period
+ * @param bool $split optional,  TRUE (default) - one component copy every day it take place during the
+ *                                       period (implies flat=FALSE)
+ *                               FALSE - one occurance of component only in output array
+ * @return array or FALSE
+ */
+  function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) {
+            /* check  if empty calendar */
+    if( 0 >= count( $this->components )) return FALSE;
+            /* check default dates */
+    if( !$startY ) $startY = date( 'Y' );
+    if( !$startM ) $startM = date( 'm' );
+    if( !$startD ) $startD = date( 'd' );
+    $startDate = mktime( 0, 0, 0, $startM, $startD, $startY );
+    if( !$endY )   $endY   = $startY;
+    if( !$endM )   $endM   = $startM;
+    if( !$endD )   $endD   = $startD;
+    $endDate   = mktime( 23, 59, 59, $endM, $endD, $endY );
+            /* check component types */
+    $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' );
+    if( is_array( $cType )) {
+      foreach( $cType as $cix => $theType ) {
+        $cType[$cix] = $theType = strtolower( $theType );
+        if( !in_array( $theType, $validTypes ))
+          $cType[$cix] = 'vevent';
+      }
+      $cType = array_unique( $cType );
+    }
+    elseif( !empty( $cType )) {
+      $cType = strtolower( $cType );
+      if( !in_array( $cType, $validTypes ))
+        $cType = array( 'vevent' );
+      else
+        $cType = array( $cType );
+    }
+    else
+      $cType = $validTypes;
+    if( 0 >= count( $cType ))
+      $cType = $validTypes;
+            /* iterate components */
+    $result = array();
+    foreach ( $this->components as $cix => $component ) {
+      if( empty( $component )) continue;
+      unset( $component->propix, $start );
+            /* deselect unvalid type components */
+      if( !in_array( $component->objName, $cType )) continue;
+      $start = $component->getProperty( 'dtstart' );
+            /* select due when dtstart is missing */
+      if( empty( $start ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $start = $component->getProperty( 'due' ))))
+        continue;
+      $dtendExist = $dueExist = $durationExist = $endAllDayEvent = FALSE;
+      unset( $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $workstart, $workend, $endDateFormat ); // clean up
+      $startWdate = iCalUtilityFunctions::_date2timestamp( $start );
+      $startDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
+            /* get end date from dtend/due/duration properties */
+      $end = $component->getProperty( 'dtend' );
+      if( !empty( $end )) {
+        $dtendExist = TRUE;
+        $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
+      }
+   // if( !empty($end))  echo 'selectComp 1 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
+      if( empty( $end ) && ( $component->objName == 'vtodo' )) {
+        $end = $component->getProperty( 'due' );
+        if( !empty( $end )) {
+          $dueExist = TRUE;
+          $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
+        }
+   // if( !empty($end))  echo 'selectComp 2 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
+      }
+      if( !empty( $end ) && !isset( $end['hour'] )) {
+          /* a DTEND without time part regards an event that ends the day before,
+             for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */
+        $endAllDayEvent = TRUE;
+        $endWdate = mktime( 23, 59, 59, $end['month'], ($end['day'] - 1), $end['year'] );
+        $end['year']  = date( 'Y', $endWdate );
+        $end['month'] = date( 'm', $endWdate );
+        $end['day']   = date( 'd', $endWdate );
+        $end['hour']  = 23;
+        $end['min']   = $end['sec'] = 59;
+   // if( !empty($end))  echo 'selectComp 3 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
+      }
+      if( empty( $end )) {
+        $end = $component->getProperty( 'duration', FALSE, FALSE, TRUE );// in dtend (array) format
+        if( !empty( $end ))
+          $durationExist = TRUE;
+          $endDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
+   // if( !empty($end))  echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
+      }
+      if( empty( $end )) { // assume one day duration if missing end date
+        $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
+   // if( isset($end))  echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
+      }
+      $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
+      if( $endWdate < $startWdate ) { // MUST be after start date!!
+        $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
+        $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
+      }
+      $rdurWsecs  = $endWdate - $startWdate; // compute component duration in seconds
+            /* make a list of optional exclude dates for component occurence from exrule and exdate */
+      $exdatelist = array();
+      $workstart  = iCalUtilityFunctions::_timestamp2date(( $startDate - $rdurWsecs ), 6);
+      $workend    = iCalUtilityFunctions::_timestamp2date(( $endDate + $rdurWsecs ), 6);
+      while( FALSE !== ( $exrule = $component->getProperty( 'exrule' )))    // check exrule
+        iCalUtilityFunctions::_recur2date( $exdatelist, $exrule, $start, $workstart, $workend );
+      while( FALSE !== ( $exdate = $component->getProperty( 'exdate' ))) {  // check exdate
+        foreach( $exdate as $theExdate ) {
+          $exWdate = iCalUtilityFunctions::_date2timestamp( $theExdate );
+          $exWdate = mktime( 0, 0, 0, date( 'm', $exWdate ), date( 'd', $exWdate ), date( 'Y', $exWdate ) ); // on a day-basis !!!
+          if((( $startDate - $rdurWsecs ) <= $exWdate ) && ( $endDate >= $exWdate ))
+            $exdatelist[$exWdate] = TRUE;
+        }
+      }
+            /* if 'any' components, check repeating components, removing all excluding dates */
+      if( TRUE === $any ) {
+            /* make a list of optional repeating dates for component occurence, rrule, rdate */
+        $recurlist = array();
+        while( FALSE !== ( $rrule = $component->getProperty( 'rrule' )))    // check rrule
+          iCalUtilityFunctions::_recur2date( $recurlist, $rrule, $start, $workstart, $workend );
+        foreach( $recurlist as $recurkey => $recurvalue ) // key=match date as timestamp
+          $recurlist[$recurkey] = $rdurWsecs; // add duration in seconds
+        while( FALSE !== ( $rdate = $component->getProperty( 'rdate' ))) {  // check rdate
+          foreach( $rdate as $theRdate ) {
+            if( is_array( $theRdate ) && ( 2 == count( $theRdate )) &&  // all days within PERIOD
+                   array_key_exists( '0', $theRdate ) &&  array_key_exists( '1', $theRdate )) {
+              $rstart = iCalUtilityFunctions::_date2timestamp( $theRdate[0] );
+              if(( $rstart < ( $startDate - $rdurWsecs )) || ( $rstart > $endDate ))
+                continue;
+              if( isset( $theRdate[1]['year'] )) // date-date period
+                $rend = iCalUtilityFunctions::_date2timestamp( $theRdate[1] );
+              else {                             // date-duration period
+                $rend = iCalUtilityFunctions::_duration2date( $theRdate[0], $theRdate[1] );
+                $rend = iCalUtilityFunctions::_date2timestamp( $rend );
+              }
+              while( $rstart < $rend ) {
+                $recurlist[$rstart] = $rdurWsecs; // set start date for recurrence instance + rdate duration in seconds
+                $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
+              }
+            } // PERIOD end
+            else { // single date
+              $theRdate = iCalUtilityFunctions::_date2timestamp( $theRdate );
+              if((( $startDate - $rdurWsecs ) <= $theRdate ) && ( $endDate >= $theRdate ))
+                $recurlist[$theRdate] = $rdurWsecs; // set start date for recurrence instance + event duration in seconds
+            }
+          }
+        }
+        if( 0 < count( $recurlist )) {
+          ksort( $recurlist );
+          $xRecurrence = 1;
+          foreach( $recurlist as $recurkey => $durvalue ) {
+            if((( $startDate - $rdurWsecs ) > $recurkey ) || ( $endDate < $recurkey )) // not within period
+              continue;
+            $checkDate = mktime( 0, 0, 0, date( 'm', $recurkey ), date( 'd', $recurkey ), date( 'Y', $recurkey ) ); // on a day-basis !!!
+            if( isset( $exdatelist[$checkDate] )) // check excluded dates
+              continue;
+            if( $startWdate >= $recurkey ) // exclude component start date
+              continue;
+            $component2   = $component->copy();
+            $rstart = $recurkey;
+            $rend   = $recurkey + $durvalue;
+           /* add repeating components within valid dates to output array, only start date set */
+            if( $flat ) {
+              $datestring = date( $startDateFormat, $recurkey );
+              if( isset( $start['tz'] ))
+                $datestring .= ' '.$start['tz'];
+              $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
+              if( $dtendExist || $dueExist || $durationExist ) {
+                $datestring = date( $endDateFormat, $recurkey + $durvalue );   // fixa korrekt sluttid
+                if( isset( $end['tz'] ))
+                  $datestring .= ' '.$end['tz'];
+                $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
+                $component2->setProperty( $propName, $datestring );
+              } // end if( $dtendExist || $dueExist || $durationExist )
+              $component2->setProperty( 'X-RECURRENCE', ++$xRecurrence );
+              $result[$component2->getProperty( 'UID' )] = $component2->copy(); // copy to output
+            }
+           /* add repeating components within valid dates to output array, one each day */
+            elseif( $split ) {
+              $xRecurrence += 1;
+              if( $rend > $endDate )
+                $rend = $endDate;
+              while( $rstart <= $rend ) { // iterate.. .
+                $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
+                if( isset( $exdatelist[$checkDate] ))  // exclude any recurrence START date, found in exdatelist
+                  break;
+                if( $rstart > $startDate ) {    // date after dtstart
+                  $datestring = date( $startDateFormat, $rstart );
+                  if( isset( $start['tz'] ))
+                    $datestring .= ' '.$start['tz'];
+                  $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
+                  if( $dtendExist || $dueExist || $durationExist ) {
+                    $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
+                    $datestring = date( $endDateFormat, $tend );
+                    if( isset( $end['tz'] ))
+                      $datestring .= ' '.$end['tz'];
+                    $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
+                    $component2->setProperty( $propName, $datestring );
+                  } // end if( $dtendExist || $dueExist || $durationExist )
+                  $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
+                  $wd = getdate( $rstart );
+                  $result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty( 'UID' )] = $component2->copy(); // copy to output
+                }
+                $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
+              } // end while( $rstart <= $rend )
+            } // end elseif( $split )
+            elseif( $rstart >= $startDate ) {     // date within period   //* flat=FALSE && split=FALSE *//
+              $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
+              if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
+                $xRecurrence += 1;
+                $datestring = date( $startDateFormat, $rstart );
+                if( isset( $start['tz'] ))
+                  $datestring .= ' '.$start['tz'];
+                $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
+                if( $dtendExist || $dueExist || $durationExist ) {
+                  $rstart += $rdurWsecs;
+                  $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
+                  $datestring = date( $endDateFormat, $tend );
+                  if( isset( $end['tz'] ))
+                    $datestring .= ' '.$end['tz'];
+                  $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
+                  $component2->setProperty( $propName, $datestring );
+                } // end if( $dtendExist || $dueExist || $durationExist )
+                $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
+                $wd = getdate( $rstart );
+                $result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty( 'UID' )] = $component2->copy(); // copy to output
+              } // end if( !isset( $exdatelist[$checkDate] ))
+            } // end elseif( $rstart >= $startDate )
+          } // end foreach( $recurlist as $recurkey => $durvalue )
+        } // end if( 0 < count( $recurlist ))
+            /* deselect components with startdate/enddate not within period */
+        if(( $endWdate < $startDate ) || ( $startWdate > $endDate ))
+          continue;
+      } // end if( TRUE === $any )
+            /* deselect components with startdate not within period */
+      elseif(( $startWdate < $startDate ) || ( $startWdate > $endDate ))
+        continue;
+            /* add the selected component (WITHIN valid dates) to output array */
+      if( $flat )
+        $result[$component->getProperty( 'UID' )] = $component->copy(); // copy to output;
+      elseif( $split ) {
+        if( $endWdate > $endDate )
+          $endWdate = $endDate;     // use period end date
+        $rstart = $startWdate;
+        if( $rstart < $startDate )
+          $rstart = $startDate; // use period start date
+        $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
+        if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
+          while( $rstart <= $endWdate ) { // iterate
+            if( $rstart > $startWdate ) { // if NOT startdate, set X-properties
+              $datestring = date( $startDateFormat, $rstart );
+              if( isset( $start['tz'] ))
+                $datestring .= ' '.$start['tz'];
+              $component->setProperty( 'X-CURRENT-DTSTART', $datestring );
+              if( $dtendExist || $dueExist || $durationExist ) {
+                $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
+                $datestring = date( $endDateFormat, $tend );
+                if( isset( $end['tz'] ))
+                  $datestring .= ' '.$end['tz'];
+                $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
+                $component->setProperty( $propName, $datestring );
+              } // end if( $dtendExist || $dueExist || $durationExist )
+            } // end if( $rstart > $startWdate )
+            $wd = getdate( $rstart );
+            $result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty( 'UID' )] = $component->copy(); // copy to output
+            $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
+          } // end while( $rstart <= $endWdate )
+        } // end if( !isset( $exdatelist[$checkDate] ))
+      } // end if( $split )   -  else use component date
+      elseif( $startWdate >= $startDate ) {          // within period
+        $checkDate = mktime( 0, 0, 0, date( 'm', $startWdate ), date( 'd', $startWdate ), date( 'Y', $startWdate ) ); // on a day-basis !!!
+        if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
+          $wd = getdate( $startWdate );
+          $result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty( 'UID' )] = $component->copy(); // copy to output
+        }
+      }
+    } // end foreach ( $this->components as $cix => $component )
+    if( 0 >= count( $result )) return FALSE;
+    elseif( !$flat ) {
+      foreach( $result as $y => $yeararr ) {
+        foreach( $yeararr as $m => $montharr ) {
+          foreach( $montharr as $d => $dayarr )
+            $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index
+          ksort( $result[$y][$m] );
+        }
+        ksort( $result[$y] );
+      }
+      ksort( $result );
+    } // end elseif( !$flat )
+    return $result;
+  }
+/**
+ * add calendar component to container
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.28 - 2011-01-01
+ * @param object $component calendar component
+ * @param mixed $arg1 optional, ordno/component type/ component uid
+ * @param mixed $arg2 optional, ordno if arg1 = component type
+ * @return void
+ */
+  function setComponent( $component, $arg1=FALSE, $arg2=FALSE  ) {
+    $component->setConfig( $this->getConfig(), FALSE, TRUE );
+    if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) {
+      unset( $component->propix );
+            /* make sure dtstamp and uid is set */
+      $dummy1 = $component->getProperty( 'dtstamp' );
+      $dummy2 = $component->getProperty( 'uid' );
+    }
+    if( !$arg1 ) { // plain insert, last in chain
+      $this->components[] = $component->copy();
+      return TRUE;
+    }
+    $argType = $index = null;
+    if ( ctype_digit( (string) $arg1 )) { // index insert/replace
+      $argType = 'INDEX';
+      $index   = (int) $arg1 - 1;
+    }
+    elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) {
+      $argType = strtolower( $arg1 );
+      $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
+    }
+    // else if arg1 is set, arg1 must be an UID
+    $cix1sC = 0;
+    foreach ( $this->components as $cix => $component2) {
+      if( empty( $component2 )) continue;
+      unset( $component2->propix );
+      if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
+        $this->components[$cix] = $component->copy();
+        return TRUE;
+      }
+      elseif( $argType == $component2->objName ) { // component Type index insert/replace
+        if( $index == $cix1sC ) {
+          $this->components[$cix] = $component->copy();
+          return TRUE;
+        }
+        $cix1sC++;
+      }
+      elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
+        $this->components[$cix] = $component->copy();
+        return TRUE;
+      }
+    }
+            /* arg1=index and not found.. . insert at index .. .*/
+    if( 'INDEX' == $argType ) {
+      $this->components[$index] = $component->copy();
+      ksort( $this->components, SORT_NUMERIC );
+    }
+    else    /* not found.. . insert last in chain anyway .. .*/
+      $this->components[] = $component->copy();
+    return TRUE;
+  }
+/**
+ * sort iCal compoments, only local date sort
+ *
+ * ascending sort on properties (if exist) x-current-dtstart, dtstart,
+ * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.3 - 2010-12-05
+ * @return void
+ *
+ */
+  function sort() {
+    if( is_array( $this->components )) {
+            /* set sort parameters for each component */
+      foreach( $this->components as & $c ) {
+        $c->srtk = array( '0', '0', '0', '0' );
+        if( 'vtimezone' == $c->objName ) {
+          if( FALSE === ( $c->srtk[0] = $c->getProperty( 'tzid' )))
+            $c->srtk[0] = 0;
+          continue;
+        }
+        if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' )))
+          $c->srtk[0] = iCalUtilityFunctions::_date_time_string( $d[1] );
+        elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' )))
+          $c->srtk[1] = 0;                                                  // sortkey 0 : dtstart
+        if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' )))
+          $c->srtk[1] = iCalUtilityFunctions::_date_time_string( $d[1] );   // sortkey 1 : dtend/due(/dtstart+duration)
+        elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) {
+          if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' )))
+            $c->srtk[1] = iCalUtilityFunctions::_date_time_string( $d[1] );
+          elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' )))
+            if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE )))
+              $c->srtk[1] = 0;
+        }
+        if( FALSE === ( $c->srtk[2] = $c->getProperty( 'created' )))      // sortkey 2 : created/dtstamp
+          if( FALSE === ( $c->srtk[2] = $c->getProperty( 'dtstamp' )))
+            $c->srtk[2] = 0;
+        if( FALSE === ( $c->srtk[3] = $c->getProperty( 'uid' )))          // sortkey 3 : uid
+          $c->srtk[3] = 0;
+      } // end foreach( $this->components as & $c
+            /* sort */
+      usort( $this->components, array( $this, '_cmpfcn' ));
+    }
+  }
+  function _cmpfcn( $a, $b ) {
+    if(        empty( $a ))                       return -1;
+    if(        empty( $b ))                       return  1;
+    if( 'vtimezone' == $a->objName ) {
+      if( 'vtimezone' != $b->objName )            return -1;
+      elseif( $a->srtk[0] <= $b->srtk[0] )        return -1;
+      else                                        return  1;
+    }
+    elseif( 'vtimezone' == $b->objName )          return  1;
+    $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' );
+    for( $k = 0; $k < 4 ; $k++ ) {
+      if(        empty( $a->srtk[$k] ))           return -1;
+      elseif(    empty( $b->srtk[$k] ))           return  1;
+      if( is_array( $a->srtk[$k] )) {
+        if( is_array( $b->srtk[$k] )) {
+          foreach( $sortkeys as $key ) {
+            if    (  empty( $a->srtk[$k][$key] )) return -1;
+            elseif(  empty( $b->srtk[$k][$key] )) return  1;
+            if    (         $a->srtk[$k][$key] == $b->srtk[$k][$key])
+                                                  continue;
+            if    ((  (int) $a->srtk[$k][$key] ) < ((int) $b->srtk[$k][$key] ))
+                                                  return -1;
+            elseif((  (int) $a->srtk[$k][$key] ) > ((int) $b->srtk[$k][$key] ))
+                                                  return  1;
+          }
+        }
+        else                                      return -1;
+      }
+      elseif( is_array( $b->srtk[$k] ))           return  1;
+      elseif( $a->srtk[$k] < $b->srtk[$k] )       return -1;
+      elseif( $a->srtk[$k] > $b->srtk[$k] )       return  1;
+    }
+    return 0;
+  }
+/**
+ * parse iCal text/file into vcalendar, components, properties and parameters
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.32 - 2010-12-19
+ * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings
+ * @return bool FALSE if error occurs during parsing
+ *
+ */
+  function parse( $unparsedtext=FALSE ) {
+    $nl = $this->getConfig( 'nl' );
+    if( FALSE === $unparsedtext ) {
+            /* directory+filename is set previously via setConfig directory+filename or url */
+      if( FALSE === ( $filename = $this->getConfig( 'url' )))
+        $filename = $this->getConfig( 'dirfile' );
+            /* READ FILE */
+      if( FALSE === ( $rows = file( $filename )))
+        return FALSE;                 /* err 1 */
+    }
+    elseif( !is_array( $unparsedtext ))
+      $rows =  explode( $nl, $unparsedtext );
+    else
+      $rows = & $unparsedtext;
+
+            /* identify BEGIN:VCALENDAR, MUST be first row */
+    if( 'BEGIN:VCALENDAR' != strtoupper( trim( $rows[0] )))
+      return FALSE;                   /* err 8 */
+
+    $lix = count( $rows );
+
+    /*skicp empty line*/
+
+    //Remove  espços em branco no final do arquivo ics
+    for($i = $lix; $i >= 0; $i--)
+    {
+        if(strtoupper(trim($rows[$i])) == 'END:VCALENDAR')
+            break;
+         else
+           unset($rows[$i]);
+    }
+    
+    $lix = count( $rows ) -1;
+
+
+    if( 'END:VCALENDAR'   != strtoupper( trim( $rows[ $lix ] )))
+      return FALSE;                   /* err 9 */
+
+
+    if( 3 > count( $rows ))
+      return FALSE;                   /* err 10 */
+    $comp    = & $this;
+    $calsync = 0;
+
+
+            /* identify components and update unparsed data within component */
+    foreach( $rows as $line ) {
+      if( '' == trim( $line ))
+        continue;
+      if( $nl == substr( $line, 0 - strlen( $nl )))
+        $line = substr( $line, 0, ( strlen( $line ) - strlen( $nl ))).'\n';
+      elseif( "\n\r" == substr( $line, 0 - strlen( "\n\r" )))
+        $line = substr( $line, 0, ( strlen( $line ) - strlen( "\n\r" ))).'\n';
+      if(     'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) {
+        $calsync++;
+        continue;
+      }
+      elseif( 'END:VCALENDAR'   == strtoupper( substr( $line, 0, 13 ))) {
+        $calsync--;
+        break;
+      }
+      elseif( 1 != $calsync )
+        return FALSE;                 /* err 20 */
+      elseif( in_array( strtoupper( substr( $line, 0, 6 )), array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' ))) {
+        $this->components[] = $comp->copy();
+        continue;
+      }
+      if(     'BEGIN:VEVENT'    == strtoupper( substr( $line, 0, 12 )))
+        $comp = new vevent();
+      elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 )))
+        $comp = new vfreebusy();
+      elseif( 'BEGIN:VJOURNAL'  == strtoupper( substr( $line, 0, 14 )))
+        $comp = new vjournal();
+      elseif( 'BEGIN:VTODO'     == strtoupper( substr( $line, 0, 11 )))
+        $comp = new vtodo();
+      elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 )))
+        $comp = new vtimezone();
+      else  /* update component with unparsed data */
+        $comp->unparsed[] = $line;
+    } // end - foreach( rows.. .
+            /* parse data for calendar (this) object */
+    if( is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) {
+            /* concatenate property values spread over several lines */
+      $lastix    = -1;
+      $propnames = array( 'calscale','method','prodid','version','x-' );
+      $proprows  = array();
+      foreach( $this->unparsed as $line ) {
+        if( '' == trim( $line ))
+          continue;
+        $newProp = FALSE;
+        foreach ( $propnames as $propname ) {
+          if( $propname == strtolower( substr( $line, 0, strlen( $propname )))) {
+            $newProp = TRUE;
+            break;
+          }
+        }
+        if( $newProp ) {
+          $newProp = FALSE;
+          $lastix++;
+          $proprows[$lastix]  = $line;
+        }
+        else {
+
+
+            /* todo: fix count for multibyte characters.. . */
+          $strlen = ( defined( MB_OVERLOAD_STRING )) ? mb_strlen( $proprows[$lastix] ) : strlen( $proprows[$lastix] );
+            /* remove line breaks */
+//          if(( 1 < strlen( $proprows[$lastix] ))         &&
+          if(( 73 < $strlen )                            &&
+             ( '\n' == substr( $proprows[$lastix], -2 )) &&
+             (  ' ' == substr( $line, 0, 1 ))) {
+//            $proprows[$lastix] = substr( $proprows[$lastix], 0, strlen( $proprows[$lastix] ) - 2 );
+            $proprows[$lastix] = substr( $proprows[$lastix], 0, ( $strlen - 2 ));
+            $line = substr( $line, 1 );
+          }
+            /* concatenate lines within the same property */
+          $proprows[$lastix] .= $line;
+        }
+      }
+      foreach( $proprows as $line ) {
+        if( '\n' == substr( $line, -2 ))
+          $line = substr( $line, 0, strlen( $line ) - 2 );
+            /* get propname */
+        $cix = $propname = null;
+        for( $cix=0, $clen = strlen( $line ); $cix < $clen; $cix++ ) {
+          if( in_array( $line[$cix], array( ':', ';' )))
+            break;
+          else
+            $propname .= $line[$cix];
+        }
+            /* ignore version/prodid properties */
+        if( in_array( strtoupper( $propname ), array( 'VERSION', 'PRODID' )))
+          continue;
+        $line = substr( $line, $cix);
+            /* separate attributes from value */
+        $attr   = array();
+        $attrix = -1;
+        $strlen = strlen( $line );
+        for( $cix=0; $cix < $strlen; $cix++ ) {
+          if((       ':'   == $line[$cix] )             &&
+                   ( '://' != substr( $line, $cix, 3 )) &&
+             ( !in_array( strtolower( substr( $line, $cix - 3, 4 )), array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ))) &&
+             ( !in_array( strtolower( substr( $line, $cix - 4, 5 )), array( 'crid:', 'news:', 'pres:' ))) &&
+             ( 'mailto:'   != strtolower( substr( $line, $cix - 6, 7 )))) {
+            $attrEnd = TRUE;
+            if(( $cix < ( $strlen - 4 )) &&
+                 ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
+              for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
+                if( '://' == substr( $line, $c2ix - 2, 3 )) {
+                  $attrEnd = FALSE;
+                  break; // an URI with a portnr!!
+                }
+              }
+            }
+            if( $attrEnd) {
+              $line = substr( $line, $cix + 1 );
+              break;
+            }
+          }
+          if( ';' == $line[$cix] )
+            $attr[++$attrix] = null;
+          else
+            $attr[$attrix] .= $line[$cix];
+        }
+
+            /* make attributes in array format */
+        $propattr = array();
+        foreach( $attr as $attribute ) {
+          $attrsplit = explode( '=', $attribute, 2 );
+          if( 1 < count( $attrsplit ))
+            $propattr[$attrsplit[0]] = $attrsplit[1];
+          else
+            $propattr[] = $attribute;
+        }
+            /* update Property */
+        if( FALSE !== strpos( $line, ',' )) {
+          $content  = explode( ',', $line );
+          $clen     = count( $content );
+          for( $cix = 0; $cix < $clen; $cix++ ) {
+            if( "\\" == substr( $content[$cix], -1 )) {
+              $content[$cix] .= ','.$content[$cix + 1];
+              unset( $content[$cix + 1] );
+              $cix++;
+            }
+          }
+          if( 1 < count( $content )) {
+            foreach( $content as $cix => $contentPart )
+              $content[$cix] = calendarComponent::_strunrep( $contentPart );
+            $this->setProperty( $propname, $content, $propattr );
+            continue;
+          }
+          else
+            $line = reset( $content );
+          $line = calendarComponent::_strunrep( $line );
+        }
+        $this->setProperty( $propname, trim( $line ), $propattr );
+      } // end - foreach( $this->unparsed.. .
+    } // end - if( is_array( $this->unparsed.. .
+    unset( $unparsedtext, $rows, $this->unparsed, $proprows );
+            /* parse Components */
+    if( is_array( $this->components ) && ( 0 < count( $this->components ))) {
+      $ckeys = array_keys( $this->components );
+      foreach( $ckeys as $ckey ) {
+        if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
+          $this->components[$ckey]->parse();
+        }
+      }
+    }
+    else
+      return FALSE;                   /* err 91 or something.. . */
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * creates formatted output for calendar object instance
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-12-12
+ * @return string
+ */
+  function createCalendar() {
+    $calendarInit1 = $calendarInit2 = $calendarxCaldecl = $calendarStart = $calendar = null;
+    switch( $this->format ) {
+      case 'xcal':
+        $calendarInit1 = '<?xml version="1.0" encoding="UTF-8"?>'.$this->nl.
+                         '<!DOCTYPE iCalendar PUBLIC "-//IETF//DTD XCAL/iCalendar XML//EN"'.$this->nl.
+                         '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"';
+        $calendarInit2 = '>'.$this->nl;
+        $calendarStart = '<vcalendar';
+        break;
+      default:
+        $calendarStart = 'BEGIN:VCALENDAR'.$this->nl;
+        break;
+    }
+    $calendarStart .= $this->createProdid();
+    $calendarStart .= $this->createVersion();
+    $calendarStart .= $this->createCalscale();
+    $calendarStart .= $this->createMethod();
+    switch( $this->format ) {
+      case 'xcal':
+        $nlstrlen = strlen( $this->nl );
+        if( $this->nl == substr( $calendarStart, ( 0 - $nlstrlen )))
+          $calendarStart = substr( $calendarStart, 0, ( strlen( $calendarStart ) - $nlstrlen ));
+        $calendarStart .= '>'.$this->nl;
+        break;
+      default:
+        break;
+    }
+    $calendar .= $this->createXprop();
+    foreach( $this->components as $component ) {
+      if( empty( $component )) continue;
+      $component->setConfig( $this->getConfig(), FALSE, TRUE );
+      $calendar .= $component->createComponent( $this->xcaldecl );
+    }
+    if(( 0 < count( $this->xcaldecl )) && ( 'xcal' == $this->format )) { // xCal only
+      $calendarInit1 .= $this->nl.'['.$this->nl;
+      $old_xcaldecl = array();
+      foreach( $this->xcaldecl as $declix => $declPart ) {
+        if(( 0 < count( $old_xcaldecl)) &&
+           ( in_array( $declPart['uri'],      $old_xcaldecl['uri'] )) &&
+           ( in_array( $declPart['external'], $old_xcaldecl['external'] )))
+          continue; // no duplicate uri and ext. references
+        $calendarxCaldecl .= '<!';
+        foreach( $declPart as $declKey => $declValue ) {
+          switch( $declKey ) {                    // index
+            case 'xmldecl':                       // no 1
+              $calendarxCaldecl .= $declValue.' ';
+              break;
+            case 'uri':                           // no 2
+              $calendarxCaldecl .= $declValue.' ';
+              $old_xcaldecl['uri'][] = $declValue;
+              break;
+            case 'ref':                           // no 3
+              $calendarxCaldecl .= $declValue.' ';
+              break;
+            case 'external':                      // no 4
+              $calendarxCaldecl .= '"'.$declValue.'" ';
+              $old_xcaldecl['external'][] = $declValue;
+              break;
+            case 'type':                          // no 5
+              $calendarxCaldecl .= $declValue.' ';
+              break;
+            case 'type2':                         // no 6
+              $calendarxCaldecl .= $declValue;
+              break;
+          }
+        }
+        $calendarxCaldecl .= '>'.$this->nl;
+      }
+      $calendarInit2 = ']'.$calendarInit2;
+    }
+    switch( $this->format ) {
+      case 'xcal':
+        $calendar .= '</vcalendar>'.$this->nl;
+        break;
+      default:
+        $calendar .= 'END:VCALENDAR'.$this->nl;
+        break;
+    }
+    return $calendarInit1.$calendarxCaldecl.$calendarInit2.$calendarStart.$calendar;
+  }
+/**
+ * a HTTP redirect header is sent with created, updated and/or parsed calendar
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.2.12 - 2007-10-23
+ * @return redirect
+ */
+  function returnCalendar() {
+    $filename = $this->getConfig( 'filename' );
+    $output   = $this->createCalendar();
+    $filesize = strlen( $output );
+//    if( headers_sent( $filename, $linenum ))
+//      die( "Headers already sent in $filename on line $linenum\n" );
+    if( 'xcal' == $this->format )
+      header( 'Content-Type: application/calendar+xml; charset=utf-8' );
+    else
+      header( 'Content-Type: text/calendar; charset=utf-8' );
+    header( 'Content-Length: '.$filesize );
+    header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
+    header( 'Cache-Control: max-age=10' );
+    echo $output;
+    die();
+  }
+/**
+ * save content in a file
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.2.12 - 2007-12-30
+ * @param string $directory optional
+ * @param string $filename optional
+ * @param string $delimiter optional
+ * @return bool
+ */
+  function saveCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE ) {
+    if( $directory )
+      $this->setConfig( 'directory', $directory );
+    if( $filename )
+      $this->setConfig( 'filename',  $filename );
+    if( $delimiter && ($delimiter != DIRECTORY_SEPARATOR ))
+      $this->setConfig( 'delimiter', $delimiter );
+    if( FALSE === ( $dirfile = $this->getConfig( 'url' )))
+      $dirfile = $this->getConfig( 'dirfile' );
+    $iCalFile = @fopen( $dirfile, 'w' );
+    if( $iCalFile ) {
+      if( FALSE === fwrite( $iCalFile, $this->createCalendar() ))
+        return FALSE;
+      fclose( $iCalFile );
+      return TRUE;
+    }
+    else
+      return FALSE;
+  }
+/**
+ * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent
+ * else FALSE is returned
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.2.12 - 2007-10-28
+ * @param string $directory optional alt. int timeout
+ * @param string $filename optional
+ * @param string $delimiter optional
+ * @param int timeout optional, default 3600 sec
+ * @return redirect/FALSE
+ */
+  function useCachedCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE, $timeout=3600) {
+    if ( $directory && ctype_digit( (string) $directory ) && !$filename ) {
+      $timeout   = (int) $directory;
+      $directory = FALSE;
+    }
+    if( $directory )
+      $this->setConfig( 'directory', $directory );
+    if( $filename )
+      $this->setConfig( 'filename',  $filename );
+    if( $delimiter && ( $delimiter != DIRECTORY_SEPARATOR ))
+      $this->setConfig( 'delimiter', $delimiter );
+    $filesize    = $this->getConfig( 'filesize' );
+    if( 0 >= $filesize )
+      return FALSE;
+    $dirfile     = $this->getConfig( 'dirfile' );
+    if( time() - filemtime( $dirfile ) < $timeout) {
+      clearstatcache();
+      $dirfile   = $this->getConfig( 'dirfile' );
+      $filename  = $this->getConfig( 'filename' );
+//    if( headers_sent( $filename, $linenum ))
+//      die( "Headers already sent in $filename on line $linenum\n" );
+      if( 'xcal' == $this->format )
+        header( 'Content-Type: application/calendar+xml; charset=utf-8' );
+      else
+        header( 'Content-Type: text/calendar; charset=utf-8' );
+      header( 'Content-Length: '.$filesize );
+      header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
+      header( 'Cache-Control: max-age=10' );
+      $fp = @fopen( $dirfile, 'r' );
+      if( $fp ) {
+        fpassthru( $fp );
+        fclose( $fp );
+      }
+      die();
+    }
+    else
+      return FALSE;
+  }
+}
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ *  abstract class for calendar components
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.19 - 2008-10-12
+ */
+class calendarComponent {
+            //  component property variables
+  var $uid;
+  var $dtstamp;
+
+            //  component config variables
+  var $allowEmpty;
+  var $language;
+  var $nl;
+  var $unique_id;
+  var $format;
+  var $objName; // created automatically at instance creation
+            //  component internal variables
+  var $componentStart1;
+  var $componentStart2;
+  var $componentEnd1;
+  var $componentEnd2;
+  var $elementStart1;
+  var $elementStart2;
+  var $elementEnd1;
+  var $elementEnd2;
+  var $intAttrDelimiter;
+  var $attributeDelimiter;
+  var $valueInit;
+            //  component xCal declaration container
+  var $xcaldecl;
+/**
+ * constructor for calendar component object
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.19 - 2008-10-23
+ */
+  function calendarComponent() {
+    $this->objName         = ( isset( $this->timezonetype )) ?
+                          strtolower( $this->timezonetype )  :  get_class ( $this );
+    $this->uid             = array();
+    $this->dtstamp         = array();
+
+    $this->language        = null;
+    $this->nl              = null;
+    $this->unique_id       = null;
+    $this->format          = null;
+    $this->allowEmpty      = TRUE;
+    $this->xcaldecl        = array();
+
+    $this->_createFormat();
+    $this->_makeDtstamp();
+  }
+/*********************************************************************************/
+/**
+ * Property Name: ACTION
+ */
+/**
+ * creates formatted output for calendar component property action
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createAction() {
+    if( empty( $this->action )) return FALSE;
+    if( empty( $this->action['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ACTION' ) : FALSE;
+    $attributes = $this->_createParams( $this->action['params'] );
+    return $this->_createElement( 'ACTION', $attributes, $this->action['value'] );
+  }
+/**
+ * set calendar component property action
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value  "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
+ * @param mixed $params
+ * @return bool
+ */
+  function setAction( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->action = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: ATTACH
+ */
+/**
+ * creates formatted output for calendar component property attach
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 0.9.7 - 2006-11-23
+ * @return string
+ */
+  function createAttach() {
+    if( empty( $this->attach )) return FALSE;
+    $output       = null;
+    foreach( $this->attach as $attachPart ) {
+      if(! empty( $attachPart['value'] )) {
+        $attributes = $this->_createParams( $attachPart['params'] );
+        $output    .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] );
+      }
+      elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property attach
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-06
+ * @param string $value
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setAttach( $value, $params=FALSE, $index=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    iCalUtilityFunctions::_setMval( $this->attach, $value, $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: ATTENDEE
+ */
+/**
+ * creates formatted output for calendar component property attendee
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.29 - 2010-11-29
+ * @return string
+ */
+  function createAttendee() {
+    if( empty( $this->attendee )) return FALSE;
+    $output = null;
+    foreach( $this->attendee as $attendeePart ) {                      // start foreach 1
+      if( empty( $attendeePart['value'] )) {
+        if( $this->getConfig( 'allowEmpty' ))
+          $output .= $this->_createElement( 'ATTENDEE' );
+        continue;
+      }
+      $attendee1 = $attendee2 = null;
+      foreach( $attendeePart as $paramlabel => $paramvalue ) {         // start foreach 2
+        if( 'value' == $paramlabel )
+          $attendee2     .= $paramvalue;
+        elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif
+        // set attenddee parameters in rfc2445 order
+          if( isset( $paramvalue['CUTYPE'] ))
+            $attendee1   .= $this->intAttrDelimiter.'CUTYPE="'.$paramvalue['CUTYPE'].'"';
+          if( isset( $paramvalue['MEMBER'] )) {
+            $attendee1   .= $this->intAttrDelimiter.'MEMBER=';
+            foreach( $paramvalue['MEMBER'] as $cix => $opv )
+              $attendee1 .= ( $cix ) ? ', "'.$opv.'"' : '"'.$opv.'"' ;
+          }
+          if( isset( $paramvalue['ROLE'] ))
+            $attendee1   .= $this->intAttrDelimiter.'ROLE="'.$paramvalue['ROLE'].'"';
+          if( isset( $paramvalue['PARTSTAT'] ))
+            $attendee1   .= $this->intAttrDelimiter.'PARTSTAT="'.$paramvalue['PARTSTAT'].'"';
+          if( isset( $paramvalue['RSVP'] ))
+            $attendee1   .= $this->intAttrDelimiter.'RSVP="'.$paramvalue['RSVP'].'"';
+          if( isset( $paramvalue['DELEGATED-TO'] )) {
+            $attendee1   .= $this->intAttrDelimiter.'DELEGATED-TO=';
+            foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv )
+              $attendee1 .= ( $cix ) ? ', "'.$opv.'"' : '"'.$opv.'"' ;
+          }
+          if( isset( $paramvalue['DELEGATED-FROM'] )) {
+            $attendee1   .= $this->intAttrDelimiter.'DELEGATED-FROM=';
+            foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv )
+              $attendee1 .= ( $cix ) ? ', "'.$opv.'"' : '"'.$opv.'"' ;
+          }
+          if( isset( $paramvalue['SENT-BY'] ))
+            $attendee1   .= $this->intAttrDelimiter.'SENT-BY="'.$paramvalue['SENT-BY'].'"';
+          if( isset( $paramvalue['CN'] ))
+            $attendee1   .= $this->intAttrDelimiter.'CN="'.$paramvalue['CN'].'"';
+          if( isset( $paramvalue['DIR'] ))
+            $attendee1   .= $this->intAttrDelimiter.'DIR="'.$paramvalue['DIR'].'"';
+          if( isset( $paramvalue['LANGUAGE'] ))
+            $attendee1   .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE'];
+          $xparams = array();
+          foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3
+            if( ctype_digit( (string) $optparamlabel )) {
+              $xparams[]  = $optparamvalue;
+              continue;
+            }
+            if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' )))
+              $xparams[$optparamlabel] = $optparamvalue;
+          } // end foreach 3
+          ksort( $xparams, SORT_STRING );
+          foreach( $xparams as $paramKey => $paramValue ) {
+            if( ctype_digit( (string) $paramKey ))
+              $attendee1 .= $this->intAttrDelimiter.$paramValue;
+            else
+              $attendee1 .= $this->intAttrDelimiter."$paramKey=$paramValue";
+          }      // end foreach 3
+        }        // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue )))
+      }          // end foreach 2
+      $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 );
+    }              // end foreach 1
+    return $output;
+  }
+/**
+ * set calendar component property attach
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.34 - 2010-12-18
+ * @param string $value
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setAttendee( $value, $params=FALSE, $index=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+          // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero://  may exist.. . also in params
+    if( FALSE !== ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
+      $value = strtoupper( substr( $value, 0, $pos )).substr( $value, $pos );
+    elseif( !empty( $value ))
+      $value = 'MAILTO:'.$value;
+    $params2 = array();
+    if( is_array($params )) {
+      $optarrays = array();
+      foreach( $params as $optparamlabel => $optparamvalue ) {
+        $optparamlabel = strtoupper( $optparamlabel );
+        switch( $optparamlabel ) {
+          case 'MEMBER':
+          case 'DELEGATED-TO':
+          case 'DELEGATED-FROM':
+            if( !is_array( $optparamvalue ))
+              $optparamvalue = array( $optparamvalue );
+            foreach( $optparamvalue as $part ) {
+              $part = trim( $part );
+              if(( '"' == substr( $part, 0, 1 )) &&
+                 ( '"' == substr( $part, -1 )))
+                $part = substr( $part, 1, ( strlen( $part ) - 2 ));
+              if( 'mailto:' != strtolower( substr( $part, 0, 7 )))
+                $part = "MAILTO:$part";
+              else
+                $part = 'MAILTO:'.substr( $part, 7 );
+              $optarrays[$optparamlabel][] = $part;
+            }
+            break;
+          default:
+            if(( '"' == substr( $optparamvalue, 0, 1 )) &&
+               ( '"' == substr( $optparamvalue, -1 )))
+              $optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 ));
+            if( 'SENT-BY' ==  $optparamlabel ) {
+              if( 'mailto:' != strtolower( substr( $optparamvalue, 0, 7 )))
+                $optparamvalue = "MAILTO:$optparamvalue";
+              else
+                $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 );
+            }
+            $params2[$optparamlabel] = $optparamvalue;
+            break;
+        } // end switch( $optparamlabel.. .
+      } // end foreach( $optparam.. .
+      foreach( $optarrays as $optparamlabel => $optparams )
+        $params2[$optparamlabel] = $optparams;
+    }
+        // remove defaults
+    iCalUtilityFunctions::_existRem( $params2, 'CUTYPE',   'INDIVIDUAL' );
+    iCalUtilityFunctions::_existRem( $params2, 'PARTSTAT', 'NEEDS-ACTION' );
+    iCalUtilityFunctions::_existRem( $params2, 'ROLE',     'REQ-PARTICIPANT' );
+    iCalUtilityFunctions::_existRem( $params2, 'RSVP',     'FALSE' );
+        // check language setting
+    if( isset( $params2['CN' ] )) {
+      $lang = $this->getConfig( 'language' );
+      if( !isset( $params2['LANGUAGE' ] ) && !empty( $lang ))
+        $params2['LANGUAGE' ] = $lang;
+    }
+    iCalUtilityFunctions::_setMval( $this->attendee, $value, $params2, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: CATEGORIES
+ */
+/**
+ * creates formatted output for calendar component property categories
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createCategories() {
+    if( empty( $this->categories )) return FALSE;
+    $output = null;
+    foreach( $this->categories as $category ) {
+      if( empty( $category['value'] )) {
+        if ( $this->getConfig( 'allowEmpty' ))
+          $output .= $this->_createElement( 'CATEGORIES' );
+        continue;
+      }
+      $attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' ));
+      if( is_array( $category['value'] )) {
+        foreach( $category['value'] as $cix => $categoryPart )
+          $category['value'][$cix] = $this->_strrep( $categoryPart );
+        $content  = implode( ',', $category['value'] );
+      }
+      else
+        $content  = $this->_strrep( $category['value'] );
+      $output    .= $this->_createElement( 'CATEGORIES', $attributes, $content );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property categories
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-06
+ * @param mixed $value
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setCategories( $value, $params=FALSE, $index=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    iCalUtilityFunctions::_setMval( $this->categories, $value, $params, FALSE, $index );
+    return TRUE;
+ }
+/*********************************************************************************/
+/**
+ * Property Name: CLASS
+ */
+/**
+ * creates formatted output for calendar component property class
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 0.9.7 - 2006-11-20
+ * @return string
+ */
+  function createClass() {
+    if( empty( $this->class )) return FALSE;
+    if( empty( $this->class['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'CLASS' ) : FALSE;
+    $attributes = $this->_createParams( $this->class['params'] );
+    return $this->_createElement( 'CLASS', $attributes, $this->class['value'] );
+  }
+/**
+ * set calendar component property class
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name
+ * @param array $params optional
+ * @return bool
+ */
+  function setClass( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->class = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: COMMENT
+ */
+/**
+ * creates formatted output for calendar component property comment
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createComment() {
+    if( empty( $this->comment )) return FALSE;
+    $output = null;
+    foreach( $this->comment as $commentPart ) {
+      if( empty( $commentPart['value'] )) {
+        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' );
+        continue;
+      }
+      $attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' ));
+      $content    = $this->_strrep( $commentPart['value'] );
+      $output    .= $this->_createElement( 'COMMENT', $attributes, $content );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property comment
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-06
+ * @param string $value
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setComment( $value, $params=FALSE, $index=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    iCalUtilityFunctions::_setMval( $this->comment, $value, $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: COMPLETED
+ */
+/**
+ * creates formatted output for calendar component property completed
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createCompleted( ) {
+    if( empty( $this->completed )) return FALSE;
+    if( !isset( $this->completed['value']['year'] )  &&
+        !isset( $this->completed['value']['month'] ) &&
+        !isset( $this->completed['value']['day'] )   &&
+        !isset( $this->completed['value']['hour'] )  &&
+        !isset( $this->completed['value']['min'] )   &&
+        !isset( $this->completed['value']['sec'] ))
+      if( $this->getConfig( 'allowEmpty' ))
+        return $this->_createElement( 'COMPLETED' );
+      else return FALSE;
+    $formatted  = iCalUtilityFunctions::_format_date_time( $this->completed['value'], 7 );
+    $attributes = $this->_createParams( $this->completed['params'] );
+    return $this->_createElement( 'COMPLETED', $attributes, $formatted );
+  }
+/**
+ * set calendar component property completed
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @param mixed $year
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param array $params optional
+ * @return bool
+ */
+  function setCompleted( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
+    if( empty( $year )) {
+      if( $this->getConfig( 'allowEmpty' )) {
+        $this->completed = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
+        return TRUE;
+      }
+      else
+        return FALSE;
+    }
+    $this->completed = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: CONTACT
+ */
+/**
+ * creates formatted output for calendar component property contact
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @return string
+ */
+  function createContact() {
+    if( empty( $this->contact )) return FALSE;
+    $output = null;
+    foreach( $this->contact as $contact ) {
+      if( !empty( $contact['value'] )) {
+        $attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' ));
+        $content    = $this->_strrep( $contact['value'] );
+        $output    .= $this->_createElement( 'CONTACT', $attributes, $content );
+      }
+      elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property contact
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-05
+ * @param string $value
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setContact( $value, $params=FALSE, $index=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    iCalUtilityFunctions::_setMval( $this->contact, $value, $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: CREATED
+ */
+/**
+ * creates formatted output for calendar component property created
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createCreated() {
+    if( empty( $this->created )) return FALSE;
+    $formatted  = iCalUtilityFunctions::_format_date_time( $this->created['value'], 7 );
+    $attributes = $this->_createParams( $this->created['params'] );
+    return $this->_createElement( 'CREATED', $attributes, $formatted );
+  }
+/**
+ * set calendar component property created
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @param mixed $year optional
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param mixed $params optional
+ * @return bool
+ */
+  function setCreated( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
+    if( !isset( $year )) {
+      $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' )));
+    }
+    $this->created = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: DESCRIPTION
+ */
+/**
+ * creates formatted output for calendar component property description
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createDescription() {
+    if( empty( $this->description )) return FALSE;
+    $output       = null;
+    foreach( $this->description as $description ) {
+      if( !empty( $description['value'] )) {
+        $attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' ));
+        $content    = $this->_strrep( $description['value'] );
+        $output    .= $this->_createElement( 'DESCRIPTION', $attributes, $content );
+      }
+      elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property description
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.24 - 2010-11-06
+ * @param string $value
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setDescription( $value, $params=FALSE, $index=FALSE ) {
+    if( empty( $value )) { if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; }
+    if( 'vjournal' != $this->objName )
+      $index = 1;
+    iCalUtilityFunctions::_setMval( $this->description, $value, $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: DTEND
+ */
+/**
+ * creates formatted output for calendar component property dtend
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createDtend() {
+    if( empty( $this->dtend )) return FALSE;
+    if( !isset( $this->dtend['value']['year'] )  &&
+        !isset( $this->dtend['value']['month'] ) &&
+        !isset( $this->dtend['value']['day'] )   &&
+        !isset( $this->dtend['value']['hour'] )  &&
+        !isset( $this->dtend['value']['min'] )   &&
+        !isset( $this->dtend['value']['sec'] ))
+      if( $this->getConfig( 'allowEmpty' ))
+        return $this->_createElement( 'DTEND' );
+      else return FALSE;
+    $formatted  = iCalUtilityFunctions::_format_date_time( $this->dtend['value'] );
+    $attributes = $this->_createParams( $this->dtend['params'] );
+    return $this->_createElement( 'DTEND', $attributes, $formatted );
+  }
+/**
+ * set calendar component property dtend
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @param mixed $year
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param string $tz optional
+ * @param array params optional
+ * @return bool
+ */
+  function setDtend( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
+    if( empty( $year )) {
+      if( $this->getConfig( 'allowEmpty' )) {
+        $this->dtend = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
+        return TRUE;
+      }
+      else
+        return FALSE;
+    }
+    $this->dtend = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: DTSTAMP
+ */
+/**
+ * creates formatted output for calendar component property dtstamp
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.4 - 2008-03-07
+ * @return string
+ */
+  function createDtstamp() {
+    if( !isset( $this->dtstamp['value']['year'] )  &&
+        !isset( $this->dtstamp['value']['month'] ) &&
+        !isset( $this->dtstamp['value']['day'] )   &&
+        !isset( $this->dtstamp['value']['hour'] )  &&
+        !isset( $this->dtstamp['value']['min'] )   &&
+        !isset( $this->dtstamp['value']['sec'] ))
+      $this->_makeDtstamp();
+    $formatted  = iCalUtilityFunctions::_format_date_time( $this->dtstamp['value'], 7 );
+    $attributes = $this->_createParams( $this->dtstamp['params'] );
+    return $this->_createElement( 'DTSTAMP', $attributes, $formatted );
+  }
+/**
+ * computes datestamp for calendar component object instance dtstamp
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.25 - 2010-11-09
+ * @return void
+ */
+  function _makeDtstamp() {
+    $d = mktime( date('H'), date('m'), (date('s') - date( 'Z' )), date('m'), date('d'), date('Y'));
+    $this->dtstamp['value'] = array( 'year'  => date( 'Y', $d )
+                                   , 'month' => date( 'm', $d )
+                                   , 'day'   => date( 'd', $d )
+                                   , 'hour'  => date( 'H', $d )
+                                   , 'min'   => date( 'i', $d )
+                                   , 'sec'   => date( 's', $d ));
+    $this->dtstamp['params'] = null;
+  }
+/**
+ * set calendar component property dtstamp
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @param mixed $year
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param array $params optional
+ * @return TRUE
+ */
+  function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
+    if( empty( $year ))
+      $this->_makeDtstamp();
+    else
+      $this->dtstamp = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: DTSTART
+ */
+/**
+ * creates formatted output for calendar component property dtstart
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-10-26
+ * @return string
+ */
+  function createDtstart() {
+    if( empty( $this->dtstart )) return FALSE;
+    if( !isset( $this->dtstart['value']['year'] )  &&
+        !isset( $this->dtstart['value']['month'] ) &&
+        !isset( $this->dtstart['value']['day'] )   &&
+        !isset( $this->dtstart['value']['hour'] )  &&
+        !isset( $this->dtstart['value']['min'] )   &&
+        !isset( $this->dtstart['value']['sec'] ))
+    if( $this->getConfig( 'allowEmpty' ))
+      return $this->_createElement( 'DTSTART' );
+    else return FALSE;
+    if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' )))
+      unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] );
+    $formatted  = iCalUtilityFunctions::_format_date_time( $this->dtstart['value'] );
+    $attributes = $this->_createParams( $this->dtstart['params'] );
+    return $this->_createElement( 'DTSTART', $attributes, $formatted );
+  }
+/**
+ * set calendar component property dtstart
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.22 - 2010-09-22
+ * @param mixed $year
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param string $tz optional
+ * @param array $params optional
+ * @return bool
+ */
+  function setDtstart( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
+    if( empty( $year )) {
+      if( $this->getConfig( 'allowEmpty' )) {
+        $this->dtstart = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
+        return TRUE;
+      }
+      else
+        return FALSE;
+    }
+    $this->dtstart = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: DUE
+ */
+/**
+ * creates formatted output for calendar component property due
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createDue() {
+    if( empty( $this->due )) return FALSE;
+    if( !isset( $this->due['value']['year'] )  &&
+        !isset( $this->due['value']['month'] ) &&
+        !isset( $this->due['value']['day'] )   &&
+        !isset( $this->due['value']['hour'] )  &&
+        !isset( $this->due['value']['min'] )   &&
+        !isset( $this->due['value']['sec'] ))
+      if( $this->getConfig( 'allowEmpty' ))
+        return $this->_createElement( 'DUE' );
+      else return FALSE;
+    $formatted  = iCalUtilityFunctions::_format_date_time( $this->due['value'] );
+    $attributes = $this->_createParams( $this->due['params'] );
+    return $this->_createElement( 'DUE', $attributes, $formatted );
+  }
+/**
+ * set calendar component property due
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param mixed $year
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param array $params optional
+ * @return bool
+ */
+  function setDue( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
+    if( empty( $year )) {
+      if( $this->getConfig( 'allowEmpty' )) {
+        $this->due = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
+        return TRUE;
+      }
+      else
+        return FALSE;
+    }
+    $this->due = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: DURATION
+ */
+/**
+ * creates formatted output for calendar component property duration
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createDuration() {
+    if( empty( $this->duration )) return FALSE;
+    if( !isset( $this->duration['value']['week'] ) &&
+        !isset( $this->duration['value']['day'] )  &&
+        !isset( $this->duration['value']['hour'] ) &&
+        !isset( $this->duration['value']['min'] )  &&
+        !isset( $this->duration['value']['sec'] ))
+      if( $this->getConfig( 'allowEmpty' ))
+        return $this->_createElement( 'DURATION', array(), null );
+      else return FALSE;
+    $attributes = $this->_createParams( $this->duration['params'] );
+    return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_format_duration( $this->duration['value'] ));
+  }
+/**
+ * set calendar component property duration
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param mixed $week
+ * @param mixed $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param array $params optional
+ * @return bool
+ */
+  function setDuration( $week, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
+    if( empty( $week )) if( $this->getConfig( 'allowEmpty' )) $week = null; else return FALSE;
+    if( is_array( $week ) && ( 1 <= count( $week )))
+      $this->duration = array( 'value' => iCalUtilityFunctions::_duration_array( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
+    elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) {
+      $week = trim( $week );
+      if( in_array( substr( $week, 0, 1 ), array( '+', '-' )))
+        $week = substr( $week, 1 );
+      $this->duration = array( 'value' => iCalUtilityFunctions::_duration_string( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
+    }
+    elseif( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec ))
+      return FALSE;
+    else
+      $this->duration = array( 'value' => iCalUtilityFunctions::_duration_array( array( $week, $day, $hour, $min, $sec )), 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: EXDATE
+ */
+/**
+ * creates formatted output for calendar component property exdate
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createExdate() {
+    if( empty( $this->exdate )) return FALSE;
+    $output = null;
+    foreach( $this->exdate as $ex => $theExdate ) {
+      if( empty( $theExdate['value'] )) {
+        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'EXDATE' );
+        continue;
+      }
+      $content = $attributes = null;
+      foreach( $theExdate['value'] as $eix => $exdatePart ) {
+        $parno = count( $exdatePart );
+        $formatted = iCalUtilityFunctions::_format_date_time( $exdatePart, $parno );
+        if( isset( $theExdate['params']['TZID'] ))
+          $formatted = str_replace( 'Z', '', $formatted);
+        if( 0 < $eix ) {
+          if( isset( $theExdate['value'][0]['tz'] )) {
+            if( ctype_digit( substr( $theExdate['value'][0]['tz'], -4 )) ||
+               ( 'Z' == $theExdate['value'][0]['tz'] )) {
+              if( 'Z' != substr( $formatted, -1 ))
+                $formatted .= 'Z';
+            }
+            else
+              $formatted = str_replace( 'Z', '', $formatted );
+          }
+          else
+            $formatted = str_replace( 'Z', '', $formatted );
+        }
+        $content .= ( 0 < $eix ) ? ','.$formatted : $formatted;
+      }
+      $attributes .= $this->_createParams( $theExdate['params'] );
+      $output .= $this->_createElement( 'EXDATE', $attributes, $content );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property exdate
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-05
+ * @param array exdates
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setExdate( $exdates, $params=FALSE, $index=FALSE ) {
+    if( empty( $exdates )) {
+      if( $this->getConfig( 'allowEmpty' )) {
+        iCalUtilityFunctions::_setMval( $this->exdate, null, $params, FALSE, $index );
+        return TRUE;
+      }
+      else
+        return FALSE;
+    }
+    $input  = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
+            /* ev. check 1:st date and save ev. timezone **/
+    iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] );
+    iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter
+    foreach( $exdates as $eix => $theExdate ) {
+      if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate ))
+        $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno );
+      elseif(  is_array( $theExdate ))
+        $exdatea = iCalUtilityFunctions::_date_time_array( $theExdate, $parno );
+      elseif( 8 <= strlen( trim( $theExdate ))) // ex. 2006-08-03 10:12:18
+        $exdatea = iCalUtilityFunctions::_date_time_string( $theExdate, $parno );
+      if( 3 == $parno )
+        unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] );
+      elseif( isset( $exdatea['tz'] ))
+        $exdatea['tz'] = (string) $exdatea['tz'];
+      if(  isset( $input['params']['TZID'] ) ||
+         ( isset( $exdatea['tz'] ) && !iCalUtilityFunctions::_isOffset( $exdatea['tz'] )) ||
+         ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) ||
+         ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] )))
+        unset( $exdatea['tz'] );
+      $input['value'][] = $exdatea;
+    }
+    if( 0 >= count( $input['value'] ))
+      return FALSE;
+    if( 3 == $parno ) {
+      $input['params']['VALUE'] = 'DATE';
+      unset( $input['params']['TZID'] );
+    }
+    iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: EXRULE
+ */
+/**
+ * creates formatted output for calendar component property exrule
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createExrule() {
+    if( empty( $this->exrule )) return FALSE;
+    return $this->_format_recur( 'EXRULE', $this->exrule );
+  }
+/**
+ * set calendar component property exdate
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-05
+ * @param array $exruleset
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setExrule( $exruleset, $params=FALSE, $index=FALSE ) {
+    if( empty( $exruleset )) if( $this->getConfig( 'allowEmpty' )) $exruleset = null; else return FALSE;
+    iCalUtilityFunctions::_setMval( $this->exrule, iCalUtilityFunctions::_setRexrule( $exruleset ), $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: FREEBUSY
+ */
+/**
+ * creates formatted output for calendar component property freebusy
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createFreebusy() {
+    if( empty( $this->freebusy )) return FALSE;
+    $output = null;
+    foreach( $this->freebusy as $freebusyPart ) {
+      if( empty( $freebusyPart['value'] )) {
+        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' );
+        continue;
+      }
+      $attributes = $content = null;
+      if( isset( $freebusyPart['value']['fbtype'] )) {
+        $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype'];
+        unset( $freebusyPart['value']['fbtype'] );
+        $freebusyPart['value'] = array_values( $freebusyPart['value'] );
+      }
+      else
+        $attributes .= $this->intAttrDelimiter.'FBTYPE=BUSY';
+      $attributes .= $this->_createParams( $freebusyPart['params'] );
+      $fno = 1;
+      $cnt = count( $freebusyPart['value']);
+      foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) {
+        $formatted   = iCalUtilityFunctions::_format_date_time( $freebusyPeriod[0] );
+        $content .= $formatted;
+        $content .= '/';
+        $cnt2 = count( $freebusyPeriod[1]);
+        if( array_key_exists( 'year', $freebusyPeriod[1] ))      // date-time
+          $cnt2 = 7;
+        elseif( array_key_exists( 'week', $freebusyPeriod[1] ))  // duration
+          $cnt2 = 5;
+        if(( 7 == $cnt2 )   &&    // period=  -> date-time
+            isset( $freebusyPeriod[1]['year'] )  &&
+            isset( $freebusyPeriod[1]['month'] ) &&
+            isset( $freebusyPeriod[1]['day'] )) {
+          $content .= iCalUtilityFunctions::_format_date_time( $freebusyPeriod[1] );
+        }
+        else {                                  // period=  -> dur-time
+          $content .= iCalUtilityFunctions::_format_duration( $freebusyPeriod[1] );
+        }
+        if( $fno < $cnt )
+          $content .= ',';
+        $fno++;
+      }
+      $output .= $this->_createElement( 'FREEBUSY', $attributes, $content );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property freebusy
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-05
+ * @param string $fbType
+ * @param array $fbValues
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setFreebusy( $fbType, $fbValues, $params=FALSE, $index=FALSE ) {
+    if( empty( $fbValues )) {
+      if( $this->getConfig( 'allowEmpty' )) {
+        iCalUtilityFunctions::_setMval( $this->freebusy, null, $params, FALSE, $index );
+        return TRUE;
+      }
+      else
+        return FALSE;
+    }
+    $fbType = strtoupper( $fbType );
+    if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) &&
+       ( 'X-' != substr( $fbType, 0, 2 )))
+      $fbType = 'BUSY';
+    $input = array( 'fbtype' => $fbType );
+    foreach( $fbValues as $fbPeriod ) {   // periods => period
+      $freebusyPeriod = array();
+      foreach( $fbPeriod as $fbMember ) { // pairs => singlepart
+        $freebusyPairMember = array();
+        if( is_array( $fbMember )) {
+          if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value
+            $freebusyPairMember       = iCalUtilityFunctions::_date_time_array( $fbMember, 7 );
+            $freebusyPairMember['tz'] = 'Z';
+          }
+          elseif( iCalUtilityFunctions::_isArrayTimestampDate( $fbMember )) { // timestamp value
+            $freebusyPairMember       = iCalUtilityFunctions::_timestamp2date( $fbMember['timestamp'], 7 );
+            $freebusyPairMember['tz'] = 'Z';
+          }
+          else {                                         // array format duration
+            $freebusyPairMember = iCalUtilityFunctions::_duration_array( $fbMember );
+          }
+        }
+        elseif(( 3 <= strlen( trim( $fbMember ))) &&    // string format duration
+               ( in_array( $fbMember{0}, array( 'P', '+', '-' )))) {
+          if( 'P' != $fbMember{0} )
+            $fbmember = substr( $fbMember, 1 );
+          $freebusyPairMember = iCalUtilityFunctions::_duration_string( $fbMember );
+        }
+        elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18
+          $freebusyPairMember       = iCalUtilityFunctions::_date_time_string( $fbMember, 7 );
+          $freebusyPairMember['tz'] = 'Z';
+        }
+        $freebusyPeriod[]   = $freebusyPairMember;
+      }
+      $input[]              = $freebusyPeriod;
+    }
+    iCalUtilityFunctions::_setMval( $this->freebusy, $input, $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: GEO
+ */
+/**
+ * creates formatted output for calendar component property geo
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createGeo() {
+    if( empty( $this->geo )) return FALSE;
+    if( empty( $this->geo['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE;
+    $attributes = $this->_createParams( $this->geo['params'] );
+    $content    = null;
+    $content   .= number_format( (float) $this->geo['value']['latitude'], 6, '.', '');
+    $content   .= ';';
+    $content   .= number_format( (float) $this->geo['value']['longitude'], 6, '.', '');
+    return $this->_createElement( 'GEO', $attributes, $content );
+  }
+/**
+ * set calendar component property geo
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param float $latitude
+ * @param float $longitude
+ * @param array $params optional
+ * @return bool
+ */
+  function setGeo( $latitude, $longitude, $params=FALSE ) {
+    if( !empty( $latitude ) && !empty( $longitude )) {
+      if( !is_array( $this->geo )) $this->geo = array();
+      $this->geo['value']['latitude']  = $latitude;
+      $this->geo['value']['longitude'] = $longitude;
+      $this->geo['params'] = iCalUtilityFunctions::_setParams( $params );
+    }
+    elseif( $this->getConfig( 'allowEmpty' ))
+      $this->geo = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) );
+    else
+      return FALSE;
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: LAST-MODIFIED
+ */
+/**
+ * creates formatted output for calendar component property last-modified
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createLastModified() {
+    if( empty( $this->lastmodified )) return FALSE;
+    $attributes = $this->_createParams( $this->lastmodified['params'] );
+    $formatted  = iCalUtilityFunctions::_format_date_time( $this->lastmodified['value'], 7 );
+    return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted );
+  }
+/**
+ * set calendar component property completed
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @param mixed $year optional
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param array $params optional
+ * @return boll
+ */
+  function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
+    if( empty( $year ))
+      $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' )));
+    $this->lastmodified = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: LOCATION
+ */
+/**
+ * creates formatted output for calendar component property location
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createLocation() {
+    if( empty( $this->location )) return FALSE;
+    if( empty( $this->location['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'LOCATION' ) : FALSE;
+    $attributes = $this->_createParams( $this->location['params'], array( 'ALTREP', 'LANGUAGE' ));
+    $content    = $this->_strrep( $this->location['value'] );
+    return $this->_createElement( 'LOCATION', $attributes, $content );
+  }
+/**
+ * set calendar component property location
+ '
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param array params optional
+ * @return bool
+ */
+  function setLocation( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->location = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: ORGANIZER
+ */
+/**
+ * creates formatted output for calendar component property organizer
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.33 - 2010-12-17
+ * @return string
+ */
+  function createOrganizer() {
+    if( empty( $this->organizer )) return FALSE;
+    if( empty( $this->organizer['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ORGANIZER' ) : FALSE;
+    $attributes = $this->_createParams( $this->organizer['params']
+                                      , array( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' ));
+    return $this->_createElement( 'ORGANIZER', $attributes, $this->organizer['value'] );
+  }
+/**
+ * set calendar component property organizer
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-11-29
+ * @param string $value
+ * @param array params optional
+ * @return bool
+ */
+  function setOrganizer( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
+      $value = 'MAILTO:'.$value;
+    else
+      $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos );
+    $value = str_replace( 'mailto:', 'MAILTO:', $value );
+    $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    if( isset( $this->organizer['params']['SENT-BY'] )){
+      if( 'mailto:' !== strtolower( substr( $this->organizer['params']['SENT-BY'], 0, 7 )))
+        $this->organizer['params']['SENT-BY'] = 'MAILTO:'.$this->organizer['params']['SENT-BY'];
+      else
+        $this->organizer['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer['params']['SENT-BY'], 7 );
+    }
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: PERCENT-COMPLETE
+ */
+/**
+ * creates formatted output for calendar component property percent-complete
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @return string
+ */
+  function createPercentComplete() {
+    if( empty( $this->percentcomplete )) return FALSE;
+    if( empty( $this->percentcomplete['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PERCENT-COMPLETE' ) : FALSE;
+    $attributes = $this->_createParams( $this->percentcomplete['params'] );
+    return $this->_createElement( 'PERCENT-COMPLETE', $attributes, $this->percentcomplete['value'] );
+  }
+/**
+ * set calendar component property percent-complete
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param int $value
+ * @param array $params optional
+ * @return bool
+ */
+  function setPercentComplete( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->percentcomplete = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: PRIORITY
+ */
+/**
+ * creates formatted output for calendar component property priority
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createPriority() {
+    if( empty( $this->priority )) return FALSE;
+    if( empty( $this->priority['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PRIORITY' ) : FALSE;
+    $attributes = $this->_createParams( $this->priority['params'] );
+    return $this->_createElement( 'PRIORITY', $attributes, $this->priority['value'] );
+  }
+/**
+ * set calendar component property priority
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param int $value
+ * @param array $params optional
+ * @return bool
+ */
+  function setPriority( $value, $params=FALSE  ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->priority = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: RDATE
+ */
+/**
+ * creates formatted output for calendar component property rdate
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-10-26
+ * @return string
+ */
+  function createRdate() {
+    if( empty( $this->rdate )) return FALSE;
+    $utctime = ( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
+    $output = null;
+    if( $utctime  )
+      unset( $this->rdate['params']['TZID'] );
+    foreach( $this->rdate as $theRdate ) {
+      if( empty( $theRdate['value'] )) {
+        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' );
+        continue;
+      }
+      if( $utctime  )
+        unset( $theRdate['params']['TZID'] );
+      $attributes = $this->_createParams( $theRdate['params'] );
+      $cnt = count( $theRdate['value'] );
+      $content = null;
+      $rno = 1;
+      foreach( $theRdate['value'] as $rpix => $rdatePart ) {
+        $contentPart = null;
+        if( is_array( $rdatePart ) &&
+            isset( $theRdate['params']['VALUE'] ) && ( 'PERIOD' == $theRdate['params']['VALUE'] )) { // PERIOD
+          if( $utctime )
+            unset( $rdatePart[0]['tz'] );
+          $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart[0]); // PERIOD part 1
+          if( $utctime || !empty( $theRdate['params']['TZID'] ))
+            $formatted = str_replace( 'Z', '', $formatted);
+          if( 0 < $rpix ) {
+            if( !empty( $rdatePart[0]['tz'] ) && iCalUtilityFunctions::_isOffset( $rdatePart[0]['tz'] )) {
+              if( 'Z' != substr( $formatted, -1 )) $formatted .= 'Z';
+            }
+            else
+              $formatted = str_replace( 'Z', '', $formatted );
+          }
+          $contentPart .= $formatted;
+          $contentPart .= '/';
+          $cnt2 = count( $rdatePart[1]);
+          if( array_key_exists( 'year', $rdatePart[1] )) {
+            if( array_key_exists( 'hour', $rdatePart[1] ))
+              $cnt2 = 7;                                      // date-time
+            else
+              $cnt2 = 3;                                      // date
+          }
+          elseif( array_key_exists( 'week', $rdatePart[1] ))  // duration
+            $cnt2 = 5;
+          if(( 7 == $cnt2 )   &&    // period=  -> date-time
+              isset( $rdatePart[1]['year'] )  &&
+              isset( $rdatePart[1]['month'] ) &&
+              isset( $rdatePart[1]['day'] )) {
+            if( $utctime )
+              unset( $rdatePart[1]['tz'] );
+            $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart[1] ); // PERIOD part 2
+            if( $utctime || !empty( $theRdate['params']['TZID'] ))
+              $formatted = str_replace( 'Z', '', $formatted);
+            if( !empty( $rdatePart[0]['tz'] ) && iCalUtilityFunctions::_isOffset( $rdatePart[0]['tz'] )) {
+              if( 'Z' != substr( $formatted, -1 )) $formatted .= 'Z';
+            }
+            else
+              $formatted = str_replace( 'Z', '', $formatted );
+           $contentPart .= $formatted;
+          }
+          else {                                  // period=  -> dur-time
+            $contentPart .= iCalUtilityFunctions::_format_duration( $rdatePart[1] );
+          }
+        } // PERIOD end
+        else { // SINGLE date start
+          if( $utctime )
+            unset( $rdatePart['tz'] );
+          $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart);
+          if( $utctime || !empty( $theRdate['params']['TZID'] ))
+            $formatted = str_replace( 'Z', '', $formatted);
+          if( !$utctime && ( 0 < $rpix )) {
+            if( !empty( $theRdate['value'][0]['tz'] ) && iCalUtilityFunctions::_isOffset( $theRdate['value'][0]['tz'] )) {
+              if( 'Z' != substr( $formatted, -1 ))
+                $formatted .= 'Z';
+            }
+            else
+              $formatted = str_replace( 'Z', '', $formatted );
+          }
+          $contentPart .= $formatted;
+        }
+        $content .= $contentPart;
+        if( $rno < $cnt )
+          $content .= ',';
+        $rno++;
+      }
+      $output    .= $this->_createElement( 'RDATE', $attributes, $content );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property rdate
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-07
+ * @param array $rdates
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setRdate( $rdates, $params=FALSE, $index=FALSE ) {
+    if( empty( $rdates )) {
+      if( $this->getConfig( 'allowEmpty' )) {
+        iCalUtilityFunctions::_setMval( $this->rdate, null, $params, FALSE, $index );
+        return TRUE;
+      }
+      else
+        return FALSE;
+    }
+    $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
+    if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) {
+      unset( $input['params']['TZID'] );
+      $input['params']['VALUE'] = 'DATE-TIME';
+    }
+            /*  check if PERIOD, if not set */
+    if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) &&
+          isset( $rdates[0] )    && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) &&
+          isset( $rdates[0][0] ) &&    isset( $rdates[0][1] ) && !isset( $rdates[0]['timestamp'] ) &&
+    (( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0]['timestamp'] ) ||
+                                      iCalUtilityFunctions::_isArrayDate( $rdates[0][0] ))) ||
+                                    ( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] )))))  &&
+     ( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] ))))))
+      $input['params']['VALUE'] = 'PERIOD';
+            /* check 1:st date, upd. $parno (opt) and save ev. timezone **/
+    $date  = reset( $rdates );
+    if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) // PERIOD
+      $date  = reset( $date );
+    iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] );
+    if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' )))
+      unset( $input['params']['TZID'] );
+    iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default
+    foreach( $rdates as $rpix => $theRdate ) {
+      $inputa = null;
+      if( is_array( $theRdate )) {
+        if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD
+          foreach( $theRdate as $rix => $rPeriod ) {
+            if( is_array( $rPeriod )) {
+              if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod ))      // timestamp
+                $inputab  = ( isset( $rPeriod['tz'] )) ? iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno ) : iCalUtilityFunctions::_timestamp2date( $rPeriod, 6 );
+              elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod ))
+                $inputab  = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_date_time_array( $rPeriod, $parno ) : iCalUtilityFunctions::_date_time_array( $rPeriod, 6 );
+              elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod ))))  // text-date
+                $inputab  = iCalUtilityFunctions::_date_time_string( reset( $rPeriod ), $parno );
+              else                                               // array format duration
+                $inputab  = iCalUtilityFunctions::_duration_array( $rPeriod );
+            }
+            elseif(( 3 <= strlen( trim( $rPeriod ))) &&          // string format duration
+                   ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) {
+              if( 'P' != $rPeriod[0] )
+                $rPeriod  = substr( $rPeriod, 1 );
+              $inputab    = iCalUtilityFunctions::_duration_string( $rPeriod );
+            }
+            elseif( 8 <= strlen( trim( $rPeriod )))              // text date ex. 2006-08-03 10:12:18
+              $inputab    = iCalUtilityFunctions::_date_time_string( $rPeriod, $parno );
+            if(  isset( $input['params']['TZID'] ) ||
+               ( isset( $inputab['tz'] )   && !iCalUtilityFunctions::_isOffset( $inputab['tz'] )) ||
+               ( isset( $inputa[0] )       && ( !isset( $inputa[0]['tz'] )))       ||
+               ( isset( $inputa[0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputa[0]['tz'] )))
+              unset( $inputab['tz'] );
+            $inputa[]     = $inputab;
+          }
+        } // PERIOD end
+        elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate ))      // timestamp
+          $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno );
+        else                                                                    // date[-time]
+          $inputa = iCalUtilityFunctions::_date_time_array( $theRdate, $parno );
+      }
+      elseif( 8 <= strlen( trim( $theRdate )))                   // text date ex. 2006-08-03 10:12:18
+        $inputa       = iCalUtilityFunctions::_date_time_string( $theRdate, $parno );
+      if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD
+        if( 3 == $parno )
+          unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] );
+        elseif( isset( $inputa['tz'] ))
+          $inputa['tz'] = (string) $inputa['tz'];
+        if(  isset( $input['params']['TZID'] ) ||
+           ( isset( $inputa['tz'] )            && !iCalUtilityFunctions::_isOffset( $inputa['tz'] ))     ||
+           ( isset( $input['value'][0] )       && ( !isset( $input['value'][0]['tz'] )))  ||
+           ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] )))
+          unset( $inputa['tz'] );
+      }
+      $input['value'][] = $inputa;
+    }
+    if( 3 == $parno ) {
+      $input['params']['VALUE'] = 'DATE';
+      unset( $input['params']['TZID'] );
+    }
+    iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: RECURRENCE-ID
+ */
+/**
+ * creates formatted output for calendar component property recurrence-id
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createRecurrenceid() {
+    if( empty( $this->recurrenceid )) return FALSE;
+    if( empty( $this->recurrenceid['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE;
+    $formatted  = iCalUtilityFunctions::_format_date_time( $this->recurrenceid['value'] );
+    $attributes = $this->_createParams( $this->recurrenceid['params'] );
+    return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted );
+  }
+/**
+ * set calendar component property recurrence-id
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @param mixed $year
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param array $params optional
+ * @return bool
+ */
+  function setRecurrenceid( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
+    if( empty( $year )) {
+      if( $this->getConfig( 'allowEmpty' )) {
+        $this->recurrenceid = array( 'value' => null, 'params' => null );
+        return TRUE;
+      }
+      else
+        return FALSE;
+    }
+    $this->recurrenceid = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: RELATED-TO
+ */
+/**
+ * creates formatted output for calendar component property related-to
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @return string
+ */
+  function createRelatedTo() {
+    if( empty( $this->relatedto )) return FALSE;
+    $output = null;
+    foreach( $this->relatedto as $relation ) {
+      if( empty( $relation['value'] )) {
+        if( $this->getConfig( 'allowEmpty' )) $output.= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ));
+        continue;
+      }
+      $attributes = $this->_createParams( $relation['params'] );
+      $content    = ( 'xcal' != $this->format ) ? '<' : '';
+      $content   .= $this->_strrep( $relation['value'] );
+      $content   .= ( 'xcal' != $this->format ) ? '>' : '';
+      $output    .= $this->_createElement( 'RELATED-TO', $attributes, $content );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property related-to
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-07
+ * @param float $relid
+ * @param array $params, optional
+ * @param index $index, optional
+ * @return bool
+ */
+  function setRelatedTo( $value, $params=FALSE, $index=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    if(( '<' == substr( $value, 0, 1 )) && ( '>' == substr( $value, -1 )))
+      $value = substr( $value, 1, ( strlen( $value ) - 2 ));
+    iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default
+    iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: REPEAT
+ */
+/**
+ * creates formatted output for calendar component property repeat
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createRepeat() {
+    if( empty( $this->repeat )) return FALSE;
+    if( empty( $this->repeat['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'REPEAT' ) : FALSE;
+    $attributes = $this->_createParams( $this->repeat['params'] );
+    return $this->_createElement( 'REPEAT', $attributes, $this->repeat['value'] );
+  }
+/**
+ * set calendar component property transp
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param array $params optional
+ * @return void
+ */
+  function setRepeat( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->repeat = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: REQUEST-STATUS
+ */
+/**
+ * creates formatted output for calendar component property request-status
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @return string
+ */
+  function createRequestStatus() {
+    if( empty( $this->requeststatus )) return FALSE;
+    $output = null;
+    foreach( $this->requeststatus as $rstat ) {
+      if( empty( $rstat['value']['statcode'] )) {
+        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' );
+        continue;
+      }
+      $attributes  = $this->_createParams( $rstat['params'], array( 'LANGUAGE' ));
+      $content     = number_format( (float) $rstat['value']['statcode'], 2, '.', '');
+      $content    .= ';'.$this->_strrep( $rstat['value']['text'] );
+      if( isset( $rstat['value']['extdata'] ))
+        $content  .= ';'.$this->_strrep( $rstat['value']['extdata'] );
+      $output     .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property request-status
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-05
+ * @param float $statcode
+ * @param string $text
+ * @param string $extdata, optional
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setRequestStatus( $statcode, $text, $extdata=FALSE, $params=FALSE, $index=FALSE ) {
+    if( empty( $statcode ) || empty( $text )) if( $this->getConfig( 'allowEmpty' )) $statcode = $text = null; else return FALSE;
+    $input              = array( 'statcode' => $statcode, 'text' => $text );
+    if( $extdata )
+      $input['extdata'] = $extdata;
+    iCalUtilityFunctions::_setMval( $this->requeststatus, $input, $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: RESOURCES
+ */
+/**
+ * creates formatted output for calendar component property resources
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-23
+ * @return string
+ */
+  function createResources() {
+    if( empty( $this->resources )) return FALSE;
+    $output = null;
+    foreach( $this->resources as $resource ) {
+      if( empty( $resource['value'] )) {
+        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' );
+        continue;
+      }
+      $attributes  = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' ));
+      if( is_array( $resource['value'] )) {
+        foreach( $resource['value'] as $rix => $resourcePart )
+          $resource['value'][$rix] = $this->_strrep( $resourcePart );
+        $content   = implode( ',', $resource['value'] );
+      }
+      else
+        $content   = $this->_strrep( $resource['value'] );
+      $output     .= $this->_createElement( 'RESOURCES', $attributes, $content );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property recources
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-05
+ * @param mixed $value
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setResources( $value, $params=FALSE, $index=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    iCalUtilityFunctions::_setMval( $this->resources, $value, $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: RRULE
+ */
+/**
+ * creates formatted output for calendar component property rrule
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createRrule() {
+    if( empty( $this->rrule )) return FALSE;
+    return $this->_format_recur( 'RRULE', $this->rrule );
+  }
+/**
+ * set calendar component property rrule
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-05
+ * @param array $rruleset
+ * @param array $params, optional
+ * @param integer $index, optional
+ * @return void
+ */
+  function setRrule( $rruleset, $params=FALSE, $index=FALSE ) {
+    if( empty( $rruleset )) if( $this->getConfig( 'allowEmpty' )) $rruleset = null; else return FALSE;
+    iCalUtilityFunctions::_setMval( $this->rrule, iCalUtilityFunctions::_setRexrule( $rruleset ), $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: SEQUENCE
+ */
+/**
+ * creates formatted output for calendar component property sequence
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.9 - 2009-12-30
+ * @return string
+ */
+  function createSequence() {
+    if( empty( $this->sequence )) return FALSE;
+    if( empty( $this->sequence['value'] ) && ( '0' != $this->sequence['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SEQUENCE' ) : FALSE;
+    $attributes = $this->_createParams( $this->sequence['params'] );
+    return $this->_createElement( 'SEQUENCE', $attributes, $this->sequence['value'] );
+  }
+/**
+ * set calendar component property sequence
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.9 - 2009-12-30
+ * @param int $value optional
+ * @param array $params optional
+ * @return bool
+ */
+  function setSequence( $value=FALSE, $params=FALSE ) {
+    if( empty( $value ) && ( '0' != $value ))
+      $value = ( isset( $this->sequence['value'] ) && ( 0 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : 1;
+    $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: STATUS
+ */
+/**
+ * creates formatted output for calendar component property status
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createStatus() {
+    if( empty( $this->status )) return FALSE;
+    if( empty( $this->status['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'STATUS' ) : FALSE;
+    $attributes = $this->_createParams( $this->status['params'] );
+    return $this->_createElement( 'STATUS', $attributes, $this->status['value'] );
+  }
+/**
+ * set calendar component property status
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param array $params optional
+ * @return bool
+ */
+  function setStatus( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->status = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: SUMMARY
+ */
+/**
+ * creates formatted output for calendar component property summary
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createSummary() {
+    if( empty( $this->summary )) return FALSE;
+    if( empty( $this->summary['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SUMMARY' ) : FALSE;
+    $attributes = $this->_createParams( $this->summary['params'], array( 'ALTREP', 'LANGUAGE' ));
+    $content    = $this->_strrep( $this->summary['value'] );
+    return $this->_createElement( 'SUMMARY', $attributes, $content );
+  }
+/**
+ * set calendar component property summary
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param string $params optional
+ * @return bool
+ */
+  function setSummary( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->summary = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: TRANSP
+ */
+/**
+ * creates formatted output for calendar component property transp
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createTransp() {
+    if( empty( $this->transp )) return FALSE;
+    if( empty( $this->transp['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRANSP' ) : FALSE;
+    $attributes = $this->_createParams( $this->transp['params'] );
+    return $this->_createElement( 'TRANSP', $attributes, $this->transp['value'] );
+  }
+/**
+ * set calendar component property transp
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param string $params optional
+ * @return bool
+ */
+  function setTransp( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->transp = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: TRIGGER
+ */
+/**
+ * creates formatted output for calendar component property trigger
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-10-21
+ * @return string
+ */
+  function createTrigger() {
+    if( empty( $this->trigger )) return FALSE;
+    if( empty( $this->trigger['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRIGGER' ) : FALSE;
+    $content = $attributes = null;
+    if( isset( $this->trigger['value']['year'] )   &&
+        isset( $this->trigger['value']['month'] )  &&
+        isset( $this->trigger['value']['day'] ))
+      $content      .= iCalUtilityFunctions::_format_date_time( $this->trigger['value'] );
+    else {
+      if( TRUE !== $this->trigger['value']['relatedStart'] )
+        $attributes .= $this->intAttrDelimiter.'RELATED=END';
+      if( $this->trigger['value']['before'] )
+        $content    .= '-';
+      $content      .= iCalUtilityFunctions::_format_duration( $this->trigger['value'] );
+    }
+    $attributes     .= $this->_createParams( $this->trigger['params'] );
+    return $this->_createElement( 'TRIGGER', $attributes, $content );
+  }
+/**
+ * set calendar component property trigger
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.16 - 2008-11-04
+ * @param mixed $year
+ * @param mixed $month optional
+ * @param int $day optional
+ * @param int $week optional
+ * @param int $hour optional
+ * @param int $min optional
+ * @param int $sec optional
+ * @param bool $relatedStart optional
+ * @param bool $before optional
+ * @param array $params optional
+ * @return bool
+ */
+  function setTrigger( $year, $month=null, $day=null, $week=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $relatedStart=TRUE, $before=TRUE, $params=FALSE ) {
+    if( empty( $year ) && empty( $month ) && empty( $day ) && empty( $week ) && empty( $hour ) && empty( $min ) && empty( $sec ))
+      if( $this->getConfig( 'allowEmpty' )) {
+        $this->trigger = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) );
+        return TRUE;
+      }
+      else
+        return FALSE;
+    if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp
+      $params = iCalUtilityFunctions::_setParams( $month );
+      $date   = iCalUtilityFunctions::_timestamp2date( $year, 7 );
+      foreach( $date as $k => $v )
+        $$k = $v;
+    }
+    elseif( is_array( $year ) && ( is_array( $month ) || empty( $month ))) {
+      $params = iCalUtilityFunctions::_setParams( $month );
+      if(!(array_key_exists( 'year',  $year ) &&   // exclude date-time
+           array_key_exists( 'month', $year ) &&
+           array_key_exists( 'day',   $year ))) {  // so this must be a duration
+        if( isset( $params['RELATED'] ) && ( 'END' == $params['RELATED'] ))
+          $relatedStart = FALSE;
+        else
+          $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ? FALSE : TRUE;
+        $before         = ( array_key_exists( 'before', $year )       && ( TRUE !== $year['before'] ))       ? FALSE : TRUE;
+      }
+      $SSYY  = ( array_key_exists( 'year',  $year )) ? $year['year']  : null;
+      $month = ( array_key_exists( 'month', $year )) ? $year['month'] : null;
+      $day   = ( array_key_exists( 'day',   $year )) ? $year['day']   : null;
+      $week  = ( array_key_exists( 'week',  $year )) ? $year['week']  : null;
+      $hour  = ( array_key_exists( 'hour',  $year )) ? $year['hour']  : 0; //null;
+      $min   = ( array_key_exists( 'min',   $year )) ? $year['min']   : 0; //null;
+      $sec   = ( array_key_exists( 'sec',   $year )) ? $year['sec']   : 0; //null;
+      $year  = $SSYY;
+    }
+    elseif(is_string( $year ) && ( is_array( $month ) || empty( $month ))) {  // duration or date in a string
+      $params = iCalUtilityFunctions::_setParams( $month );
+      if( in_array( $year{0}, array( 'P', '+', '-' ))) { // duration
+        $relatedStart = ( isset( $params['RELATED'] ) && ( 'END' == $params['RELATED'] )) ? FALSE : TRUE;
+        $before       = ( '-'  == $year{0} ) ? TRUE : FALSE;
+        if(     'P'  != $year{0} )
+          $year       = substr( $year, 1 );
+        $date         = iCalUtilityFunctions::_duration_string( $year);
+      }
+      else   // date
+        $date    = iCalUtilityFunctions::_date_time_string( $year, 7 );
+      unset( $year, $month, $day );
+      foreach( $date as $k => $v )
+        $$k = $v;
+    }
+    else // single values in function input parameters
+      $params = iCalUtilityFunctions::_setParams( $params );
+    if( !empty( $year ) && !empty( $month ) && !empty( $day )) { // date
+      $params['VALUE'] = 'DATE-TIME';
+      $hour = ( $hour ) ? $hour : 0;
+      $min  = ( $min  ) ? $min  : 0;
+      $sec  = ( $sec  ) ? $sec  : 0;
+      $this->trigger = array( 'params' => $params );
+      $this->trigger['value'] = array( 'year'  => $year
+                                     , 'month' => $month
+                                     , 'day'   => $day
+                                     , 'hour'  => $hour
+                                     , 'min'   => $min
+                                     , 'sec'   => $sec
+                                     , 'tz'    => 'Z' );
+      return TRUE;
+    }
+    elseif(( empty( $year ) && empty( $month )) &&    // duration
+           (!empty( $week ) || !empty( $day ) || !empty( $hour ) || !empty( $min ) || !empty( $sec ))) {
+      unset( $params['RELATED'] ); // set at output creation (END only)
+      unset( $params['VALUE'] );   // 'DURATION' default
+      $this->trigger = array( 'params' => $params );
+      $relatedStart = ( FALSE !== $relatedStart ) ? TRUE : FALSE;
+      $before       = ( FALSE !== $before )       ? TRUE : FALSE;
+      $this->trigger['value']  = array( 'relatedStart' => $relatedStart
+                                      , 'before'       => $before );
+      if( !empty( $week )) $this->trigger['value']['week'] = $week;
+      if( !empty( $day  )) $this->trigger['value']['day']  = $day;
+      if( !empty( $hour )) $this->trigger['value']['hour'] = $hour;
+      if( !empty( $min  )) $this->trigger['value']['min']  = $min;
+      if( !empty( $sec  )) $this->trigger['value']['sec']  = $sec;
+      return TRUE;
+    }
+    return FALSE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: TZID
+ */
+/**
+ * creates formatted output for calendar component property tzid
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createTzid() {
+    if( empty( $this->tzid )) return FALSE;
+    if( empty( $this->tzid['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZID' ) : FALSE;
+    $attributes = $this->_createParams( $this->tzid['params'] );
+    return $this->_createElement( 'TZID', $attributes, $this->_strrep( $this->tzid['value'] ));
+  }
+/**
+ * set calendar component property tzid
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param array $params optional
+ * @return bool
+ */
+  function setTzid( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->tzid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * .. .
+ * Property Name: TZNAME
+ */
+/**
+ * creates formatted output for calendar component property tzname
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createTzname() {
+    if( empty( $this->tzname )) return FALSE;
+    $output = null;
+    foreach( $this->tzname as $theName ) {
+      if( !empty( $theName['value'] )) {
+        $attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' ));
+        $output    .= $this->_createElement( 'TZNAME', $attributes, $this->_strrep( $theName['value'] ));
+      }
+      elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property tzname
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-05
+ * @param string $value
+ * @param string $params, optional
+ * @param integer $index, optional
+ * @return bool
+ */
+  function setTzname( $value, $params=FALSE, $index=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    iCalUtilityFunctions::_setMval( $this->tzname, $value, $params, FALSE, $index );
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: TZOFFSETFROM
+ */
+/**
+ * creates formatted output for calendar component property tzoffsetfrom
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createTzoffsetfrom() {
+    if( empty( $this->tzoffsetfrom )) return FALSE;
+    if( empty( $this->tzoffsetfrom['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETFROM' ) : FALSE;
+    $attributes = $this->_createParams( $this->tzoffsetfrom['params'] );
+    return $this->_createElement( 'TZOFFSETFROM', $attributes, $this->tzoffsetfrom['value'] );
+  }
+/**
+ * set calendar component property tzoffsetfrom
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param string $params optional
+ * @return bool
+ */
+  function setTzoffsetfrom( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->tzoffsetfrom = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: TZOFFSETTO
+ */
+/**
+ * creates formatted output for calendar component property tzoffsetto
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createTzoffsetto() {
+    if( empty( $this->tzoffsetto )) return FALSE;
+    if( empty( $this->tzoffsetto['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETTO' ) : FALSE;
+    $attributes = $this->_createParams( $this->tzoffsetto['params'] );
+    return $this->_createElement( 'TZOFFSETTO', $attributes, $this->tzoffsetto['value'] );
+  }
+/**
+ * set calendar component property tzoffsetto
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param string $params optional
+ * @return bool
+ */
+  function setTzoffsetto( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->tzoffsetto = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: TZURL
+ */
+/**
+ * creates formatted output for calendar component property tzurl
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createTzurl() {
+    if( empty( $this->tzurl )) return FALSE;
+    if( empty( $this->tzurl['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZURL' ) : FALSE;
+    $attributes = $this->_createParams( $this->tzurl['params'] );
+    return $this->_createElement( 'TZURL', $attributes, $this->tzurl['value'] );
+  }
+/**
+ * set calendar component property tzurl
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param string $params optional
+ * @return boll
+ */
+  function setTzurl( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->tzurl = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: UID
+ */
+/**
+ * creates formatted output for calendar component property uid
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 0.9.7 - 2006-11-20
+ * @return string
+ */
+  function createUid() {
+    if( 0 >= count( $this->uid ))
+      $this->_makeuid();
+    $attributes = $this->_createParams( $this->uid['params'] );
+    return $this->_createElement( 'UID', $attributes, $this->uid['value'] );
+  }
+/**
+ * create an unique id for this calendar component object instance
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.2.7 - 2007-09-04
+ * @return void
+ */
+  function _makeUid() {
+    $date   = date('Ymd\THisT');
+    $unique = substr(microtime(), 2, 4);
+    $base   = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890';
+    $start  = 0;
+    $end    = strlen( $base ) - 1;
+    $length = 6;
+    $str    = null;
+    for( $p = 0; $p < $length; $p++ )
+      $unique .= $base{mt_rand( $start, $end )};
+    $this->uid = array( 'params' => null );
+    
+    /*
+     * Modificado por Cristiano Correa Schmidt
+     */
+        //$this->uid['value']  = $date.'-'.$unique.'@'.$this->getConfig( 'unique_id' );
+    $this->uid['value']  = $this->getConfig( 'unique_id' );
+  }
+/**
+ * set calendar component property uid
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param string $params optional
+ * @return bool
+ */
+  function setUid( $value, $params=FALSE ) {
+    if( empty( $value )) return FALSE; // no allowEmpty check here !!!!
+    $this->uid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: URL
+ */
+/**
+ * creates formatted output for calendar component property url
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-21
+ * @return string
+ */
+  function createUrl() {
+    if( empty( $this->url )) return FALSE;
+    if( empty( $this->url['value'] ))
+      return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'URL' ) : FALSE;
+    $attributes = $this->_createParams( $this->url['params'] );
+    return $this->_createElement( 'URL', $attributes, $this->url['value'] );
+  }
+/**
+ * set calendar component property url
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-11-04
+ * @param string $value
+ * @param string $params optional
+ * @return bool
+ */
+  function setUrl( $value, $params=FALSE ) {
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $this->url = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
+    return TRUE;
+  }
+/*********************************************************************************/
+/**
+ * Property Name: x-prop
+ */
+/**
+ * creates formatted output for calendar component property x-prop
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.11 - 2008-10-22
+ * @return string
+ */
+  function createXprop() {
+    if( empty( $this->xprop )) return FALSE;
+    $output = null;
+    foreach( $this->xprop as $label => $xpropPart ) {
+      if( empty( $xpropPart['value'] )) {
+        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $label );
+        continue;
+      }
+      $attributes = $this->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
+      if( is_array( $xpropPart['value'] )) {
+        foreach( $xpropPart['value'] as $pix => $theXpart )
+          $xpropPart['value'][$pix] = $this->_strrep( $theXpart );
+        $xpropPart['value']  = implode( ',', $xpropPart['value'] );
+      }
+      else
+        $xpropPart['value'] = $this->_strrep( $xpropPart['value'] );
+      $output    .= $this->_createElement( $label, $attributes, $xpropPart['value'] );
+    }
+    return $output;
+  }
+/**
+ * set calendar component property x-prop
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.22 - 2010-09-25
+ * @param string $label
+ * @param mixed $value
+ * @param array $params optional
+ * @return bool
+ */
+  function setXprop( $label, $value, $params=FALSE ) {
+    if( empty( $label )) return;
+    if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
+    $xprop           = array( 'value' => $value );
+    $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
+    if( !is_array( $this->xprop )) $this->xprop = array();
+    $this->xprop[strtoupper( $label )] = $xprop;
+    return TRUE;
+  }
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ * create element format parts
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.0.6 - 2006-06-20
+ * @return string
+ */
+  function _createFormat() {
+    $objectname                   = null;
+    switch( $this->format ) {
+      case 'xcal':
+        $objectname               = ( isset( $this->timezonetype )) ?
+                                 strtolower( $this->timezonetype )  :  strtolower( $this->objName );
+        $this->componentStart1    = $this->elementStart1 = '<';
+        $this->componentStart2    = $this->elementStart2 = '>';
+        $this->componentEnd1      = $this->elementEnd1   = '</';
+        $this->componentEnd2      = $this->elementEnd2   = '>'.$this->nl;
+        $this->intAttrDelimiter   = '<!-- -->';
+        $this->attributeDelimiter = $this->nl;
+        $this->valueInit          = null;
+        break;
+      default:
+        $objectname               = ( isset( $this->timezonetype )) ?
+                                 strtoupper( $this->timezonetype )  :  strtoupper( $this->objName );
+        $this->componentStart1    = 'BEGIN:';
+        $this->componentStart2    = null;
+        $this->componentEnd1      = 'END:';
+        $this->componentEnd2      = $this->nl;
+        $this->elementStart1      = null;
+        $this->elementStart2      = null;
+        $this->elementEnd1        = null;
+        $this->elementEnd2        = $this->nl;
+        $this->intAttrDelimiter   = '<!-- -->';
+        $this->attributeDelimiter = ';';
+        $this->valueInit          = ':';
+        break;
+    }
+    return $objectname;
+  }
+/**
+ * creates formatted output for calendar component property
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.22 - 2010-12-06
+ * @param string $label property name
+ * @param string $attributes property attributes
+ * @param string $content property content (optional)
+ * @return string
+ */
+  function _createElement( $label, $attributes=null, $content=FALSE ) {
+    switch( $this->format ) {
+      case 'xcal':
+        $label = strtolower( $label );
+        break;
+      default:
+        $label = strtoupper( $label );
+        break;
+    }
+    $output = $this->elementStart1.$label;
+    $categoriesAttrLang = null;
+    $attachInlineBinary = FALSE;
+    $attachfmttype      = null;
+    if( !empty( $attributes ))  {
+      $attributes  = trim( $attributes );
+      if ( 'xcal' == $this->format) {
+        $attributes2 = explode( $this->intAttrDelimiter, $attributes );
+        $attributes  = null;
+        foreach( $attributes2 as $attribute ) {
+          $attrKVarr = explode( '=', $attribute );
+          if( empty( $attrKVarr[0] ))
+            continue;
+          if( !isset( $attrKVarr[1] )) {
+            $attrValue = $attrKVarr[0];
+            $attrKey   = null;
+          }
+          elseif( 2 == count( $attrKVarr)) {
+            $attrKey   = strtolower( $attrKVarr[0] );
+            $attrValue = $attrKVarr[1];
+          }
+          else {
+            $attrKey   = strtolower( $attrKVarr[0] );
+            unset( $attrKVarr[0] );
+            $attrValue = implode( '=', $attrKVarr );
+          }
+          if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) {
+            $attachInlineBinary = TRUE;
+            if( 'fmttype' == $attrKey )
+              $attachfmttype = $attrKey.'='.$attrValue;
+            continue;
+          }
+          elseif(( 'categories' == $label ) && ( 'language' == $attrKey ))
+            $categoriesAttrLang = $attrKey.'='.$attrValue;
+          else {
+            $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
+            $attributes .= ( !empty( $attrKey )) ? $attrKey.'=' : null;
+            if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, -1 ))) {
+              $attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 ));
+              $attrValue = str_replace( '"', '', $attrValue );
+            }
+            $attributes .= '"'.htmlspecialchars( $attrValue ).'"';
+          }
+        }
+      }
+      else {
+        $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes );
+      }
+    }
+    if(((( 'attach' == $label ) && !$attachInlineBinary ) ||
+         ( in_array( $label, array( 'tzurl', 'url' ))))      && ( 'xcal' == $this->format)) {
+      $pos = strrpos($content, "/");
+      $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content;
+      $this->xcaldecl[] = array( 'xmldecl'  => 'ENTITY'
+                               , 'uri'      => $docname
+                               , 'ref'      => 'SYSTEM'
+                               , 'external' => $content
+                               , 'type'     => 'NDATA'
+                               , 'type2'    => 'BINERY' );
+      $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
+      $attributes .= 'uri="'.$docname.'"';
+      $content = null;
+      if( 'attach' == $label ) {
+        $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes );
+        $content = $this->_createElement( 'extref', $attributes, null );
+        $attributes = null;
+      }
+    }
+    elseif(( 'attach' == $label ) && $attachInlineBinary && ( 'xcal' == $this->format)) {
+      $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute
+    }
+    $output .= $attributes;
+    if( !$content && ( '0' != $content )) {
+      switch( $this->format ) {
+        case 'xcal':
+          $output .= ' /';
+          $output .= $this->elementStart2;
+          return $output;
+          break;
+        default:
+          $output .= $this->elementStart2.$this->valueInit;
+          return $this->_size75( $output );
+          break;
+      }
+    }
+    $output .= $this->elementStart2;
+    $output .= $this->valueInit.$content;
+    switch( $this->format ) {
+      case 'xcal':
+        return $output.$this->elementEnd1.$label.$this->elementEnd2;
+        break;
+      default:
+        return $this->_size75( $output );
+        break;
+    }
+  }
+/**
+ * creates formatted output for calendar component property parameters
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.33 - 2010-12-18
+ * @param array $params  optional
+ * @param array $ctrKeys optional
+ * @return string
+ */
+  function _createParams( $params=array(), $ctrKeys=array() ) {
+    if( !is_array( $params ) || empty( $params ))
+      $params = array();
+    $attrLANG = $attr1 = $attr2 = $lang = null;
+    $CNattrKey   = ( in_array( 'CN',       $ctrKeys )) ? TRUE : FALSE ;
+    $LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ;
+    $CNattrExist = $LANGattrExist = FALSE;
+    $xparams = array();
+    foreach( $params as $paramKey => $paramValue ) {
+      if( ctype_digit( (string) $paramKey )) {
+        $xparams[]          = $paramValue;
+        continue;
+      }
+      $paramKey             = strtoupper( $paramKey );
+      if( !in_array( $paramKey, array( 'ALTREP', 'CN', 'DIR', 'ENCODING', 'FMTTYPE', 'LANGUAGE', 'RANGE', 'RELTYPE', 'SENT-BY', 'TZID', 'VALUE' )))
+        $xparams[$paramKey] = $paramValue;
+      else
+        $params[$paramKey]  = $paramValue;
+    }
+    ksort( $xparams, SORT_STRING );
+    foreach( $xparams as $paramKey => $paramValue ) {
+      if( ctype_digit( (string) $paramKey ))
+        $attr2             .= $this->intAttrDelimiter.$paramValue;
+      else
+        $attr2             .= $this->intAttrDelimiter."$paramKey=$paramValue";
+    }
+    if( isset( $params['FMTTYPE'] )  && !in_array( 'FMTTYPE', $ctrKeys )) {
+      $attr1               .= $this->intAttrDelimiter.'FMTTYPE='.$params['FMTTYPE'].$attr2;
+      $attr2                = null;
+    }
+    if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING',   $ctrKeys )) {
+      if( !empty( $attr2 )) {
+        $attr1             .= $attr2;
+        $attr2              = null;
+      }
+      $attr1               .= $this->intAttrDelimiter.'ENCODING='.$params['ENCODING'];
+    }
+    if( isset( $params['VALUE'] )    && !in_array( 'VALUE',   $ctrKeys ))
+      $attr1               .= $this->intAttrDelimiter.'VALUE='.$params['VALUE'];
+    if( isset( $params['TZID'] )     && !in_array( 'TZID',    $ctrKeys ))
+      $attr1               .= $this->intAttrDelimiter.'TZID='.$params['TZID'];
+    if( isset( $params['RANGE'] )    && !in_array( 'RANGE',   $ctrKeys ))
+      $attr1               .= $this->intAttrDelimiter.'RANGE='.$params['RANGE'];
+    if( isset( $params['RELTYPE'] )  && !in_array( 'RELTYPE', $ctrKeys ))
+      $attr1               .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE'];
+    if( isset( $params['CN'] )       && $CNattrKey ) {
+      $attr1                = $this->intAttrDelimiter.'CN="'.$params['CN'].'"';
+      $CNattrExist          = TRUE;
+    }
+    if( isset( $params['DIR'] )      && in_array( 'DIR',      $ctrKeys ))
+      $attr1               .= $this->intAttrDelimiter.'DIR="'.$params['DIR'].'"';
+    if( isset( $params['SENT-BY'] )  && in_array( 'SENT-BY',  $ctrKeys ))
+      $attr1               .= $this->intAttrDelimiter.'SENT-BY="'.$params['SENT-BY'].'"';
+    if( isset( $params['ALTREP'] )   && in_array( 'ALTREP',   $ctrKeys ))
+      $attr1               .= $this->intAttrDelimiter.'ALTREP="'.$params['ALTREP'].'"';
+    if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) {
+      $attrLANG            .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE'];
+      $LANGattrExist        = TRUE;
+    }
+    if( !$LANGattrExist ) {
+      $lang = $this->getConfig( 'language' );
+      if(( $CNattrExist || $LANGattrKey ) && $lang )
+        $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$lang;
+    }
+    return $attr1.$attrLANG.$attr2;
+  }
+/**
+ * creates formatted output for calendar component property data value type recur
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.4.8 - 2008-10-22
+ * @param array $recurlabel
+ * @param array $recurdata
+ * @return string
+ */
+  function _format_recur( $recurlabel, $recurdata ) {
+    $output = null;
+    foreach( $recurdata as $therule ) {
+      if( empty( $therule['value'] )) {
+        if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel );
+        continue;
+      }
+      $attributes = ( isset( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null;
+      $content1  = $content2  = null;
+      foreach( $therule['value'] as $rulelabel => $rulevalue ) {
+        switch( $rulelabel ) {
+          case 'FREQ': {
+            $content1 .= "FREQ=$rulevalue";
+            break;
+          }
+          case 'UNTIL': {
+            $content2 .= ";UNTIL=";
+            $content2 .= iCalUtilityFunctions::_format_date_time( $rulevalue );
+            break;
+          }
+          case 'COUNT':
+          case 'INTERVAL':
+          case 'WKST': {
+            $content2 .= ";$rulelabel=$rulevalue";
+            break;
+          }
+          case 'BYSECOND':
+          case 'BYMINUTE':
+          case 'BYHOUR':
+          case 'BYMONTHDAY':
+          case 'BYYEARDAY':
+          case 'BYWEEKNO':
+          case 'BYMONTH':
+          case 'BYSETPOS': {
+            $content2 .= ";$rulelabel=";
+            if( is_array( $rulevalue )) {
+              foreach( $rulevalue as $vix => $valuePart ) {
+                $content2 .= ( $vix ) ? ',' : null;
+                $content2 .= $valuePart;
+              }
+            }
+            else
+             $content2 .= $rulevalue;
+            break;
+          }
+          case 'BYDAY': {
+            $content2 .= ";$rulelabel=";
+            $bydaycnt = 0;
+            foreach( $rulevalue as $vix => $valuePart ) {
+              $content21 = $content22 = null;
+              if( is_array( $valuePart )) {
+                $content2 .= ( $bydaycnt ) ? ',' : null;
+                foreach( $valuePart as $vix2 => $valuePart2 ) {
+                  if( 'DAY' != strtoupper( $vix2 ))
+                      $content21 .= $valuePart2;
+                  else
+                    $content22 .= $valuePart2;
+                }
+                $content2 .= $content21.$content22;
+                $bydaycnt++;
+              }
+              else {
+                $content2 .= ( $bydaycnt ) ? ',' : null;
+                if( 'DAY' != strtoupper( $vix ))
+                    $content21 .= $valuePart;
+                else {
+                  $content22 .= $valuePart;
+                  $bydaycnt++;
+                }
+                $content2 .= $content21.$content22;
+              }
+            }
+            break;
+          }
+          default: {
+            $content2 .= ";$rulelabel=$rulevalue";
+            break;
+          }
+        }
+      }
+      $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 );
+    }
+    return $output;
+  }
+/**
+ * check if property not exists within component
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-15
+ * @param string $propName
+ * @return bool
+ */
+  function _notExistProp( $propName ) {
+    if( empty( $propName )) return FALSE; // when deleting x-prop, an empty propName may be used=allowed
+    $propName = strtolower( $propName );
+    if(     'last-modified'    == $propName )  { if( !isset( $this->lastmodified ))    return TRUE; }
+    elseif( 'percent-complete' == $propName )  { if( !isset( $this->percentcomplete )) return TRUE; }
+    elseif( 'recurrence-id'    == $propName )  { if( !isset( $this->recurrenceid ))    return TRUE; }
+    elseif( 'related-to'       == $propName )  { if( !isset( $this->relatedto ))       return TRUE; }
+    elseif( 'request-status'   == $propName )  { if( !isset( $this->requeststatus ))   return TRUE; }
+    elseif((       'x-' != substr($propName,0,2)) && !isset( $this->$propName ))       return TRUE;
+    return FALSE;
+  }
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ * get general component config variables or info about subcomponents
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-11-27
+ * @param mixed $config
+ * @return value
+ */
+  function getConfig( $config = FALSE) {
+    if( !$config ) {
+      $return = array();
+      $return['ALLOWEMPTY']  = $this->getConfig( 'ALLOWEMPTY' );
+      $return['FORMAT']      = $this->getConfig( 'FORMAT' );
+      if( FALSE !== ( $lang  = $this->getConfig( 'LANGUAGE' )))
+        $return['LANGUAGE']  = $lang;
+      $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
+      $return['UNIQUE_ID']   = $this->getConfig( 'UNIQUE_ID' );
+      return $return;
+    }
+    switch( strtoupper( $config )) {
+      case 'ALLOWEMPTY':
+        return $this->allowEmpty;
+        break;
+      case 'COMPSINFO':
+        unset( $this->compix );
+        $info = array();
+        if( isset( $this->components )) {
+          foreach( $this->components as $cix => $component ) {
+            if( empty( $component )) continue;
+            unset( $component->propix );
+            $info[$cix]['ordno'] = $cix + 1;
+            $info[$cix]['type']  = $component->objName;
+            $info[$cix]['uid']   = $component->getProperty( 'uid' );
+            $info[$cix]['props'] = $component->getConfig( 'propinfo' );
+            $info[$cix]['sub']   = $component->getConfig( 'compsinfo' );
+            unset( $component->propix );
+          }
+        }
+        return $info;
+        break;
+      case 'FORMAT':
+        return $this->format;
+        break;
+      case 'LANGUAGE':
+         // get language for calendar component as defined in [RFC 1766]
+        return $this->language;
+        break;
+      case 'NL':
+      case 'NEWLINECHAR':
+        return $this->nl;
+        break;
+      case 'PROPINFO':
+        $output = array();
+        if( !in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
+          if( empty( $this->uid['value'] )) $this->_makeuid();
+                                              $output['UID']              = 1;
+        }
+        if( !empty( $this->dtstamp ))         $output['DTSTAMP']          = 1;
+        if( !empty( $this->summary ))         $output['SUMMARY']          = 1;
+        if( !empty( $this->description ))     $output['DESCRIPTION']      = count( $this->description );
+        if( !empty( $this->dtstart ))         $output['DTSTART']          = 1;
+        if( !empty( $this->dtend ))           $output['DTEND']            = 1;
+        if( !empty( $this->due ))             $output['DUE']              = 1;
+        if( !empty( $this->duration ))        $output['DURATION']         = 1;
+        if( !empty( $this->rrule ))           $output['RRULE']            = count( $this->rrule );
+        if( !empty( $this->rdate ))           $output['RDATE']            = count( $this->rdate );
+        if( !empty( $this->exdate ))          $output['EXDATE']           = count( $this->exdate );
+        if( !empty( $this->exrule ))          $output['EXRULE']           = count( $this->exrule );
+        if( !empty( $this->action ))          $output['ACTION']           = 1;
+        if( !empty( $this->attach ))          $output['ATTACH']           = count( $this->attach );
+        if( !empty( $this->attendee ))        $output['ATTENDEE']         = count( $this->attendee );
+        if( !empty( $this->categories ))      $output['CATEGORIES']       = count( $this->categories );
+        if( !empty( $this->class ))           $output['CLASS']            = 1;
+        if( !empty( $this->comment ))         $output['COMMENT']          = count( $this->comment );
+        if( !empty( $this->completed ))       $output['COMPLETED']        = 1;
+        if( !empty( $this->contact ))         $output['CONTACT']          = count( $this->contact );
+        if( !empty( $this->created ))         $output['CREATED']          = 1;
+        if( !empty( $this->freebusy ))        $output['FREEBUSY']         = count( $this->freebusy );
+        if( !empty( $this->geo ))             $output['GEO']              = 1;
+        if( !empty( $this->lastmodified ))    $output['LAST-MODIFIED']    = 1;
+        if( !empty( $this->location ))        $output['LOCATION']         = 1;
+        if( !empty( $this->organizer ))       $output['ORGANIZER']        = 1;
+        if( !empty( $this->percentcomplete )) $output['PERCENT-COMPLETE'] = 1;
+        if( !empty( $this->priority ))        $output['PRIORITY']         = 1;
+        if( !empty( $this->recurrenceid ))    $output['RECURRENCE-ID']    = 1;
+        if( !empty( $this->relatedto ))       $output['RELATED-TO']       = count( $this->relatedto );
+        if( !empty( $this->repeat ))          $output['REPEAT']           = 1;
+        if( !empty( $this->requeststatus ))   $output['REQUEST-STATUS']   = count( $this->requeststatus );
+        if( !empty( $this->resources ))       $output['RESOURCES']        = count( $this->resources );
+        if( !empty( $this->sequence ))        $output['SEQUENCE']         = 1;
+        if( !empty( $this->sequence ))        $output['SEQUENCE']         = 1;
+        if( !empty( $this->status ))          $output['STATUS']           = 1;
+        if( !empty( $this->transp ))          $output['TRANSP']           = 1;
+        if( !empty( $this->trigger ))         $output['TRIGGER']          = 1;
+        if( !empty( $this->tzid ))            $output['TZID']             = 1;
+        if( !empty( $this->tzname ))          $output['TZNAME']           = count( $this->tzname );
+        if( !empty( $this->tzoffsetfrom ))    $output['TZOFFSETFROM']     = 1;
+        if( !empty( $this->tzoffsetto ))      $output['TZOFFSETTO']       = 1;
+        if( !empty( $this->tzurl ))           $output['TZURL']            = 1;
+        if( !empty( $this->url ))             $output['URL']              = 1;
+        if( !empty( $this->xprop ))           $output['X-PROP']           = count( $this->xprop );
+        return $output;
+        break;
+      case 'UNIQUE_ID':
+        if( empty( $this->unique_id ))
+          $this->unique_id  = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
+        return $this->unique_id;
+        break;
+    }
+  }
+/**
+ * general component config setting
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-12-12
+ * @param mixed  $config
+ * @param string $value
+ * @param bool   $softUpdate
+ * @return void
+ */
+  function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) {
+    if( is_array( $config )) {
+      foreach( $config as $cKey => $cValue ) {
+        if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate ))
+          return FALSE;
+      }
+      return TRUE;
+    }
+    $res = FALSE;
+    switch( strtoupper( $config )) {
+      case 'ALLOWEMPTY':
+        $this->allowEmpty = $value;
+        $subcfg = array( 'ALLOWEMPTY' => $value );
+        $res    = TRUE;
+        break;
+      case 'FORMAT':
+        $value  = trim( strtolower( $value ));
+        $this->format = $value;
+        $this->_createFormat();
+        $subcfg = array( 'FORMAT' => $value );
+        $res    = TRUE;
+        break;
+      case 'LANGUAGE':
+         // set language for calendar component as defined in [RFC 1766]
+        $value  = trim( $value );
+        if( empty( $this->language ) || !$softUpdate )
+          $this->language = $value;
+        $subcfg = array( 'LANGUAGE' => $value );
+        $res    = TRUE;
+        break;
+      case 'NL':
+      case 'NEWLINECHAR':
+        $this->nl = $value;
+        $subcfg = array( 'NL' => $value );
+        $res    = TRUE;
+        break;
+      case 'UNIQUE_ID':
+        $value  = trim( $value );
+        $this->unique_id = $value;
+        $subcfg = array( 'UNIQUE_ID' => $value );
+        $res    = TRUE;
+        break;
+      default:  // any unvalid config key.. .
+        return TRUE;
+    }
+    if( !$res ) return FALSE;
+    if( isset( $subcfg ) && !empty( $this->components )) {
+      foreach( $subcfg as $cfgkey => $cfgvalue ) {
+        foreach( $this->components as $cix => $component ) {
+          $res = $component->setConfig( $cfgkey, $cfgvalue, $softUpdate );
+          if( !$res )
+            break 2;
+          $this->components[$cix] = $component->copy(); // PHP4 compliant
+        }
+      }
+    }
+    return $res;
+  }
+/*********************************************************************************/
+/**
+ * delete component property value
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.4 - 2011-01-05
+ * @param mixed $propName, bool FALSE => X-property
+ * @param int   $propix, optional, if specific property is wanted in case of multiply occurences
+ * @return bool, if successfull delete TRUE
+ */
+  function deleteProperty( $propName=FALSE, $propix=FALSE ) {
+    if( $this->_notExistProp( $propName )) return FALSE;
+    $propName = strtoupper( $propName );
+    if( in_array( $propName, array( 'ATTACH',   'ATTENDEE', 'CATEGORIES', 'COMMENT',   'CONTACT', 'DESCRIPTION',    'EXDATE', 'EXRULE',
+                                    'FREEBUSY', 'RDATE',    'RELATED-TO', 'RESOURCES', 'RRULE',   'REQUEST-STATUS', 'TZNAME', 'X-PROP'  ))) {
+      if( !$propix )
+        $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
+      $this->propdelix[$propName] = --$propix;
+    }
+    $return = FALSE;
+    switch( $propName ) {
+      case 'ACTION':
+        if( !empty( $this->action )) {
+          $this->action = '';
+          $return = TRUE;
+        }
+        break;
+      case 'ATTACH':
+        return $this->deletePropertyM( $this->attach, $propix );
+        break;
+      case 'ATTENDEE':
+        return $this->deletePropertyM( $this->attendee, $propix );
+        break;
+      case 'CATEGORIES':
+        return $this->deletePropertyM( $this->categories, $propix );
+        break;
+      case 'CLASS':
+        if( !empty( $this->class )) {
+          $this->class = '';
+          $return = TRUE;
+        }
+        break;
+      case 'COMMENT':
+        return $this->deletePropertyM( $this->comment, $propix );
+        break;
+      case 'COMPLETED':
+        if( !empty( $this->completed )) {
+          $this->completed = '';
+          $return = TRUE;
+        }
+        break;
+      case 'CONTACT':
+        return $this->deletePropertyM( $this->contact, $propix );
+        break;
+      case 'CREATED':
+        if( !empty( $this->created )) {
+          $this->created = '';
+          $return = TRUE;
+        }
+        break;
+      case 'DESCRIPTION':
+        return $this->deletePropertyM( $this->description, $propix );
+        break;
+      case 'DTEND':
+        if( !empty( $this->dtend )) {
+          $this->dtend = '';
+          $return = TRUE;
+        }
+        break;
+      case 'DTSTAMP':
+        if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
+          return FALSE;
+        if( !empty( $this->dtstamp )) {
+          $this->dtstamp = '';
+          $return = TRUE;
+        }
+        break;
+      case 'DTSTART':
+        if( !empty( $this->dtstart )) {
+          $this->dtstart = '';
+          $return = TRUE;
+        }
+        break;
+      case 'DUE':
+        if( !empty( $this->due )) {
+          $this->due = '';
+          $return = TRUE;
+        }
+        break;
+      case 'DURATION':
+        if( !empty( $this->duration )) {
+          $this->duration = '';
+          $return = TRUE;
+        }
+        break;
+      case 'EXDATE':
+        return $this->deletePropertyM( $this->exdate, $propix );
+        break;
+      case 'EXRULE':
+        return $this->deletePropertyM( $this->exrule, $propix );
+        break;
+      case 'FREEBUSY':
+        return $this->deletePropertyM( $this->freebusy, $propix );
+        break;
+      case 'GEO':
+        if( !empty( $this->geo )) {
+          $this->geo = '';
+          $return = TRUE;
+        }
+        break;
+      case 'LAST-MODIFIED':
+        if( !empty( $this->lastmodified )) {
+          $this->lastmodified = '';
+          $return = TRUE;
+        }
+        break;
+      case 'LOCATION':
+        if( !empty( $this->location )) {
+          $this->location = '';
+          $return = TRUE;
+        }
+        break;
+      case 'ORGANIZER':
+        if( !empty( $this->organizer )) {
+          $this->organizer = '';
+          $return = TRUE;
+        }
+        break;
+      case 'PERCENT-COMPLETE':
+        if( !empty( $this->percentcomplete )) {
+          $this->percentcomplete = '';
+          $return = TRUE;
+        }
+        break;
+      case 'PRIORITY':
+        if( !empty( $this->priority )) {
+          $this->priority = '';
+          $return = TRUE;
+        }
+        break;
+      case 'RDATE':
+        return $this->deletePropertyM( $this->rdate, $propix );
+        break;
+      case 'RECURRENCE-ID':
+        if( !empty( $this->recurrenceid )) {
+          $this->recurrenceid = '';
+          $return = TRUE;
+        }
+        break;
+      case 'RELATED-TO':
+        return $this->deletePropertyM( $this->relatedto, $propix );
+        break;
+      case 'REPEAT':
+        if( !empty( $this->repeat )) {
+          $this->repeat = '';
+          $return = TRUE;
+        }
+        break;
+      case 'REQUEST-STATUS':
+        return $this->deletePropertyM( $this->requeststatus, $propix );
+        break;
+      case 'RESOURCES':
+        return $this->deletePropertyM( $this->resources, $propix );
+        break;
+      case 'RRULE':
+        return $this->deletePropertyM( $this->rrule, $propix );
+        break;
+      case 'SEQUENCE':
+        if( !empty( $this->sequence )) {
+          $this->sequence = '';
+          $return = TRUE;
+        }
+        break;
+      case 'STATUS':
+        if( !empty( $this->status )) {
+          $this->status = '';
+          $return = TRUE;
+        }
+        break;
+      case 'SUMMARY':
+        if( !empty( $this->summary )) {
+          $this->summary = '';
+          $return = TRUE;
+        }
+        break;
+      case 'TRANSP':
+        if( !empty( $this->transp )) {
+          $this->transp = '';
+          $return = TRUE;
+        }
+        break;
+      case 'TRIGGER':
+        if( !empty( $this->trigger )) {
+          $this->trigger = '';
+          $return = TRUE;
+        }
+        break;
+      case 'TZID':
+        if( !empty( $this->tzid )) {
+          $this->tzid = '';
+          $return = TRUE;
+        }
+        break;
+      case 'TZNAME':
+        return $this->deletePropertyM( $this->tzname, $propix );
+        break;
+      case 'TZOFFSETFROM':
+        if( !empty( $this->tzoffsetfrom )) {
+          $this->tzoffsetfrom = '';
+          $return = TRUE;
+        }
+        break;
+      case 'TZOFFSETTO':
+        if( !empty( $this->tzoffsetto )) {
+          $this->tzoffsetto = '';
+          $return = TRUE;
+        }
+        break;
+      case 'TZURL':
+        if( !empty( $this->tzurl )) {
+          $this->tzurl = '';
+          $return = TRUE;
+        }
+        break;
+      case 'UID':
+        if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
+          return FALSE;
+        if( !empty( $this->uid )) {
+          $this->uid = '';
+          $return = TRUE;
+        }
+        break;
+      case 'URL':
+        if( !empty( $this->url )) {
+          $this->url = '';
+          $return = TRUE;
+        }
+        break;
+      default:
+        $reduced = '';
+        if( $propName != 'X-PROP' ) {
+          if( !isset( $this->xprop[$propName] )) return FALSE;
+          foreach( $this->xprop as $k => $a ) {
+            if(( $k != $propName ) && !empty( $a ))
+              $reduced[$k] = $a;
+          }
+        }
+        else {
+          if( count( $this->xprop ) <= $propix ) return FALSE;
+          $xpropno = 0;
+          foreach( $this->xprop as $xpropkey => $xpropvalue ) {
+            if( $propix != $xpropno )
+              $reduced[$xpropkey] = $xpropvalue;
+            $xpropno++;
+          }
+        }
+        $this->xprop = $reduced;
+        if( empty( $this->xprop ))
+          return FALSE;
+        return TRUE;
+    }
+    return $return;
+  }
+/*********************************************************************************/
+/**
+ * delete component property value, fixing components with multiple occurencies
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.19 - 2009-12-30
+ * @param array $multiprop, reference to a component property
+ * @param int   $propix, default 0
+ * @return bool TRUE
+ */
+  function deletePropertyM( & $multiprop, $propix=0 ) {
+    if( !isset( $multiprop[$propix])) return FALSE;
+    unset( $multiprop[$propix] );
+    if( empty( $multiprop )) $multiprop = '';
+    return ( isset( $multiprop[$propix] )) ? FALSE : TRUE;
+  }
+/**
+ * get component property value/params
+ *
+ * if property has multiply values, consequtive function calls are needed
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.22 - 2010-10-21
+ * @param string $propName, optional
+ * @param int @propix, optional, if specific property is wanted in case of multiply occurences
+ * @param bool $inclParam=FALSE
+ * @param bool $specform=FALSE
+ * @return mixed
+ */
+  function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) {
+    if( $this->_notExistProp( $propName )) return FALSE;
+    $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
+    if( in_array( $propName, array( 'ATTACH',   'ATTENDEE', 'CATEGORIES', 'COMMENT',   'CONTACT', 'DESCRIPTION',    'EXDATE', 'EXRULE',
+                                    'FREEBUSY', 'RDATE',    'RELATED-TO', 'RESOURCES', 'RRULE',   'REQUEST-STATUS', 'TZNAME', 'X-PROP'  ))) {
+      if( !$propix )
+        $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
+      $this->propix[$propName] = --$propix;
+    }
+    switch( $propName ) {
+      case 'ACTION':
+        if( !empty( $this->action['value'] )) return ( $inclParam ) ? $this->action : $this->action['value'];
+        break;
+      case 'ATTACH':
+        if( !isset( $this->attach[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value'];
+        break;
+      case 'ATTENDEE':
+        if( !isset( $this->attendee[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value'];
+        break;
+      case 'CATEGORIES':
+        if( !isset( $this->categories[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value'];
+        break;
+      case 'CLASS':
+        if( !empty( $this->class['value'] )) return ( $inclParam ) ? $this->class : $this->class['value'];
+        break;
+      case 'COMMENT':
+        if( !isset( $this->comment[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value'];
+        break;
+      case 'COMPLETED':
+        if( !empty( $this->completed['value'] )) return ( $inclParam ) ? $this->completed : $this->completed['value'];
+        break;
+      case 'CONTACT':
+        if( !isset( $this->contact[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value'];
+        break;
+      case 'CREATED':
+        if( !empty( $this->created['value'] )) return ( $inclParam ) ? $this->created : $this->created['value'];
+        break;
+      case 'DESCRIPTION':
+        if( !isset( $this->description[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value'];
+        break;
+      case 'DTEND':
+        if( !empty( $this->dtend['value'] )) return ( $inclParam ) ? $this->dtend : $this->dtend['value'];
+        break;
+      case 'DTSTAMP':
+        if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
+          return;
+        if( !isset( $this->dtstamp['value'] ))
+          $this->_makeDtstamp();
+        return ( $inclParam ) ? $this->dtstamp : $this->dtstamp['value'];
+        break;
+      case 'DTSTART':
+        if( !empty( $this->dtstart['value'] )) return ( $inclParam ) ? $this->dtstart : $this->dtstart['value'];
+        break;
+      case 'DUE':
+        if( !empty( $this->due['value'] )) return ( $inclParam ) ? $this->due : $this->due['value'];
+        break;
+      case 'DURATION':
+        if( !isset( $this->duration['value'] )) return FALSE;
+        $value = ( $specform && isset( $this->dtstart['value'] ) && isset( $this->duration['value'] )) ? iCalUtilityFunctions::_duration2date( $this->dtstart['value'], $this->duration['value'] ) : $this->duration['value'];
+        return ( $inclParam ) ? array( 'value' => $value, 'params' =>  $this->duration['params'] ) : $value;
+        break;
+      case 'EXDATE':
+        if( !isset( $this->exdate[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value'];
+        break;
+      case 'EXRULE':
+        if( !isset( $this->exrule[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value'];
+        break;
+      case 'FREEBUSY':
+        if( !isset( $this->freebusy[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value'];
+        break;
+      case 'GEO':
+        if( !empty( $this->geo['value'] )) return ( $inclParam ) ? $this->geo : $this->geo['value'];
+        break;
+      case 'LAST-MODIFIED':
+        if( !empty( $this->lastmodified['value'] )) return ( $inclParam ) ? $this->lastmodified : $this->lastmodified['value'];
+        break;
+      case 'LOCATION':
+        if( !empty( $this->location['value'] )) return ( $inclParam ) ? $this->location : $this->location['value'];
+        break;
+      case 'ORGANIZER':
+        if( !empty( $this->organizer['value'] )) return ( $inclParam ) ? $this->organizer : $this->organizer['value'];
+        break;
+      case 'PERCENT-COMPLETE':
+        if( !empty( $this->percentcomplete['value'] )) return ( $inclParam ) ? $this->percentcomplete : $this->percentcomplete['value'];
+        break;
+      case 'PRIORITY':
+        if( !empty( $this->priority['value'] )) return ( $inclParam ) ? $this->priority : $this->priority['value'];
+        break;
+      case 'RDATE':
+        if( !isset( $this->rdate[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value'];
+        break;
+      case 'RECURRENCE-ID':
+        if( !empty( $this->recurrenceid['value'] )) return ( $inclParam ) ? $this->recurrenceid : $this->recurrenceid['value'];
+        break;
+      case 'RELATED-TO':
+        if( !isset( $this->relatedto[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value'];
+        break;
+      case 'REPEAT':
+        if( !empty( $this->repeat['value'] )) return ( $inclParam ) ? $this->repeat : $this->repeat['value'];
+        break;
+      case 'REQUEST-STATUS':
+        if( !isset( $this->requeststatus[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value'];
+        break;
+      case 'RESOURCES':
+        if( !isset( $this->resources[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value'];
+        break;
+      case 'RRULE':
+        if( !isset( $this->rrule[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value'];
+        break;
+      case 'SEQUENCE':
+        if( isset( $this->sequence['value'] ) && ( '0' <= $this->sequence['value'] )) return ( $inclParam ) ? $this->sequence : $this->sequence['value'];
+        break;
+      case 'STATUS':
+        if( !empty( $this->status['value'] )) return ( $inclParam ) ? $this->status : $this->status['value'];
+        break;
+      case 'SUMMARY':
+        if( !empty( $this->summary['value'] )) return ( $inclParam ) ? $this->summary : $this->summary['value'];
+        break;
+      case 'TRANSP':
+        if( !empty( $this->transp['value'] )) return ( $inclParam ) ? $this->transp : $this->transp['value'];
+        break;
+      case 'TRIGGER':
+        if( !empty( $this->trigger['value'] )) return ( $inclParam ) ? $this->trigger : $this->trigger['value'];
+        break;
+      case 'TZID':
+        if( !empty( $this->tzid['value'] )) return ( $inclParam ) ? $this->tzid : $this->tzid['value'];
+        break;
+      case 'TZNAME':
+        if( !isset( $this->tzname[$propix] )) return FALSE;
+        return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value'];
+        break;
+      case 'TZOFFSETFROM':
+        if( !empty( $this->tzoffsetfrom['value'] )) return ( $inclParam ) ? $this->tzoffsetfrom : $this->tzoffsetfrom['value'];
+        break;
+      case 'TZOFFSETTO':
+        if( !empty( $this->tzoffsetto['value'] )) return ( $inclParam ) ? $this->tzoffsetto : $this->tzoffsetto['value'];
+        break;
+      case 'TZURL':
+        if( !empty( $this->tzurl['value'] )) return ( $inclParam ) ? $this->tzurl : $this->tzurl['value'];
+        break;
+      case 'UID':
+        if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
+          return FALSE;
+        if( empty( $this->uid['value'] ))
+          $this->_makeuid();
+        return ( $inclParam ) ? $this->uid : $this->uid['value'];
+        break;
+      case 'URL':
+        if( !empty( $this->url['value'] )) return ( $inclParam ) ? $this->url : $this->url['value'];
+        break;
+      default:
+        if( $propName != 'X-PROP' ) {
+          if( !isset( $this->xprop[$propName] )) return FALSE;
+          return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
+                                : array( $propName, $this->xprop[$propName]['value'] );
+        }
+        else {
+          if( empty( $this->xprop )) return FALSE;
+          $xpropno = 0;
+          foreach( $this->xprop as $xpropkey => $xpropvalue ) {
+            if( $propix == $xpropno )
+              return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
+                                    : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
+            else
+              $xpropno++;
+          }
+          return FALSE; // not found ??
+        }
+    }
+    return FALSE;
+  }
+/**
+ * general component property setting
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-05
+ * @param mixed $args variable number of function arguments,
+ *                    first argument is ALWAYS component name,
+ *                    second ALWAYS component value!
+ * @return void
+ */
+  function setProperty() {
+    $numargs    = func_num_args();
+    if( 1 > $numargs ) return FALSE;
+    $arglist    = func_get_args();
+    if( $this->_notExistProp( $arglist[0] )) return FALSE;
+    if( !$this->getConfig( 'allowEmpty' ) && ( !isset( $arglist[1] ) || empty( $arglist[1] )))
+      return FALSE;
+    $arglist[0] = strtoupper( $arglist[0] );
+    for( $argix=$numargs; $argix < 12; $argix++ ) {
+      if( !isset( $arglist[$argix] ))
+        $arglist[$argix] = null;
+    }
+    switch( $arglist[0] ) {
+      case 'ACTION':
+        return $this->setAction( $arglist[1], $arglist[2] );
+      case 'ATTACH':
+        return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] );
+      case 'ATTENDEE':
+        return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] );
+      case 'CATEGORIES':
+        return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] );
+      case 'CLASS':
+        return $this->setClass( $arglist[1], $arglist[2] );
+      case 'COMMENT':
+        return $this->setComment( $arglist[1], $arglist[2], $arglist[3] );
+      case 'COMPLETED':
+        return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
+      case 'CONTACT':
+        return $this->setContact( $arglist[1], $arglist[2], $arglist[3] );
+      case 'CREATED':
+        return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
+      case 'DESCRIPTION':
+        return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] );
+      case 'DTEND':
+        return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
+      case 'DTSTAMP':
+        return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
+      case 'DTSTART':
+        return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
+      case 'DUE':
+        return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
+      case 'DURATION':
+        return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] );
+      case 'EXDATE':
+        return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] );
+      case 'EXRULE':
+        return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] );
+      case 'FREEBUSY':
+        return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] );
+      case 'GEO':
+        return $this->setGeo( $arglist[1], $arglist[2], $arglist[3] );
+      case 'LAST-MODIFIED':
+        return $this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
+      case 'LOCATION':
+        return $this->setLocation( $arglist[1], $arglist[2] );
+      case 'ORGANIZER':
+        return $this->setOrganizer( $arglist[1], $arglist[2] );
+      case 'PERCENT-COMPLETE':
+        return $this->setPercentComplete( $arglist[1], $arglist[2] );
+      case 'PRIORITY':
+        return $this->setPriority( $arglist[1], $arglist[2] );
+      case 'RDATE':
+        return $this->setRdate( $arglist[1], $arglist[2], $arglist[3] );
+      case 'RECURRENCE-ID':
+       return $this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
+      case 'RELATED-TO':
+        return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] );
+      case 'REPEAT':
+        return $this->setRepeat( $arglist[1], $arglist[2] );
+      case 'REQUEST-STATUS':
+        return $this->setRequestStatus( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5] );
+      case 'RESOURCES':
+        return $this->setResources( $arglist[1], $arglist[2], $arglist[3] );
+      case 'RRULE':
+        return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] );
+      case 'SEQUENCE':
+        return $this->setSequence( $arglist[1], $arglist[2] );
+      case 'STATUS':
+        return $this->setStatus( $arglist[1], $arglist[2] );
+      case 'SUMMARY':
+        return $this->setSummary( $arglist[1], $arglist[2] );
+      case 'TRANSP':
+        return $this->setTransp( $arglist[1], $arglist[2] );
+      case 'TRIGGER':
+        return $this->setTrigger( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8], $arglist[9], $arglist[10], $arglist[11] );
+      case 'TZID':
+        return $this->setTzid( $arglist[1], $arglist[2] );
+      case 'TZNAME':
+        return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] );
+      case 'TZOFFSETFROM':
+        return $this->setTzoffsetfrom( $arglist[1], $arglist[2] );
+      case 'TZOFFSETTO':
+        return $this->setTzoffsetto( $arglist[1], $arglist[2] );
+      case 'TZURL':
+        return $this->setTzurl( $arglist[1], $arglist[2] );
+      case 'UID':
+        return $this->setUid( $arglist[1], $arglist[2] );
+      case 'URL':
+        return $this->setUrl( $arglist[1], $arglist[2] );
+      default:
+        return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
+    }
+    return FALSE;
+  }
+/*********************************************************************************/
+/**
+ * parse component unparsed data into properties
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.33 - 2010-12-14
+ * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings
+ * @return bool FALSE if error occurs during parsing
+ *
+ */
+  function parse( $unparsedtext=null ) {
+    if( $unparsedtext ) {
+      if( !is_array( $unparsedtext ))
+        $unparsedtext = array( $unparsedtext );
+    }
+    elseif( !isset( $this->unparsed ))
+      $unparsedtext = array();
+    else
+      $unparsedtext = $this->unparsed;
+    $this->unparsed = array();
+    $comp = & $this;
+    foreach ( $unparsedtext as $line ) {
+// echo $comp->objName.": $line<br />"; // test ###
+      if( in_array( strtoupper( substr( $line, 0, 6 )), array( 'END:VA', 'END:ST', 'END:DA' )))
+      $this->components[] = $comp->copy();
+      elseif( 'END:' == strtoupper( substr( $line, 0, 4 )))
+        break;
+      elseif( 'BEGIN:VALARM'   == strtoupper( substr( $line, 0, 12 )))
+        $comp = new valarm();
+      elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 )))
+        $comp = new vtimezone( 'standard' );
+      elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 )))
+        $comp = new vtimezone( 'daylight' );
+      elseif( 'BEGIN:'         == strtoupper( substr( $line, 0, 6 )))
+        continue;
+      else {
+        $comp->unparsed[] = $line;
+// echo $comp->objName.": $line<br />\n"; // test ###
+      }
+    }
+// echo $this->objName.'<br />'.var_export( $this->unparsed, TRUE )."<br />\n"; // test ###
+            /* concatenate property values spread over several lines */
+    $lastix    = -1;
+    $propnames = array( 'action', 'attach', 'attendee', 'categories', 'comment', 'completed'
+                      , 'contact', 'class', 'created', 'description', 'dtend', 'dtstart'
+                      , 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo'
+                      , 'last-modified', 'location', 'organizer', 'percent-complete'
+                      , 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat'
+                      , 'request-status', 'resources', 'rrule', 'sequence', 'status'
+                      , 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom'
+                      , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' );
+    $proprows  = array();
+    foreach( $this->unparsed as $line ) {
+      $newProp = FALSE;
+      foreach ( $propnames as $propname ) {
+        if( $propname == strtolower( substr( $line, 0, strlen( $propname )))) {
+          $newProp = TRUE;
+          break;
+        }
+      }
+      if( $newProp ) {
+        $newProp = FALSE;
+        $lastix++;
+        $proprows[$lastix]  = $line;
+      }
+      else {
+
+            /* remove line breaks */
+        if(( 1 < strlen( $proprows[$lastix] ))         &&
+           ( '\n' == substr( $proprows[$lastix], -2 )) &&
+           (  ' ' == substr( $line, 0, 1 ))) {
+          $proprows[$lastix] = substr( $proprows[$lastix], 0, strlen( $proprows[$lastix] ) - 2 );
+          $line = substr( $line, 1 );
+        }
+
+        if(( 1 < strlen( $proprows[$lastix] ))         &&
+           (  ' ' == substr( $line, 0, 1 ))) {
+          $proprows[$lastix] = substr( $proprows[$lastix], 0, strlen( $proprows[$lastix] ) - 1 );
+          $line = substr( $line, 1 );
+        }
+        $proprows[$lastix] .= $line;
+      }
+    }
+            /* parse each property 'line' */
+    foreach( $proprows as $line ) {
+      $line = str_replace( "\n ", '', $line );
+      if( '\n' == substr( $line, -2 ))
+        $line = substr( $line, 0, strlen( $line ) - 2 );
+            /* get propname, (problem with x-properties, otherwise in previous loop) */
+      $cix = $propname = null;
+      for( $cix=0, $clen = strlen( $line ); $cix < $clen; $cix++ ) {
+        if( in_array( $line[$cix], array( ':', ';' )))
+          break;
+        else {
+          $propname .= $line[$cix];
+        }
+      }
+      if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) {
+        $propname2 = $propname;
+        $propname  = 'X-';
+      }
+            /* rest of the line is opt.params and value */
+      $line = substr( $line, $cix );
+            /* separate attributes from value */
+      $attr   = array();
+      $attrix = -1;
+      $clen = strlen( $line );
+      $controlCN = false;
+
+      for( $cix=0; $cix < $clen; $cix++ ) {
+
+        if('"' == $line[$cix] && $controlCN)  $controlCN = false;
+        if('"'   == $line[$cix] && strtoupper( substr( $line, $cix - 3, 3 )) == 'CN=' )  $controlCN = true;
+
+        if((  ':'   == $line[$cix] )             &&
+             ( $controlCN === false)                        &&
+             ( '://' != substr( $line, $cix, 3 )) &&
+             ( !in_array( strtolower( substr( $line, $cix - 3, 4 )), array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ))) &&
+             ( !in_array( strtolower( substr( $line, $cix - 4, 5 )), array( 'crid:', 'news:', 'pres:' ))) &&
+             ( 'mailto:'   != strtolower( substr( $line, $cix - 6, 7 )))) {
+          $attrEnd = TRUE;
+          if(( $cix < ( $clen - 4 )) &&
+               ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
+            for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
+              if( '://' == substr( $line, $c2ix - 2, 3 )) {
+                $attrEnd = FALSE;
+                break; // an URI with a portnr!!
+              }
+            }
+          }
+          if( $attrEnd) {
+            $line = substr( $line, $cix + 1 );
+            break;
+          }
+        }
+        if( ';' == $line[$cix])
+          $attr[++$attrix] = null;
+        else
+          $attr[$attrix] .= $line[$cix];
+      }      
+            /* make attributes in array format */
+      $propattr = array();
+      foreach( $attr as $attribute ) {
+        $attrsplit = explode( '=', $attribute, 2 );
+        if( 1 < count( $attrsplit ))
+          $propattr[$attrsplit[0]] = $attrsplit[1];
+        else
+          $propattr[] = $attribute;
+      }
+            /* call setProperty( $propname.. . */
+      switch( strtoupper( $propname )) {
+        case 'ATTENDEE':
+          foreach( $propattr as $pix => $attr ) {
+            if(strtoupper($pix) == 'CN') continue;          
+            $attr2 = explode( ',', $attr );
+              if( 1 < count( $attr2 ))
+                $propattr[$pix] = $attr2;
+          }
+          $this->setProperty( $propname, $line, $propattr );
+          break;
+        case 'CATEGORIES':
+        case 'RESOURCES':
+          if( FALSE !== strpos( $line, ',' )) {
+            $content  = explode( ',', $line );
+            $clen     = count( $content );
+            for( $cix = 0; $cix < $clen; $cix++ ) {
+              if( "\\" == substr($content[$cix], -1)) {
+                $content[$cix] .= ','.$content[$cix + 1];
+                unset($content[$cix + 1]);
+                $cix++;
+              }
+            }
+            if( 1 < count( $content )) {
+              $content = array_values( $content );
+              foreach( $content as $cix => $contentPart )
+                $content[$cix] = calendarComponent::_strunrep( $contentPart );
+              $this->setProperty( $propname, $content, $propattr );
+              break;
+            }
+            else
+              $line = reset( $content );
+          }
+        case 'X-':
+          $propname = ( isset( $propname2 )) ? $propname2 : $propname;
+        case 'COMMENT':
+        case 'CONTACT':
+        case 'DESCRIPTION':
+        case 'LOCATION':
+        case 'SUMMARY':
+          if( empty( $line ))
+            $propattr = null;
+          $this->setProperty( $propname, calendarComponent::_strunrep( $line ), $propattr );
+          unset( $propname2 );
+          break;
+        case 'REQUEST-STATUS':
+          $values    = explode( ';', $line, 3 );
+          $values[1] = ( !isset( $values[1] )) ? null : calendarComponent::_strunrep( $values[1] );
+          $values[2] = ( !isset( $values[2] )) ? null : calendarComponent::_strunrep( $values[2] );
+          $this->setProperty( $propname
+                            , $values[0]  // statcode
+                            , $values[1]  // statdesc
+                            , $values[2]  // extdata
+                            , $propattr );
+          break;
+        case 'FREEBUSY':
+          $fbtype = ( isset( $propattr['FBTYPE'] )) ? $propattr['FBTYPE'] : ''; // force setting default, if missing
+          unset( $propattr['FBTYPE'] );
+          $values = explode( ',', $line );
+          foreach( $values as $vix => $value ) {
+            $value2 = explode( '/', $value );
+            if( 1 < count( $value2 ))
+              $values[$vix] = $value2;
+          }
+          $this->setProperty( $propname, $fbtype, $values, $propattr );
+          break;
+        case 'GEO':
+          $value = explode( ';', $line, 2 );
+          if( 2 > count( $value ))
+            $value[1] = null;
+          $this->setProperty( $propname, $value[0], $value[1], $propattr );
+          break;
+        case 'EXDATE':
+          $values = ( !empty( $line )) ? explode( ',', $line ) : null;
+          $this->setProperty( $propname, $values, $propattr );
+          break;
+        case 'RDATE':
+          if( empty( $line )) {
+            $this->setProperty( $propname, $line, $propattr );
+            break;
+          }
+          $values = explode( ',', $line );
+          foreach( $values as $vix => $value ) {
+            $value2 = explode( '/', $value );
+            if( 1 < count( $value2 ))
+              $values[$vix] = $value2;
+          }
+          $this->setProperty( $propname, $values, $propattr );
+          break;
+        case 'EXRULE':
+        case 'RRULE':
+          $values = explode( ';', $line );
+          $recur = array();
+          foreach( $values as $value2 ) {
+            if( empty( $value2 ))
+              continue; // ;-char in ending position ???
+            $value3 = explode( '=', $value2, 2 );
+            $rulelabel = strtoupper( $value3[0] );
+            switch( $rulelabel ) {
+              case 'BYDAY': {
+                $value4 = explode( ',', $value3[1] );
+                if( 1 < count( $value4 )) {
+                  foreach( $value4 as $v5ix => $value5 ) {
+                    $value6 = array();
+                    $dayno = $dayname = null;
+                    $value5 = trim( (string) $value5 );
+                    if(( ctype_alpha( substr( $value5, -1 ))) &&
+                       ( ctype_alpha( substr( $value5, -2, 1 )))) {
+                      $dayname = substr( $value5, -2, 2 );
+                      if( 2 < strlen( $value5 ))
+                        $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
+                    }
+                    if( $dayno )
+                      $value6[] = $dayno;
+                    if( $dayname )
+                      $value6['DAY'] = $dayname;
+                    $value4[$v5ix] = $value6;
+                  }
+                }
+                else {
+                  $value4 = array();
+                  $dayno  = $dayname = null;
+                  $value5 = trim( (string) $value3[1] );
+                  if(( ctype_alpha( substr( $value5, -1 ))) &&
+                     ( ctype_alpha( substr( $value5, -2, 1 )))) {
+                      $dayname = substr( $value5, -2, 2 );
+                    if( 2 < strlen( $value5 ))
+                      $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
+                  }
+                  if( $dayno )
+                    $value4[] = $dayno;
+                  if( $dayname )
+                    $value4['DAY'] = $dayname;
+                }
+                $recur[$rulelabel] = $value4;
+                break;
+              }
+              default: {
+                $value4 = explode( ',', $value3[1] );
+                if( 1 < count( $value4 ))
+                  $value3[1] = $value4;
+                $recur[$rulelabel] = $value3[1];
+                break;
+              }
+            } // end - switch $rulelabel
+          } // end - foreach( $values.. .
+          $this->setProperty( $propname, $recur, $propattr );
+          break;
+        case 'ACTION':
+        case 'CLASSIFICATION':
+        case 'STATUS':
+        case 'TRANSP':
+        case 'UID':
+        case 'TZID':
+        case 'RELATED-TO':
+        case 'TZNAME':
+          $line = calendarComponent::_strunrep( $line );
+        default:
+          $this->setProperty( $propname, $line, $propattr );
+          break;
+      } // end  switch( $propname.. .
+    } // end - foreach( $proprows.. .
+    unset( $unparsedtext, $this->unparsed, $proprows );
+    if( isset( $this->components ) && is_array( $this->components ) && ( 0 < count( $this->components ))) {
+      $ckeys = array_keys( $this->components );
+      foreach( $ckeys as $ckey ) {
+        if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
+          $this->components[$ckey]->parse();
+        }
+      }
+    }
+    return TRUE;
+  }
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ * return a copy of this component
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.2.16 - 2007-11-07
+ * @return object
+ */
+  function copy() {
+    $serialized_contents = serialize( $this );
+    $copy = unserialize( $serialized_contents );
+    unset( $copy->propix );
+    return $copy;
+ }
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ * delete calendar subcomponent from component container
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-15
+ * @param mixed $arg1 ordno / component type / component uid
+ * @param mixed $arg2 optional, ordno if arg1 = component type
+ * @return void
+ */
+  function deleteComponent( $arg1, $arg2=FALSE  ) {
+    if( !isset( $this->components )) return FALSE;
+    $argType = $index = null;
+    if ( ctype_digit( (string) $arg1 )) {
+      $argType = 'INDEX';
+      $index   = (int) $arg1 - 1;
+    }
+    elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
+      $argType = strtolower( $arg1 );
+      $index   = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
+    }
+    $cix2dC = 0;
+    foreach ( $this->components as $cix => $component) {
+      if( empty( $component )) continue;
+      unset( $component->propix );
+      if(( 'INDEX' == $argType ) && ( $index == $cix )) {
+        unset( $this->components[$cix] );
+        return TRUE;
+      }
+      elseif( $argType == $component->objName ) {
+        if( $index == $cix2dC ) {
+          unset( $this->components[$cix] );
+          return TRUE;
+        }
+        $cix2dC++;
+      }
+      elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
+        unset( $this->components[$cix] );
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+/**
+ * get calendar component subcomponent from component container
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-15
+ * @param mixed $arg1 optional, ordno/component type/ component uid
+ * @param mixed $arg2 optional, ordno if arg1 = component type
+ * @return object
+ */
+  function getComponent ( $arg1=FALSE, $arg2=FALSE ) {
+    if( !isset( $this->components )) return FALSE;
+    $index = $argType = null;
+    if ( !$arg1 ) {
+      $argType = 'INDEX';
+      $index   = $this->compix['INDEX'] =
+        ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
+    }
+    elseif ( ctype_digit( (string) $arg1 )) {
+      $argType = 'INDEX';
+      $index   = (int) $arg1;
+      unset( $this->compix );
+    }
+    elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
+      unset( $this->compix['INDEX'] );
+      $argType = strtolower( $arg1 );
+      if( !$arg2 )
+        $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
+      else
+        $index = (int) $arg2;
+    }
+    $index  -= 1;
+    $ckeys = array_keys( $this->components );
+    if( !empty( $index) && ( $index > end( $ckeys )))
+      return FALSE;
+    $cix2gC = 0;
+    foreach( $this->components as $cix => $component ) {
+      if( empty( $component )) continue;
+      unset( $component->propix );
+      if(( 'INDEX' == $argType ) && ( $index == $cix ))
+        return $component->copy();
+      elseif( $argType == $component->objName ) {
+         if( $index == $cix2gC )
+           return $component->copy();
+         $cix2gC++;
+      }
+      elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' ))) {
+        unset( $component->propix );
+        return $component->copy();
+      }
+    }
+            /* not found.. . */
+    unset( $this->compix );
+    return false;
+  }
+/**
+ * add calendar component as subcomponent to container for subcomponents
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 1.x.x - 2007-04-24
+ * @param object $component calendar component
+ * @return void
+ */
+  function addSubComponent ( $component ) {
+    $this->setComponent( $component );
+  }
+/**
+ * create new calendar component subcomponent, already included within component
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.33 - 2011-01-03
+ * @param string $compType subcomponent type
+ * @return object (reference)
+ */
+  function & newComponent( $compType ) {
+    $config = $this->getConfig();
+    $keys   = array_keys( $this->components );
+    $ix     = end( $keys) + 1;
+    switch( strtoupper( $compType )) {
+      case 'ALARM':
+      case 'VALARM':
+        $this->components[$ix] = new valarm( $config );
+        break;
+      case 'STANDARD':
+        array_unshift( $this->components, new vtimezone( 'STANDARD', $config ));
+        $ix = 0;
+        break;
+      case 'DAYLIGHT':
+        $this->components[$ix] = new vtimezone( 'DAYLIGHT', $config );
+        break;
+      default:
+        return FALSE;
+    }
+    return $this->components[$ix];
+  }
+/**
+ * add calendar component as subcomponent to container for subcomponents
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.28 - 2011-01-01
+ * @param object $component calendar component
+ * @param mixed $arg1 optional, ordno/component type/ component uid
+ * @param mixed $arg2 optional, ordno if arg1 = component type
+ * @return bool
+ */
+  function setComponent( $component, $arg1=FALSE, $arg2=FALSE  ) {
+    if( !isset( $this->components )) return FALSE;
+    $component->setConfig( $this->getConfig(), FALSE, TRUE );
+    if( !in_array( $component->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
+      unset( $component->propix );
+            /* make sure dtstamp and uid is set */
+      $dummy = $component->getProperty( 'dtstamp' );
+      $dummy = $component->getProperty( 'uid' );
+    }
+    if( !$arg1 ) { // plain insert, last in chain
+      $this->components[] = $component->copy();
+      return TRUE;
+    }
+    $argType = $index = null;
+    if ( ctype_digit( (string) $arg1 )) { // index insert/replace
+      $argType = 'INDEX';
+      $index   = (int) $arg1 - 1;
+    }
+    elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) {
+      $argType = strtolower( $arg1 );
+      $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
+    }
+    // else if arg1 is set, arg1 must be an UID
+    $cix2sC = 0;
+    foreach ( $this->components as $cix => $component2 ) {
+      if( empty( $component2 )) continue;
+      unset( $component2->propix );
+      if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
+        $this->components[$cix] = $component->copy();
+        return TRUE;
+      }
+      elseif( $argType == $component2->objName ) { // component Type index insert/replace
+        if( $index == $cix2sC ) {
+          $this->components[$cix] = $component->copy();
+          return TRUE;
+        }
+        $cix2sC++;
+      }
+      elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
+        $this->components[$cix] = $component->copy();
+        return TRUE;
+      }
+    }
+            /* arg1=index and not found.. . insert at index .. .*/
+    if( 'INDEX' == $argType ) {
+      $this->components[$index] = $component->copy();
+      ksort( $this->components, SORT_NUMERIC );
+    }
+    else    /* not found.. . insert last in chain anyway .. .*/
+    $this->components[] = $component->copy();
+    return TRUE;
+  }
+/**
+ * creates formatted output for subcomponents
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-12-12
+ * @return string
+ */
+  function createSubComponent() {
+    $output = null;
+    foreach( $this->components as $component ) {
+      if( empty( $component )) continue;
+      $component->setConfig( $this->getConfig(), FALSE, TRUE );
+      $output .= $component->createComponent( $this->xcaldecl );
+    }
+    return $output;
+  }
+/********************************************************************************/
+/**
+ * break lines at pos 75
+ *
+ * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
+ * break. Long content lines SHOULD be split into a multiple line
+ * representations using a line "folding" technique. That is, a long
+ * line can be split between any two characters by inserting a CRLF
+ * immediately followed by a single linear white space character (i.e.,
+ * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
+ * of CRLF followed immediately by a single linear white space character
+ * is ignored (i.e., removed) when processing the content type.
+ *
+ * Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where
+ * the reserved expression "\n" in the arg $string could be broken up by the
+ * folding of lines, causing ambiguity in the return string.
+ * Fix uses var $breakAtChar=75 and breaks the line at $breakAtChar-1 if need be.
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.13 - 2010-12-06
+ * @param string $value
+ * @return string
+ */
+  function _size75( $string ) {
+    $tmp    = $string;
+    $string = null;
+            /* if PHP is config with  mb_string.. . */
+    if( defined( MB_OVERLOAD_STRING )) {
+      $strlen  = mb_strlen( $tmp );
+      while( $strlen > 75 ) {
+         $breakAtChar = 75;
+         if( substr( $tmp, ( $breakAtChar - 1 ), strlen( '\n' )) == '\n' )
+           $breakAtChar = $breakAtChar - 1;
+         $string .= mb_substr( $tmp, 0, $breakAtChar ).$this->nl;
+         $tmp     = ' '.mb_substr( $tmp, $breakAtChar );
+         $strlen  = mb_strlen( $tmp );
+      } // end while
+      $string .= rtrim( $tmp ); // the rest
+      if( $this->nl != mb_substr( $string, ( 0 - strlen( $this->nl ))))
+        $string .= $this->nl;
+      return $string;
+    }
+            /* if PHP is not config with  mb_string.. . */
+    $eolcharlen = strlen( '\n' );
+    $eolchrxlen = strlen( "\n" );
+    while( TRUE ) {
+      $bytecnt = strlen( $tmp );
+      $charCnt = $ix = 0;
+      for( $ix = 0; $ix < $bytecnt; $ix++ ) {
+        if(( 73 < $charCnt ) && ( '\n' == substr( $tmp, $ix, $eolcharlen ))) {
+          $ix += $eolcharlen;
+          break;                                    // break when '\n' and eol
+        }
+        elseif( 74 < $charCnt )
+          break;                                    // always break for-loop here
+        else {
+          $byte = ord( $tmp[$ix] );
+          if ($byte <= 127) {                       // add a one byte character
+            $string .= substr( $tmp, $ix, 1 );
+            $charCnt += 1;
+          }
+          else if ($byte >= 194 && $byte <= 223) {  // start byte in two byte character
+            $string .= substr( $tmp, $ix, 2 );      // add a two bytes character
+            $charCnt += 1;
+          }
+          else if ($byte >= 224 && $byte <= 239) {  // start byte in three bytes character
+            $string .= substr( $tmp, $ix, 3 );      // add a three bytes character
+            $charCnt += 1;
+          }
+          else if ($byte >= 240 && $byte <= 244) {  // start byte in four bytes character
+            $string .= substr( $tmp, $ix, 4 );      // add a four bytes character
+            $charCnt += 1;
+          }
+        }
+      } // end for
+      $string .= $this->nl;
+      $tmp     = substr( $tmp, $ix );
+      if( empty( $tmp ))
+        break; // while-loop breakes here
+      else
+        $tmp  = ' '.$tmp;
+    } // end while
+    $string .= rtrim( $tmp ); // the rest
+    $string = str_replace( "\\\\n", "\\n", $string );
+    if( $this->nl != substr( $string, ( 0 - strlen( $this->nl ))))
+      $string .= $this->nl;
+    return $string;
+  }
+/**
+ * special characters management output
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.15 - 2010-09-24
+ * @param string $string
+ * @return string
+ */
+  function _strrep( $string ) {
+    switch( $this->format ) {
+      case 'xcal':
+        $string = str_replace( '\n',  $this->nl, $string);
+        $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string ))));
+        break;
+      default:
+        $pos = 0;
+        $specChars = array( 'n', 'N', 'r', ',', ';' );
+        while( $pos <= strlen( $string )) {
+          $pos = strpos( $string, "\\", $pos );
+          if( FALSE === $pos )
+            break;
+          if( !in_array( substr( $string, $pos, 1 ), $specChars )) {
+            $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos + 1 ));
+            $pos += 1;
+          }
+          $pos += 1;
+        }
+        if( FALSE !== strpos( $string, '"' ))
+          $string = str_replace('"',   "'",       $string);
+        if( FALSE !== strpos( $string, ',' ))
+          $string = str_replace(',',   '\,',      $string);
+        if( FALSE !== strpos( $string, ';' ))
+          $string = str_replace(';',   '\;',      $string);
+        if( FALSE !== strpos( $string, "\r\n" ))
+          $string = str_replace( "\r\n", '\n',    $string);
+        elseif( FALSE !== strpos( $string, "\r" ))
+          $string = str_replace( "\r", '\n',      $string);
+
+        elseif( FALSE !== strpos( $string, "\n" ))
+          $string = str_replace( "\n", '\n',      $string);
+
+        if( FALSE !== strpos( $string, '\N' ))
+          $string = str_replace( '\N', '\n',      $string);
+//        if( FALSE !== strpos( $string, $this->nl ))
+          $string = str_replace( $this->nl, '\n', $string);
+        break;
+    }
+    return $string;
+  }
+/**
+ * special characters management input (from iCal file)
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.22 - 2010-10-17
+ * @param string $string
+ * @return string
+ */
+  static function _strunrep( $string ) {
+    $string = str_replace( '\\\\', '\\',     $string);
+    $string = str_replace( '\,',   ',',      $string);
+    $string = str_replace( '\;',   ';',      $string);
+//    $string = str_replace( '\n',  $this->nl, $string); // ??
+    return $string;
+  }
+}
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ * class for calendar component VEVENT
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-12
+ */
+class vevent extends calendarComponent {
+  var $attach;
+  var $attendee;
+  var $categories;
+  var $comment;
+  var $contact;
+  var $class;
+  var $created;
+  var $description;
+  var $dtend;
+  var $dtstart;
+  var $duration;
+  var $exdate;
+  var $exrule;
+  var $geo;
+  var $lastmodified;
+  var $location;
+  var $organizer;
+  var $priority;
+  var $rdate;
+  var $recurrenceid;
+  var $relatedto;
+  var $requeststatus;
+  var $resources;
+  var $rrule;
+  var $sequence;
+  var $status;
+  var $summary;
+  var $transp;
+  var $url;
+  var $xprop;
+            //  component subcomponents container
+  var $components;
+/**
+ * constructor for calendar component VEVENT object
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-11-20
+ * @param  array $config
+ * @return void
+ */
+  function vevent( $config = array()) {
+    $this->calendarComponent();
+
+    $this->attach          = '';
+    $this->attendee        = '';
+    $this->categories      = '';
+    $this->class           = '';
+    $this->comment         = '';
+    $this->contact         = '';
+    $this->created         = '';
+    $this->description     = '';
+    $this->dtstart         = '';
+    $this->dtend           = '';
+    $this->duration        = '';
+    $this->exdate          = '';
+    $this->exrule          = '';
+    $this->geo             = '';
+    $this->lastmodified    = '';
+    $this->location        = '';
+    $this->organizer       = '';
+    $this->priority        = '';
+    $this->rdate           = '';
+    $this->recurrenceid    = '';
+    $this->relatedto       = '';
+    $this->requeststatus   = '';
+    $this->resources       = '';
+    $this->rrule           = '';
+    $this->sequence        = '';
+    $this->status          = '';
+    $this->summary         = '';
+    $this->transp          = '';
+    $this->url             = '';
+    $this->xprop           = '';
+
+    $this->components      = array();
+
+    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
+                                          $config['language']   = ICAL_LANG;
+    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
+    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
+    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
+    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
+    $this->setConfig( $config );
+
+  }
+/**
+ * create formatted output for calendar component VEVENT object instance
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-07
+ * @param array $xcaldecl
+ * @return string
+ */
+  function createComponent( &$xcaldecl ) {
+    $objectname    = $this->_createFormat();
+    $component     = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
+    $component    .= $this->createUid();
+    $component    .= $this->createDtstamp();
+    $component    .= $this->createAttach();
+    $component    .= $this->createAttendee();
+    $component    .= $this->createCategories();
+    $component    .= $this->createComment();
+    $component    .= $this->createContact();
+    $component    .= $this->createClass();
+    $component    .= $this->createCreated();
+    $component    .= $this->createDescription();
+    $component    .= $this->createDtstart();
+    $component    .= $this->createDtend();
+    $component    .= $this->createDuration();
+    $component    .= $this->createExdate();
+    $component    .= $this->createExrule();
+    $component    .= $this->createGeo();
+    $component    .= $this->createLastModified();
+    $component    .= $this->createLocation();
+    $component    .= $this->createOrganizer();
+    $component    .= $this->createPriority();
+    $component    .= $this->createRdate();
+    $component    .= $this->createRrule();
+    $component    .= $this->createRelatedTo();
+    $component    .= $this->createRequestStatus();
+    $component    .= $this->createRecurrenceid();
+    $component    .= $this->createResources();
+    $component    .= $this->createSequence();
+    $component    .= $this->createStatus();
+    $component    .= $this->createSummary();
+    $component    .= $this->createTransp();
+    $component    .= $this->createUrl();
+    $component    .= $this->createXprop();
+    $component    .= $this->createSubComponent();
+    $component    .= $this->componentEnd1.$objectname.$this->componentEnd2;
+    if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
+      foreach( $this->xcaldecl as $localxcaldecl )
+        $xcaldecl[] = $localxcaldecl;
+    }
+    return $component;
+  }
+}
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ * class for calendar component VTODO
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-12
+ */
+class vtodo extends calendarComponent {
+  var $attach;
+  var $attendee;
+  var $categories;
+  var $comment;
+  var $completed;
+  var $contact;
+  var $class;
+  var $created;
+  var $description;
+  var $dtstart;
+  var $due;
+  var $duration;
+  var $exdate;
+  var $exrule;
+  var $geo;
+  var $lastmodified;
+  var $location;
+  var $organizer;
+  var $percentcomplete;
+  var $priority;
+  var $rdate;
+  var $recurrenceid;
+  var $relatedto;
+  var $requeststatus;
+  var $resources;
+  var $rrule;
+  var $sequence;
+  var $status;
+  var $summary;
+  var $url;
+  var $xprop;
+            //  component subcomponents container
+  var $components;
+/**
+ * constructor for calendar component VTODO object
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-11-20
+ * @param array $config
+ * @return void
+ */
+  function vtodo( $config = array()) {
+    $this->calendarComponent();
+
+    $this->attach          = '';
+    $this->attendee        = '';
+    $this->categories      = '';
+    $this->class           = '';
+    $this->comment         = '';
+    $this->completed       = '';
+    $this->contact         = '';
+    $this->created         = '';
+    $this->description     = '';
+    $this->dtstart         = '';
+    $this->due             = '';
+    $this->duration        = '';
+    $this->exdate          = '';
+    $this->exrule          = '';
+    $this->geo             = '';
+    $this->lastmodified    = '';
+    $this->location        = '';
+    $this->organizer       = '';
+    $this->percentcomplete = '';
+    $this->priority        = '';
+    $this->rdate           = '';
+    $this->recurrenceid    = '';
+    $this->relatedto       = '';
+    $this->requeststatus   = '';
+    $this->resources       = '';
+    $this->rrule           = '';
+    $this->sequence        = '';
+    $this->status          = '';
+    $this->summary         = '';
+    $this->url             = '';
+    $this->xprop           = '';
+
+    $this->components      = array();
+
+    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
+                                          $config['language']   = ICAL_LANG;
+    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
+    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
+    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
+    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
+    $this->setConfig( $config );
+
+  }
+/**
+ * create formatted output for calendar component VTODO object instance
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-11-07
+ * @param array $xcaldecl
+ * @return string
+ */
+  function createComponent( &$xcaldecl ) {
+    $objectname    = $this->_createFormat();
+    $component     = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
+    $component    .= $this->createUid();
+    $component    .= $this->createDtstamp();
+    $component    .= $this->createAttach();
+    $component    .= $this->createAttendee();
+    $component    .= $this->createCategories();
+    $component    .= $this->createClass();
+    $component    .= $this->createComment();
+    $component    .= $this->createCompleted();
+    $component    .= $this->createContact();
+    $component    .= $this->createCreated();
+    $component    .= $this->createDescription();
+    $component    .= $this->createDtstart();
+    $component    .= $this->createDue();
+    $component    .= $this->createDuration();
+    $component    .= $this->createExdate();
+    $component    .= $this->createExrule();
+    $component    .= $this->createGeo();
+    $component    .= $this->createLastModified();
+    $component    .= $this->createLocation();
+    $component    .= $this->createOrganizer();
+    $component    .= $this->createPercentComplete();
+    $component    .= $this->createPriority();
+    $component    .= $this->createRdate();
+    $component    .= $this->createRelatedTo();
+    $component    .= $this->createRequestStatus();
+    $component    .= $this->createRecurrenceid();
+    $component    .= $this->createResources();
+    $component    .= $this->createRrule();
+    $component    .= $this->createSequence();
+    $component    .= $this->createStatus();
+    $component    .= $this->createSummary();
+    $component    .= $this->createUrl();
+    $component    .= $this->createXprop();
+    $component    .= $this->createSubComponent();
+    $component    .= $this->componentEnd1.$objectname.$this->componentEnd2;
+    if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
+      foreach( $this->xcaldecl as $localxcaldecl )
+        $xcaldecl[] = $localxcaldecl;
+    }
+    return $component;
+  }
+}
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ * class for calendar component VJOURNAL
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-12
+ */
+class vjournal extends calendarComponent {
+  var $attach;
+  var $attendee;
+  var $categories;
+  var $comment;
+  var $contact;
+  var $class;
+  var $created;
+  var $description;
+  var $dtstart;
+  var $exdate;
+  var $exrule;
+  var $lastmodified;
+  var $organizer;
+  var $rdate;
+  var $recurrenceid;
+  var $relatedto;
+  var $requeststatus;
+  var $rrule;
+  var $sequence;
+  var $status;
+  var $summary;
+  var $url;
+  var $xprop;
+/**
+ * constructor for calendar component VJOURNAL object
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-11-20
+ * @param array $config
+ * @return void
+ */
+  function vjournal( $config = array()) {
+    $this->calendarComponent();
+
+    $this->attach          = '';
+    $this->attendee        = '';
+    $this->categories      = '';
+    $this->class           = '';
+    $this->comment         = '';
+    $this->contact         = '';
+    $this->created         = '';
+    $this->description     = '';
+    $this->dtstart         = '';
+    $this->exdate          = '';
+    $this->exrule          = '';
+    $this->lastmodified    = '';
+    $this->organizer       = '';
+    $this->rdate           = '';
+    $this->recurrenceid    = '';
+    $this->relatedto       = '';
+    $this->requeststatus   = '';
+    $this->rrule           = '';
+    $this->sequence        = '';
+    $this->status          = '';
+    $this->summary         = '';
+    $this->url             = '';
+    $this->xprop           = '';
+
+    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
+                                          $config['language']   = ICAL_LANG;
+    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
+    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
+    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
+    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
+    $this->setConfig( $config );
+
+  }
+/**
+ * create formatted output for calendar component VJOURNAL object instance
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-12
+ * @param array $xcaldecl
+ * @return string
+ */
+  function createComponent( &$xcaldecl ) {
+    $objectname = $this->_createFormat();
+    $component  = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
+    $component .= $this->createUid();
+    $component .= $this->createDtstamp();
+    $component .= $this->createAttach();
+    $component .= $this->createAttendee();
+    $component .= $this->createCategories();
+    $component .= $this->createClass();
+    $component .= $this->createComment();
+    $component .= $this->createContact();
+    $component .= $this->createCreated();
+    $component .= $this->createDescription();
+    $component .= $this->createDtstart();
+    $component .= $this->createExdate();
+    $component .= $this->createExrule();
+    $component .= $this->createLastModified();
+    $component .= $this->createOrganizer();
+    $component .= $this->createRdate();
+    $component .= $this->createRequestStatus();
+    $component .= $this->createRecurrenceid();
+    $component .= $this->createRelatedTo();
+    $component .= $this->createRrule();
+    $component .= $this->createSequence();
+    $component .= $this->createStatus();
+    $component .= $this->createSummary();
+    $component .= $this->createUrl();
+    $component .= $this->createXprop();
+    $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
+    if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
+      foreach( $this->xcaldecl as $localxcaldecl )
+        $xcaldecl[] = $localxcaldecl;
+    }
+    return $component;
+  }
+}
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ * class for calendar component VFREEBUSY
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-12
+ */
+class vfreebusy extends calendarComponent {
+  var $attendee;
+  var $comment;
+  var $contact;
+  var $dtend;
+  var $dtstart;
+  var $duration;
+  var $freebusy;
+  var $organizer;
+  var $requeststatus;
+  var $url;
+  var $xprop;
+            //  component subcomponents container
+  var $components;
+/**
+ * constructor for calendar component VFREEBUSY object
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-11-10
+ * @param array $config
+ * @return void
+ */
+  function vfreebusy( $config = array()) {
+    $this->calendarComponent();
+
+    $this->attendee        = '';
+    $this->comment         = '';
+    $this->contact         = '';
+    $this->dtend           = '';
+    $this->dtstart         = '';
+    $this->duration        = '';
+    $this->freebusy        = '';
+    $this->organizer       = '';
+    $this->requeststatus   = '';
+    $this->url             = '';
+    $this->xprop           = '';
+
+    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
+                                          $config['language']   = ICAL_LANG;
+    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
+    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
+    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
+    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
+    $this->setConfig( $config );
+
+  }
+/**
+ * create formatted output for calendar component VFREEBUSY object instance
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.3.1 - 2007-11-19
+ * @param array $xcaldecl
+ * @return string
+ */
+  function createComponent( &$xcaldecl ) {
+    $objectname = $this->_createFormat();
+    $component  = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
+    $component .= $this->createUid();
+    $component .= $this->createDtstamp();
+    $component .= $this->createAttendee();
+    $component .= $this->createComment();
+    $component .= $this->createContact();
+    $component .= $this->createDtstart();
+    $component .= $this->createDtend();
+    $component .= $this->createDuration();
+    $component .= $this->createFreebusy();
+    $component .= $this->createOrganizer();
+    $component .= $this->createRequestStatus();
+    $component .= $this->createUrl();
+    $component .= $this->createXprop();
+    $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
+    if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
+      foreach( $this->xcaldecl as $localxcaldecl )
+        $xcaldecl[] = $localxcaldecl;
+    }
+    return $component;
+  }
+}
+/*********************************************************************************/
+/*********************************************************************************/
+/**
+ * class for calendar component VALARM
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-12
+ */
+class valarm extends calendarComponent {
+  var $action;
+  var $attach;
+  var $attendee;
+  var $description;
+  var $duration;
+  var $repeat;
+  var $summary;
+  var $trigger;
+  var $xprop;
+/**
+ * constructor for calendar component VALARM object
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-11-20
+ * @param array $config
+ * @return void
+ */
+  function valarm( $config = array()) {
+    $this->calendarComponent();
+
+    $this->action          = '';
+    $this->attach          = '';
+    $this->attendee        = '';
+    $this->description     = '';
+    $this->duration        = '';
+    $this->repeat          = '';
+    $this->summary         = '';
+    $this->trigger         = '';
+    $this->xprop           = '';
+
+    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
+                                          $config['language']   = ICAL_LANG;
+    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
+    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
+    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
+    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
+    $this->setConfig( $config );
+
+  }
+/**
+ * create formatted output for calendar component VALARM object instance
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-22
+ * @param array $xcaldecl
+ * @return string
+ */
+  function createComponent( &$xcaldecl ) {
+    $objectname    = $this->_createFormat();
+    $component     = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
+    $component    .= $this->createAction();
+    $component    .= $this->createAttach();
+    $component    .= $this->createAttendee();
+    $component    .= $this->createDescription();
+    $component    .= $this->createDuration();
+    $component    .= $this->createRepeat();
+    $component    .= $this->createSummary();
+    $component    .= $this->createTrigger();
+    $component    .= $this->createXprop();
+    $component    .= $this->componentEnd1.$objectname.$this->componentEnd2;
+    return $component;
+  }
+}
+/**********************************************************************************
+/*********************************************************************************/
+/**
+ * class for calendar component VTIMEZONE
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-12
+ */
+class vtimezone extends calendarComponent {
+  var $timezonetype;
+
+  var $comment;
+  var $dtstart;
+  var $lastmodified;
+  var $rdate;
+  var $rrule;
+  var $tzid;
+  var $tzname;
+  var $tzoffsetfrom;
+  var $tzoffsetto;
+  var $tzurl;
+  var $xprop;
+            //  component subcomponents container
+  var $components;
+/**
+ * constructor for calendar component VTIMEZONE object
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.6.27 - 2010-11-20
+ * @param mixed $timezonetype optional, default FALSE ( STANDARD / DAYLIGHT )
+ * @param array $config
+ * @return void
+ */
+  function vtimezone( $timezonetype=FALSE, $config = array()) {
+    if( is_array( $timezonetype )) {
+      $config       = $timezonetype;
+      $timezonetype = FALSE;
+    }
+    if( !$timezonetype )
+      $this->timezonetype = 'VTIMEZONE';
+    else
+      $this->timezonetype = strtoupper( $timezonetype );
+    $this->calendarComponent();
+
+    $this->comment         = '';
+    $this->dtstart         = '';
+    $this->lastmodified    = '';
+    $this->rdate           = '';
+    $this->rrule           = '';
+    $this->tzid            = '';
+    $this->tzname          = '';
+    $this->tzoffsetfrom    = '';
+    $this->tzoffsetto      = '';
+    $this->tzurl           = '';
+    $this->xprop           = '';
+
+    $this->components      = array();
+
+    if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
+                                          $config['language']   = ICAL_LANG;
+    if( !isset( $config['allowEmpty'] ))  $config['allowEmpty'] = TRUE;
+    if( !isset( $config['nl'] ))          $config['nl']         = PHP_EOL;
+    if( !isset( $config['format'] ))      $config['format']     = 'iCal';
+    if( !isset( $config['delimiter'] ))   $config['delimiter']  = DIRECTORY_SEPARATOR;
+    $this->setConfig( $config );
+
+  }
+/**
+ * create formatted output for calendar component VTIMEZONE object instance
+ *
+ * @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
+ * @since 2.5.1 - 2008-10-25
+ * @param array $xcaldecl
+ * @return string
+ */
+  function createComponent( &$xcaldecl ) {
+    $objectname    = $this->_createFormat();
+    $component     = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
+    $component    .= $this->createTzid();
+    $component    .= $this->createLastModified();
+    $component    .= $this->createTzurl();
+    $component    .= $this->createDtstart();
+    $component    .= $this->createTzoffsetfrom();
+    $component    .= $this->createTzoffsetto();
+    $component    .= $this->createComment();
+    $component    .= $this->createRdate();
+    $component    .= $this->createRrule();
+    $component    .= $this->createTzname();
+    $component    .= $this->createXprop();
+    $component    .= $this->createSubComponent();
+    $component    .= $this->componentEnd1.$objectname.$this->componentEnd2;
+    if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
+      foreach( $this->xcaldecl as $localxcaldecl )
+        $xcaldecl[] = $localxcaldecl;
+    }
+    return $component;
+  }
+}
+?>
Index: trunk/library/iCalcreator/lgpl.txt
===================================================================
--- trunk/library/iCalcreator/lgpl.txt	(revision 5146)
+++ trunk/library/iCalcreator/lgpl.txt	(revision 5146)
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
Index: trunk/library/iCalcreator/releaseNotes-2.8.txt
===================================================================
--- trunk/library/iCalcreator/releaseNotes-2.8.txt	(revision 5146)
+++ trunk/library/iCalcreator/releaseNotes-2.8.txt	(revision 5146)
@@ -0,0 +1,100 @@
+2.6.1 ######################
+config 'nl' default set to PHP_EOL
+function vcalendar, parse
+
+2.6.2 ###################### /thanks, Simon L.
+function selectComponents, select components with (latest) recurrence_id
+
+2.6.3 ######################
+function sort, refactored
+
+2.6.4 ######################
+function deleteProperty (removal of x-properties)
+
+2.6.5 ######################
+function getConfig, typo error
+
+2.6.6 ######################
+function setConfig, no longer creates file (on disc) when setting 'filename'
+
+2.6.7 ###################### /thanks Steve S.
+using.html, typo errors
+
+2.6.8 ######################
+Updated calendar properties PRODID and VERSION
+
+2.6.9 ######################
+function createSequence, setSequence, getProperty, allow SEQUENCE '0'
+
+2.6.10 ######################
+function _format_duration, fix duration format, skip trailing ..0S, ..0M0S etc.
+
+2.6.11 ######################
+function (component) parse and management of all TEXT-properties
+
+2.6.12 ###################### /thanks Enrico T.
+function _setMval, PHP error
+
+2.6.13 ###################### /thanks Ivar N. and Robert S.
+function _size75, support for multibyte characters
+alt. using mb_string, if installed and configured
+
+2.6.15 ######################
+function _strrep, eol character management
+
+2.6.19 ######################
+function deletePropertyM typo errror
+
+2.6.20 ######################
+function (component) parse, return value
+
+2.6.20 ######################
+function selectComponent, accepts DUE in vtodo if DTSTART is missing
+
+2.6.22 ######################
+utility (static) functions collected in support class,
+iCalUtilityFunctions.class.php
+
+2.6.23 ######################
+function selectComponents, RDATE/EXDATE bug
+
+2.6.24 ######################
+function setDescription, force index=1 if NOT vjournal
+DESCRIPTION MAY occur more than once only in VJOURNAL component
+
+2.6.25 ######################
+function makeDtstamp, bug in date creation
+
+2.6.26 ######################
+function setComponent, insert at index position
+
+2.6.27 ######################
+function setConfig, accepts array as single argument
+function getConfig. returns array if called without arguments
+create vcalendar/components and accept config array as argument
+
+2.6.28 ###################### /Damian R.
+function setComponent, bugfix when using a short UID
+
+2.6.29 ######################
+function setOrganizer, value prefix (protocol)
+
+2.6.31 ###################### /thanks Nate
+function _date_time_string, Time Zone incorrectly detected
+
+2.6.32 ###################### /thanks Mauro B.
+function (calendar ) parse, removed argument filename, accepts string/array (calendar)
+
+2.6.33 ######################
+new function newComponent, factory-method for creating components
+
+2.6.34 ######################
+function setAttendee, setRdate, all TEXT-properties etc
+property parameters in rfc 2445 order
+
+2.6.35 ######################
+function _date_time_string, bug when datetime ends with 'T000000Z'
+
+2.6.36 ###################### /thanks Greg L.
+date_default_timezone_set is available from PHP 5.1.0, updated
+
