/hg/icedtea-web: 2 new changesets

adomurad at icedtea.classpath.org adomurad at icedtea.classpath.org
Tue Jan 15 12:50:11 PST 2013


changeset 2e9ee0b45e33 in /hg/icedtea-web
details: http://icedtea.classpath.org/hg/icedtea-web?cmd=changeset;node=2e9ee0b45e33
author: Adam Domurad <adomurad at redhat.com>
date: Tue Jan 15 14:34:47 2013 -0500

	Fix for PR1198: JSObject passed incorrectly to Javascript


changeset e647159f95a4 in /hg/icedtea-web
details: http://icedtea.classpath.org/hg/icedtea-web?cmd=changeset;node=e647159f95a4
author: Adam Domurad <adomurad at redhat.com>
date: Tue Jan 15 15:50:06 2013 -0500

	Unit tests for PR1198: JSObject not passed correctly to Javascript


diffstat:

 ChangeLog                                                                     |   42 +
 NEWS                                                                          |    1 +
 plugin/icedteanp/IcedTeaJavaRequestProcessor.cc                               |    2 +-
 plugin/icedteanp/IcedTeaPluginRequestProcessor.cc                             |    2 +-
 plugin/icedteanp/IcedTeaPluginUtils.cc                                        |   13 +
 plugin/icedteanp/java/netscape/javascript/JSObject.java                       |   10 +
 plugin/icedteanp/java/netscape/javascript/JSObjectUnboxPermission.java        |   49 ++
 plugin/icedteanp/java/netscape/javascript/JSUtil.java                         |   14 +-
 plugin/icedteanp/java/sun/applet/PluginAppletSecurityContext.java             |  231 ++++-----
 plugin/icedteanp/java/sun/applet/PluginAppletViewer.java                      |   60 +--
 tests/netx/unit/sun/applet/PluginAppletSecurityContextTest.java               |  185 ++++++++
 tests/reproducers/simple/JSObjectFromEval/testcases/JSObjectFromEvalTest.java |    3 -
 12 files changed, 429 insertions(+), 183 deletions(-)

diffs (truncated from 820 to 500 lines):

diff -r 4118632d3c49 -r e647159f95a4 ChangeLog
--- a/ChangeLog	Thu Jan 10 18:23:06 2013 +0100
+++ b/ChangeLog	Tue Jan 15 15:50:06 2013 -0500
@@ -1,3 +1,45 @@
+2013-01-15  Adam Domurad  <adomurad at redhat.com>
+
+	Unit test for PluginAppletSecurityContext#toObjectIDString. Make 
+	PluginAppletSecurityContext more unit-testable.
+	* plugin/icedteanp/java/sun/applet/PluginAppletSecurityContext.java: 
+	Don't initialize security manager in constructor. Fix a few Java->JS 
+	corner cases.
+	* plugin/icedteanp/java/sun/applet/PluginMain.java: Create testing-only
+	constructor for bypassing initialization of SecurityManager.
+	* tests/netx/unit/sun/applet/PluginAppletSecurityContextTest.java:
+	Unit test for all the corner cases of converting a Java object to a
+	string that can be precisely identified.
+
+2013-01-15  Adam Domurad  <adomurad at redhat.com>
+
+	Fix PR1198: JSObject passed incorrectly to Javascript
+	* plugin/icedteanp/IcedTeaJavaRequestProcessor.cc: Pass extra data for
+	'jsobject' object result messages.
+	* plugin/icedteanp/IcedTeaPluginRequestProcessor.cc: Same.
+	* plugin/icedteanp/IcedTeaPluginUtils.cc: Add special casing of 
+	javascript references passed from java.
+	* plugin/icedteanp/java/netscape/javascript/JSObjectUnboxPermission.java:
+	New permission for unboxing a JSObject's internal reference.
+	* plugin/icedteanp/java/netscape/javascript/JSObject.java
+	(getInternalReference): New, package-private, retrieves internal
+	reference (Must have proper permission).
+	* plugin/icedteanp/java/netscape/javascript/JSUtil.java
+	(getJSObjectInternalReference) New, utility for accessing 
+	JSObject#getInternalReference from outside the package.
+	* plugin/icedteanp/java/sun/applet/PluginAppletSecurityContext.java:
+	(toObjectIDString): New, creates a string that precisely identifies a 
+	Java object.
+	(handleMessage): Replace a lot of duplicated functionality with 
+	'toObjectIDString'.
+	* plugin/icedteanp/java/sun/applet/PluginAppletViewer.java: Replace 
+	duplicated functionality with 'toObjectIDString'.
+	* tests/reproducers/simple/JSObjectFromEval/srcs/JSObjectFromEval.java:
+	Don't print out type passed (differs from browser to browser).
+	* tests/reproducers/simple/JSObjectFromEval/testcases/JSObjectFromEvalTest.java:
+	Don't check type passed (differs from browser to browser). Remove 
+	known-to-fail. Reformat.
+
 2013-01-10  Jiri Vanek  <jvanek at redhat.com>
 
 	Download indicator made compact for more then one jar
diff -r 4118632d3c49 -r e647159f95a4 NEWS
--- a/NEWS	Thu Jan 10 18:23:06 2013 +0100
+++ b/NEWS	Tue Jan 15 15:50:06 2013 -0500
@@ -24,6 +24,7 @@
   - PR1166: Embedded JNLP File is not supported in applet tag
   - PR1217: Add command line arguments for plugins
   - PR1189: Icedtea-plugin requires code attribute when using jnlp_href
+  - PR1198: JSObject is not passed to javascript correctly
 * Common
   - PR1049: Extension jnlp's signed jar with the content of only META-INF/* is considered
   - PR955: regression: SweetHome3D fails to run
diff -r 4118632d3c49 -r e647159f95a4 plugin/icedteanp/IcedTeaJavaRequestProcessor.cc
--- a/plugin/icedteanp/IcedTeaJavaRequestProcessor.cc	Thu Jan 10 18:23:06 2013 +0100
+++ b/plugin/icedteanp/IcedTeaJavaRequestProcessor.cc	Tue Jan 15 15:50:06 2013 -0500
@@ -131,7 +131,7 @@
 			           !message_parts->at(4)->find("GetObjectArrayElement"))
 			{
 
-			    if (!message_parts->at(5)->find("literalreturn"))
+			    if (!message_parts->at(5)->find("literalreturn") || !message_parts->at(5)->find("jsobject"))
                 {
 			        // literal returns don't have a corresponding jni id
 			        result->return_identifier = 0;
diff -r 4118632d3c49 -r e647159f95a4 plugin/icedteanp/IcedTeaPluginRequestProcessor.cc
--- a/plugin/icedteanp/IcedTeaPluginRequestProcessor.cc	Thu Jan 10 18:23:06 2013 +0100
+++ b/plugin/icedteanp/IcedTeaPluginRequestProcessor.cc	Tue Jan 15 15:50:06 2013 -0500
@@ -413,7 +413,7 @@
     member = (NPVariant*) (IcedTeaPluginUtilities::stringToJSID(*(message_parts->at(5))));
     propertyNameID = *(message_parts->at(6));
 
-    if (*(message_parts->at(7)) == "literalreturn")
+    if (*(message_parts->at(7)) == "literalreturn" || *(message_parts->at(7)) == "jsobject" )
     {
         value.append(*(message_parts->at(7)));
         value.append(" ");
diff -r 4118632d3c49 -r e647159f95a4 plugin/icedteanp/IcedTeaPluginUtils.cc
--- a/plugin/icedteanp/IcedTeaPluginUtils.cc	Thu Jan 10 18:23:06 2013 +0100
+++ b/plugin/icedteanp/IcedTeaPluginUtils.cc	Tue Jan 15 15:50:06 2013 -0500
@@ -776,6 +776,14 @@
 }
 
 static bool
+javaJSObjectResultToNPVariant(const std::string& js_id, NPVariant* variant)
+{
+    NPVariant* result_variant = (NPVariant*) IcedTeaPluginUtilities::stringToJSID(js_id);
+    *variant = *result_variant;
+    return true;
+}
+
+static bool
 javaObjectResultToNPVariant(NPP instance, const std::string& jobject_id, NPVariant* variant)
 {
     // Reference the class object so we can construct an NPObject with it and the instance
@@ -811,9 +819,14 @@
         std::string* java_value, NPVariant* variant)
 {
     int literal_n = sizeof("literalreturn"); // Accounts for one space char
+    int jsobject_n = sizeof("jsobject"); // Accounts for one space char
+
     if (strncmp("literalreturn ", java_value->c_str(), literal_n) == 0)
     {
         javaPrimitiveResultToNPVariant(java_value->substr(literal_n), variant);
+    } else if (strncmp("jsobject ", java_value->c_str(), jsobject_n) == 0)
+    {
+        javaJSObjectResultToNPVariant(java_value->substr(jsobject_n), variant);
     } else
     {
         std::string jobject_id = *java_value;
diff -r 4118632d3c49 -r e647159f95a4 plugin/icedteanp/java/netscape/javascript/JSObject.java
--- a/plugin/icedteanp/java/netscape/javascript/JSObject.java	Thu Jan 10 18:23:06 2013 +0100
+++ b/plugin/icedteanp/java/netscape/javascript/JSObject.java	Tue Jan 15 15:50:06 2013 -0500
@@ -100,6 +100,16 @@
     }
 
     /**
+     * Package-private method used through JSUtil#getJSObjectInternalReference.
+     * We make this package-private to avoid polluting the public interface.
+     * @return the internal identifier
+     */
+    long getInternalReference() {
+        AccessController.getContext().checkPermission(new JSObjectUnboxPermission());
+        return internal;
+    }
+
+    /**
      * it is illegal to construct a JSObject manually
      */
     public JSObject(int jsobj_addr) {
diff -r 4118632d3c49 -r e647159f95a4 plugin/icedteanp/java/netscape/javascript/JSObjectUnboxPermission.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/java/netscape/javascript/JSObjectUnboxPermission.java	Tue Jan 15 15:50:06 2013 -0500
@@ -0,0 +1,49 @@
+/* JSObjectUnboxPermission.java
+   Copyright (C) 2012  Red Hat
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+IcedTea 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
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+package netscape.javascript;
+
+import java.security.BasicPermission;
+
+/**
+ * Permission to access internal reference of JSObject
+ */
+public class JSObjectUnboxPermission extends BasicPermission {
+    public JSObjectUnboxPermission() {
+        super("JSObjectUnbox");
+    }
+}
diff -r 4118632d3c49 -r e647159f95a4 plugin/icedteanp/java/netscape/javascript/JSUtil.java
--- a/plugin/icedteanp/java/netscape/javascript/JSUtil.java	Thu Jan 10 18:23:06 2013 +0100
+++ b/plugin/icedteanp/java/netscape/javascript/JSUtil.java	Tue Jan 15 15:50:06 2013 -0500
@@ -57,4 +57,16 @@
 
         return captureStream.toString();
     }
-}
+
+    /**
+     * Uses package-private method JSObject.getInternalReference.
+     * This is package-private to avoid polluting the public interface.
+     * @param js JSObject to unbox
+     * @return the internal reference stored by the JSObject
+     */
+    public static long getJSObjectInternalReference(JSObject js) {
+        // NB: permission is checked in JSObject
+        return js.getInternalReference();
+    }
+
+}
\ No newline at end of file
diff -r 4118632d3c49 -r e647159f95a4 plugin/icedteanp/java/sun/applet/PluginAppletSecurityContext.java
--- a/plugin/icedteanp/java/sun/applet/PluginAppletSecurityContext.java	Thu Jan 10 18:23:06 2013 +0100
+++ b/plugin/icedteanp/java/sun/applet/PluginAppletSecurityContext.java	Tue Jan 15 15:50:06 2013 -0500
@@ -238,19 +238,24 @@
 
     long startTime = 0;
 
-    public PluginAppletSecurityContext(int identifier) {
+    /* Package-private constructor that allows for bypassing security manager installation.
+     * This is useful for testing. Note that while the public constructor should be used otherwise,
+     * the security installation can't be bypassed if it has already occurred.*/
+    PluginAppletSecurityContext(int identifier, boolean ensureSecurityContext) {
         this.identifier = identifier;
 
-        // We need a security manager.. and since there is a good chance that
-        // an applet will be loaded at some point, we should make it the SM
-        // that JNLPRuntime will try to install
-        if (System.getSecurityManager() == null) {
-            JNLPRuntime.initialize(/* isApplication */false);
-            JNLPRuntime.setDefaultLaunchHandler(new DefaultLaunchHandler(System.err));
+        if (ensureSecurityContext) {
+            // We need a security manager.. and since there is a good chance that
+            // an applet will be loaded at some point, we should make it the SM
+            // that JNLPRuntime will try to install
+            if (System.getSecurityManager() == null) {
+                JNLPRuntime.initialize(/* isApplication */false);
+                JNLPRuntime.setDefaultLaunchHandler(new DefaultLaunchHandler(System.err));
+            }
+
+            JNLPRuntime.disableExit();
         }
 
-        JNLPRuntime.disableExit();
-
         URL u = null;
         try {
             u = new URL("file://");
@@ -261,6 +266,10 @@
         this.classLoaders.put(liveconnectLoader, u);
     }
 
+    public PluginAppletSecurityContext(int identifier) {
+        this(identifier, true);
+    }
+
     private static <V> V parseCall(String s, ClassLoader cl, Class<V> c) {
         if (c == Integer.class)
             return c.cast(new Integer(s));
@@ -321,6 +330,83 @@
         return map;
     }
 
+    private static long privilegedJSObjectUnbox(final JSObject js) {
+        return AccessController.doPrivileged(new PrivilegedAction<Long>() {
+            public Long run() {  
+                return JSUtil.getJSObjectInternalReference(js); 
+            }
+        });
+    }
+
+    /**
+     * Create a string that identifies a Java object precisely, for passing to 
+     * Javascript.
+     * 
+     * For builtin value types, a 'literalreturn' prefix is used and the object 
+     * is passed with a string representation.
+     * 
+     * For JSObject's, a 'jsobject' prefix is used and the object is passed 
+     * with the JSObject's internal identifier.
+     * 
+     * For other Java objects, an object store reference is used.
+     * 
+     * @param obj the object for which to create an identifier
+     * @param type the type to use for representation decisions
+     * @param unboxPrimitives whether to treat boxed primitives as value types
+     * @return an identifier string
+     */
+    public String toObjectIDString(Object obj, Class<?> type, boolean unboxPrimitives) {
+
+        /* Void (can occur from declared return type), pass special "void" string: */
+        if (type == Void.TYPE) {
+            return "literalreturn void";
+        }
+
+        /* Null, pass special "null" string: */
+        if (obj == null) {
+            return "literalreturn null";
+        }
+
+        /* Primitive, accurately represented by its toString() form: */
+        boolean returnAsString = ( type == Boolean.TYPE
+                                || type == Byte.TYPE 
+                                || type == Short.TYPE 
+                                || type == Integer.TYPE 
+                                || type == Long.TYPE );
+        if (unboxPrimitives) {
+            returnAsString = ( returnAsString 
+                            || type == Boolean.class
+                            || type == Byte.class
+                            || type == Short.class
+                            || type == Integer.class 
+                            || type == Long.class);
+        }
+        if (returnAsString) {
+            return "literalreturn " + obj.toString();
+        } 
+
+        /* Floating point number, we ensure we give enough precision: */
+        if ( type == Float.TYPE || type == Double.TYPE || 
+                ( unboxPrimitives && (type == Float.class || type == Double.class) )) {
+            return "literalreturn " + String.format("%308.308e", obj);
+        }
+
+        /* Character that should be returned as number: */
+        if (type == Character.TYPE || (unboxPrimitives && type == Character.class)) {
+            return "literalreturn " + (int) (Character) obj;
+        }
+
+        /* JSObject, unwrap underlying Javascript reference: */
+        if (type == netscape.javascript.JSObject.class) {
+            long reference = privilegedJSObjectUnbox((JSObject)obj);
+            return "jsobject " + Long.toString(reference);
+        }
+
+        /* Other kind of object, track this object and return our reference: */
+        store.reference(obj);
+        return store.getIdentifier(obj).toString();
+    }
+
     public void handleMessage(int reference, String src, AccessControlContext callContext, String message) {
 
         startTime = new java.util.Date().getTime();
@@ -429,56 +515,16 @@
                 if (ret instanceof Throwable)
                     throw (Throwable) ret;
 
-                if (ret == null) {
-                    write(reference, "GetStaticField literalreturn null");
-                } else if (f.getType() == Boolean.TYPE
-                        || f.getType() == Byte.TYPE
-                        || f.getType() == Short.TYPE
-                        || f.getType() == Integer.TYPE
-                        || f.getType() == Long.TYPE) {
-                    write(reference, "GetStaticField literalreturn " + ret);
-                } else if (f.getType() == Float.TYPE
-                                || f.getType() == Double.TYPE) {
-                    write(reference, "GetStaticField literalreturn " + String.format("%308.308e", ret));
-                } else if (f.getType() == Character.TYPE) {
-                    write(reference, "GetStaticField literalreturn " + (int) (Character) ret);
-                } else {
-                    // Track returned object.
-                    store.reference(ret);
-                    write(reference, "GetStaticField " + store.getIdentifier(ret));
-                }
+                String objIDStr = toObjectIDString(ret, f.getType(), false /*do not unbox primitives*/);
+                write(reference, "GetStaticField " + objIDStr);
             } else if (message.startsWith("GetValue")) {
                 String[] args = message.split(" ");
                 Integer index = parseCall(args[1], null, Integer.class);
 
                 Object ret = store.getObject(index);
 
-                if (ret == null) {
-                    write(reference, "GetValue literalreturn null");
-                } else if (ret.getClass() == Boolean.TYPE
-                        || ret.getClass() == Boolean.class
-                        || ret.getClass() == Byte.TYPE
-                        || ret.getClass() == Byte.class
-                        || ret.getClass() == Short.TYPE
-                        || ret.getClass() == Short.class
-                        || ret.getClass() == Integer.TYPE
-                        || ret.getClass() == Integer.class
-                        || ret.getClass() == Long.TYPE
-                        || ret.getClass() == Long.class) {
-                    write(reference, "GetValue literalreturn " + ret);
-                } else if (ret.getClass() == Float.TYPE
-                        || ret.getClass() == Float.class
-                        || ret.getClass() == Double.TYPE
-                        || ret.getClass() == Double.class) {
-                    write(reference, "GetValue literalreturn " + String.format("%308.308e", ret));
-                } else if (ret.getClass() == Character.TYPE
-                        || ret.getClass() == Character.class) {
-                    write(reference, "GetValue literalreturn " + (int) (Character) ret);
-                } else {
-                    // Track returned object.
-                    store.reference(ret);
-                    write(reference, "GetValue " + store.getIdentifier(ret));
-                }
+                String objIDStr = toObjectIDString(ret, ret.getClass(), true /*unbox primitives*/);
+                write(reference, "GetValue " + objIDStr);
             } else if (message.startsWith("SetStaticField") ||
                                    message.startsWith("SetField")) {
                 String[] args = message.split(" ");
@@ -519,24 +565,8 @@
                 Object ret = Array.get(array, index);
                 Class<?> retClass = array.getClass().getComponentType(); // prevent auto-boxing influence
 
-                if (ret == null) {
-                    write(reference, "GetObjectArrayElement literalreturn null");
-                } else if (retClass == Boolean.TYPE
-                        || retClass == Byte.TYPE
-                        || retClass == Short.TYPE
-                        || retClass == Integer.TYPE
-                        || retClass == Long.TYPE) {
-                    write(reference, "GetObjectArrayElement literalreturn " + ret);
-                } else if (retClass == Float.TYPE
-                                || retClass == Double.TYPE) {
-                    write(reference, "GetObjectArrayElement literalreturn " + String.format("%308.308e", ret));
-                } else if (retClass == Character.TYPE) {
-                    write(reference, "GetObjectArrayElement literalreturn " + (int) (Character) ret);
-                } else {
-                    // Track returned object.
-                    store.reference(ret);
-                    write(reference, "GetObjectArrayElement " + store.getIdentifier(ret));
-                }
+                String objIDStr = toObjectIDString(ret, retClass, false /*do not unbox primitives*/);
+                write(reference, "GetObjectArrayElement " + objIDStr);
 
             } else if (message.startsWith("SetObjectArrayElement")) {
                 String[] args = message.split(" ");
@@ -586,25 +616,8 @@
                 if (ret instanceof Throwable)
                     throw (Throwable) ret;
 
-                if (ret == null) {
-                    write(reference, "GetField literalreturn null");
-                } else if (f.getType() == Boolean.TYPE
-                        || f.getType() == Byte.TYPE
-                        || f.getType() == Short.TYPE
-                        || f.getType() == Integer.TYPE
-                        || f.getType() == Long.TYPE) {
-                    write(reference, "GetField literalreturn " + ret);
-                } else if (f.getType() == Float.TYPE
-                                || f.getType() == Double.TYPE) {
-                    write(reference, "GetField literalreturn " + String.format("%308.308e", ret));
-                } else if (f.getType() == Character.TYPE) {
-                    write(reference, "GetField literalreturn " + (int) (Character) ret);
-                } else {
-                    // Track returned object.
-                    store.reference(ret);
-                    write(reference, "GetField " + store.getIdentifier(ret));
-                }
-
+                String objIDStr = toObjectIDString(ret, f.getType(), false /*do not unbox primitives*/);
+                write(reference, "GetField " + objIDStr);
             } else if (message.startsWith("GetObjectClass")) {
                 int oid = Integer.parseInt(message.substring("GetObjectClass"
                                                 .length() + 1));
@@ -690,27 +703,8 @@
                                                 , collapsedArgs, " and that returned: ", ret
                                                 , " of type ", retO);
 
-                if (m.getReturnType().equals(java.lang.Void.class) ||
-                                    m.getReturnType().equals(java.lang.Void.TYPE)) {
-                    write(reference, "CallMethod literalreturn void");
-                } else if (ret == null) {
-                    write(reference, "CallMethod literalreturn null");
-                } else if (m.getReturnType() == Boolean.TYPE
-                                                || m.getReturnType() == Byte.TYPE
-                                                || m.getReturnType() == Short.TYPE
-                                                || m.getReturnType() == Integer.TYPE
-                                                || m.getReturnType() == Long.TYPE) {
-                    write(reference, "CallMethod literalreturn " + ret);
-                } else if (m.getReturnType() == Float.TYPE
-                                || m.getReturnType() == Double.TYPE) {
-                    write(reference, "CallMethod literalreturn " + String.format("%308.308e", ret));
-                } else if (m.getReturnType() == Character.TYPE) {
-                    write(reference, "CallMethod literalreturn " + (int) (Character) ret);
-                } else {
-                    // Track returned object.
-                    store.reference(ret);
-                    write(reference, "CallMethod " + store.getIdentifier(ret));
-                }
+                String objIDStr = toObjectIDString(ret, m.getReturnType(), false /*do not unbox primitives*/);
+                write(reference, "CallMethod " + objIDStr);
             } else if (message.startsWith("GetSuperclass")) {
                 String[] args = message.split(" ");
                 Integer classID = parseCall(args[1], null, Integer.class);
@@ -778,10 +772,7 @@
                 buf = new StringBuffer(b.length * 2);
                 buf.append(b.length);
                 for (int i = 0; i < b.length; i++)
-                    buf
-                                                        .append(" "
-                                                                        + Integer
-                                                                                        .toString(((int) b[i]) & 0x0ff, 16));
+                    buf.append(" " + Integer.toString(((int) b[i]) & 0x0ff, 16));
 



More information about the distro-pkg-dev mailing list