Hello again. So I've started my my effort and since it's my first
attempt, I'd like some opinions and review.

I started with stringprep and punycode parts. Some functions are kinda
difficult to use in Vala, as they require preallocated buffer to store
output and some even use input buffer for it. So I've created some
inline methods to honor Vala's "immutable strings" rule.

I've also bound some structs under different name, so it is more
obvious what they are (I know I probably shouldn't do that.. I'll
change it back if that's a problem.)


I've also stumbled across a strange problem. In the Punycode.decode
inline method, I have to copy the input string (even when I don't use
the copy at all), otherwise it crashes sometimes. It's probably some
mistake I made in the inline methods, but I have no idea how can that
copy affect anything. C code gives me no clue. Can anyone look into
it?

For the test case to work, you have to add following bindings to glib-2.0.vapi:

        [CCode (array_length = true)]
        public unichar[] utf8_to_ucs4 (string str, long len = -1, out long
items_read = null) throws ConvertError;
        [CCode (array_length = true)]
        public unichar[] utf8_to_ucs4_fast (string str, long len = -1);
        
        public string ucs4_to_utf8 (unichar[] input, out long items_read =
null, out long items_written = null) throws ConvertError;

By the way, C documentation of libidn is here:
http://www.gnu.org/software/libidn/manual/libidn.html#Stringprep-Functions

Thanks in advance for any pointers.

Dne 13. červen 2009 20:55 Jiří Zárevúcky <zarevucky.j...@gmail.com> napsal(a):
> Hello. Is there anyone working on those? There is no mention on this
> list or bugzilla, so I suppose there isn't. I've already started
> frying my brain with it. :)
>
// Copyright © 2009 Jiří Zárevúcky <zarevucky.j...@gmail.com>
//
// Vala binding for GNU libidn library.
// This file doesn't bind unicode and charset processing functions, as those are already present in GLib.
//

namespace libidn {

	[CCode (cname="STRINGPREP_VERSION")]
	public const string LIBIDN_VERSION;

	[CCode (cheader_filename="stringprep.h", cprefix="stringprep_rfc3454_")]
	// Stringprep character tables defined in RFC3454

	namespace RFC3454 {
		public const Stringprep.TableElement[] A_1;
		public const Stringprep.TableElement[] B_1;
		public const Stringprep.TableElement[] B_2;
		public const Stringprep.TableElement[] B_3;
		public const Stringprep.TableElement[] C_1_1;
		public const Stringprep.TableElement[] C_1_2;
		public const Stringprep.TableElement[] C_2_1;
		public const Stringprep.TableElement[] C_2_2;
		public const Stringprep.TableElement[] C_3;
		public const Stringprep.TableElement[] C_4;
		public const Stringprep.TableElement[] C_5;
		public const Stringprep.TableElement[] C_6;
		public const Stringprep.TableElement[] C_7;
		public const Stringprep.TableElement[] C_8;
		public const Stringprep.TableElement[] C_9;
		public const Stringprep.TableElement[] D_1;
		public const Stringprep.TableElement[] D_2;
	}

	[CCode (cheader_filename="stringprep.h", cprefix="Stringprep_", lower_case_cprefix="stringprep_")]
	namespace Stringprep {

		/* constants */

		public const int MAX_MAP_CHARS;

		/* enums */

		[CCode (cname="Stringprep_rc", cprefix="STRINGPREP_")]
		public enum ReturnCode {
			OK,
			CONTAINS_UNASSIGNED,
			CONTAINS_PROHIBITED,
			BIDI_BOTH_L_AND_RAL,
			BIDI_LEADTRAIL_NOT_RAL,
			BIDI_CONTAINS_PROHIBITED,
			TOO_SMALL_BUFFER,
			PROFILE_ERROR,
			FLAG_ERROR,
			UNKNOWN_PROFILE,
			NFKC_FAILED,
			MALLOC_ERROR
		}

		[Flags]
		[CCode (cname="Stringprep_profile_flags", cprefix="STRINGPREP_")]
		public enum ProfileFlags {
			NO_NFKC,
			NO_BIDI,
			NO_UNASSIGNED
		}

		[CCode (cname="Stringprep_profile_steps", cprefix="STRINGPREP_")]
		public enum ProfileSteps {
			NFKC,
			BIDI,
			MAP_TABLE,
			UNASSIGNED_TABLE,
			PROHIBIT_TABLE,
			BIDI_PROHIBIT_TABLE,
			BIDI_RAL_TABLE,
			BIDI_L_TABLE
		}

		/* structs */

		[Compact]
		[CCode (cname="Stringprep_table_element", set_type_id=false)]
		public struct TableElement {
			// start of the range, or a single charater to replace
			unichar start;
			// end of the range, or 0 if only one character
			unichar end;
			// characters to replace with, or null if element is a range
			// this shouldn't have more than MAP_MAX_CHARS members (== 4)
			unichar[] map;
		}

		[Compact]
		[CCode (cname="Stringprep_profile", set_type_id=false)]
		public struct ProfileTable {
			ProfileSteps operation;
			ProfileFlags flags;
			TableElement[] table;
		}

		[Compact]
		[CCode (cname="Stringprep_profiles", set_type_id=false)]
		public struct NamedProfile {
			string name;
			ProfileTable[] tables;
		}

		/* predefined profiles */

		[CCode (cname="stringprep_profiles", array_null_terminated=true, array_length=false)]
		// Contains all built-in profiles with appropriate names
		public NamedProfile[] ProfileList;



		[CCode (cname="stringprep_nameprep", array_null_terminated=true, array_length=false)]
		public ProfileTable[] nameprep_profile;

		[CCode (cname="stringprep_saslprep", array_null_terminated=true, array_length=false)]
		public ProfileTable[] saslprep_profile;
		[CCode (cname="stringprep_plain", array_null_terminated=true, array_length=false)]
		public ProfileTable[] plain_profile;
		[CCode (cname="stringprep_trace", array_null_terminated=true, array_length=false)]
		public ProfileTable[] trace_profile;

		[CCode (cname="stringprep_kerberos5", array_null_terminated=true, array_length=false)]
		public ProfileTable[] kerberos5_profile;

		[CCode (cname="stringprep_xmpp_nodeprep_prohibit", array_null_terminated=true, array_length=false)]
		// Characters prohibited in the nodeprep profile
		public TableElement[] xmpp_nodeprep_prohibited;

		[CCode (cname="stringprep_xmpp_nodeprep", array_null_terminated=true, array_length=false)]
		public ProfileTable[] xmpp_nodeprep_profile;
		[CCode (cname="stringprep_xmpp_resourceprep", array_null_terminated=true, array_length=false)]
		public ProfileTable[] xmpp_resourceprep_profile;

		[CCode (cname="stringprep_iscsi", array_null_terminated=true, array_length=false)]
		public ProfileTable[] iscsi_profile;

		/* bound C functions - some of them not really usable in Vala */

		// @param requested		version of libidn your program requires, or null
		// @return				if the request is satisfied, lib's version; null otherwise
		public unowned string check_version (string? requested);

		[CCode (cname="stringprep_4i")]
		private ReturnCode _stringprep_4i ([CCode (array_length = false, array_null_terminated = false)] unichar[] ucs4,
		                                 ref size_t len, size_t max_len, ProfileFlags flags, [CCode (array_length = false)] ProfileTable[] profile);

	//	[CCode (cname="stringprep_4zi")]
	//	private ReturnCode _stringprep_4zi ([CCode (array_null_terminated = true)] unichar[] ucs4, size_t max_len
	//	                                    ProfileFlags flags, [CCode (array_length = false)] Profile[] profile);

		[CCode (cname="stringprep")]
		private ReturnCode _stringprep (uchar[] buffer, ProfileFlags flags, [CCode (array_length = false)] ProfileTable[] profile);

		[CCode (cname="stringprep_profile")]
		// @param profile has to be builtin profile; i.e. one of: "Nameprep", "Nodeprep", "Resourceprep", "plain", "trace", "SASLprep", "iSCSI"
		public ReturnCode stringprep_with_profile (string input, out string output, string profile, ProfileFlags flags = 0);

		[CCode (cname="stringprep_strerror")]
		public string return_code_to_string (ReturnCode code);

		/* interface methods */

		// The same as stringprep (), except it takes and outputs UCS4 string (unichar array)
		public ReturnCode stringprep_ucs4 (unichar[] input, out unichar[] output, ProfileTable[] profile, ProfileFlags flags = 0)
		{
			ReturnCode code = 0;
			unichar[] buffer;
			var buffer_length = input.length;
			size_t len;

			do {
				buffer = input;
				buffer.resize (buffer_length += 32);
				code = _stringprep_4i (buffer, ref len, buffer_length, flags, profile);
			} while (code == ReturnCode.TOO_SMALL_BUFFER);

			if (code == ReturnCode.OK) {
				buffer.resize ((int) len);
				output = buffer;
			}

			return code;
		}

		// Prepare the input string according to the stringprep profile
		public ReturnCode stringprep (string input, out string output, ProfileTable[] profile, ProfileFlags flags = 0)
		{
			ReturnCode code = 0;

			string tmp;
			unowned uchar[] buffer;
			var buffer_length = input.size ();

			do {
				tmp = input;
				buffer = (uchar[]) tmp;
				buffer.length = (int) input.size () + 1;
				buffer.resize ((int) (buffer_length += 32));
				code = _stringprep (buffer, flags, profile);
			} while (code == ReturnCode.TOO_SMALL_BUFFER);

			if (code == ReturnCode.OK) {
				output = tmp;
			}

			return code;
		}
	}

	[CCode (cheader_filename="punycode.h", cprefix="Punycode_")]
	namespace Punycode {

		[CCode (cname="Punycode_status", cprefix="PUNYCODE_")]
		public enum Status {
			SUCCESS, BAD_INPUT, BIG_OUTPUT, OVERFLOW
		}

		[CCode (cname="punycode_encode")]
		// output has to be preallocated
		private Status _encode ([CCode (array_length_pos = 0.9)] unichar[] input,
		                        [CCode (array_length = false)] uint8[]? case_flags,
		                        ref size_t output_length, [CCode (array_length = false)] uchar[] output);

		[CCode (cname="punycode_decode")]
		private Status _decode (size_t input_length, string input, ref size_t output_length,
		                        [CCode (array_length = false)] unichar[] output, [CCode (array_length = false)] uint8[]? case_flags);


		public Status encode (unichar[] input, uint8[]? case_flags, out string output)
		{
			Status status = 0;

			size_t output_length = input.length;
			uchar[] buffer;
			size_t real_length;

			do {
				real_length = (output_length += 32);
				buffer = new uchar [real_length];
				status = _encode (input, case_flags, ref real_length, buffer);
			} while (status == Status.BIG_OUTPUT);

			if (status == Status.SUCCESS) {
				buffer.resize ((int) real_length + 1);
				buffer [real_length] = 0x00;
				output = (string) buffer;
			}

			return status;
		}

		// @param input has to be ASCII encoded string
		public Status decode (string input, out unichar[] output, out uint8[] case_flags)
		{
			var copy = input;   // I have no idea why, but it sometimes crashes if this is not here
			var length = (size_t) input.size ();
			output = new unichar [length];
			case_flags = new uint8 [length];
			var status = _decode (length, input, ref length, output, case_flags);
			if (status == Status.SUCCESS) {
				output.resize ((int) length);
				case_flags.resize ((int) length);
			}
			else {
				output = null;
				case_flags = null;
			}
			return status;
		}
	}
}

using libidn;

public int main (string[] args)
{
	{
		//var str = "This is an UTF-8 encoded string. / Tohle je řetězec zakódovaný v UTF-8.";
		var str = "Tento řetězec je zakódovaný v UTF-8.";

		var ucs4 = utf8_to_ucs4_fast (str);

		string output;


		var status = Punycode.encode (ucs4, null, out output);

		stdout.puts (output);
		stdout.puts ("\n");

		uint8[] flags;
		status = Punycode.decode (output, out ucs4, out flags);

		output = ucs4_to_utf8 (ucs4);


		stdout.puts (output);
		stdout.puts ("\n");

	}

	{
		string output;
		var str = "MiXeD-CaSe";
		//string output;
		var code = Stringprep.stringprep (str, out output, Stringprep.xmpp_nodeprep_profile);
		stdout.printf ("Input: %s\n", str);
		stdout.printf ("Return code: %d\n", code);
		stdout.printf ("Output length %ld\n", output.length);
		stdout.printf ("Output: %s\n", output);

		str = "prohibited character";
		code = Stringprep.stringprep (str, out output, Stringprep.xmpp_nodeprep_profile);
		stdout.printf ("Input: %s\n", str);
		stdout.printf ("Return code: %d\n", code);
		stdout.printf ("Output: %s\n", output);

	}

	return 0;
}
_______________________________________________
Vala-list mailing list
Vala-list@gnome.org
http://mail.gnome.org/mailman/listinfo/vala-list

Reply via email to