[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Implementing string items support in deva and the libc
From: |
Matthieu Lemerre |
Subject: |
Implementing string items support in deva and the libc |
Date: |
Wed, 09 Feb 2005 02:18:06 +0100 |
User-agent: |
Gnus/5.110003 (No Gnus v0.3) Emacs/21.3 (gnu/linux) |
Here's three patches, whose purpose is to implement string item
support in deva, and making use of it in the C library.
I had to write two extensions to libl4 : one of them enables to read
the size of a simple string item, and the second to get a string item
from a message (this one isn't necessary, but as we discussed with
marcus, getting compound string items of arbitrary length isn't that
trivial, so using this function marks a distinction between getting a
simple string item and a compound string item. )
I made use of the fact that the message was re-written by
libhurd-cap-server (and all string items become simple string items)
to simplify things a bit.
PS: I didn't see that cvs -l produced a patch that can't be apply at
the root of the checkout... Sorry Marcus for this inconvenience.
2005-02-08 Matthieu Lemerre <address@hidden>
* deva.c (main): Now accepts one string item.
* device-serial.c (serial_irq_handler): Sets fifo_full, enable TX
interrupts only once.
* device.h (struct device): Modified io_read signature.
(IO_READ_MAX): New constant.
(struct device): Added fifo_full field for serial.
* device.h (struct device): Modified io_write signature.
* device-console.c (console_io_write): Now takes a string as an
argument.
(console_io_read): Likewise.
* device-serial.c (serial_io_write): Likewise.
(serial_io_write): Likewise.
Index: deva.c
===================================================================
RCS file: /cvsroot/hurd/hurd-l4/deva/deva.c,v
retrieving revision 1.6
diff -a -u -p -r1.6 deva.c
--- deva.c 3 Feb 2005 22:52:52 -0000 1.6
+++ deva.c 8 Feb 2005 23:06:22 -0000
@@ -266,7 +266,8 @@ main (int argc, char *argv[])
if (err)
panic ("device_class_init: %i\n", err);
- err = hurd_cap_bucket_create (&bucket);
+ size_t string_item_sizes[2] = {4096, 0};
+ err = hurd_cap_bucket_create (&bucket, string_item_sizes);
if (err)
panic ("bucket_create: %i\n", err);
Index: device-console.c
===================================================================
RCS file: /cvsroot/hurd/hurd-l4/deva/device-console.c,v
retrieving revision 1.2
diff -a -u -p -r1.2 device-console.c
--- device-console.c 3 Feb 2005 14:45:42 -0000 1.2
+++ device-console.c 8 Feb 2005 23:06:22 -0000
@@ -140,32 +140,53 @@ console_init (device_t *dev)
}
-static error_t
-console_io_read (device_t *dev, int *chr)
+static size_t
+console_io_read (device_t *dev, unsigned char *string, size_t count)
{
- unsigned char b = '\x00';
+ size_t bytes_copied = 0;
+ int ret = 0;
pthread_mutex_lock (&dev->console.lock);
- while (!dev->console.input_len)
- pthread_cond_wait (&dev->console.cond, &dev->console.lock);
- b = dev->console.input[0];
- dev->console.input_len--;
- memmove (&dev->console.input[0], &dev->console.input[1],
- dev->console.input_len);
+ while (!ret && bytes_copied < count)
+ {
+ while (!dev->console.input_len)
+ {
+ pthread_cond_wait (&dev->console.cond, &dev->console.lock);
+ }
+ size_t size_max = ((count - bytes_copied) < dev->console.input_len
+ ? (count - bytes_copied) : dev->console.input_len);
+ int i;
+ for (i = 0; i < size_max; i++)
+ {
+ string[bytes_copied++] = dev->console.input[i];
+ dev->console.input_len --;
+ if (dev->console.input[i] == '\n')
+ {
+ ret = 1;
+ break;
+ }
+ }
+
+ if (dev->console.input_len)
+ {
+ memmove (&dev->console.input[0], &dev->console.input[size_max],
+ dev->console.input_len);
+ }
+ }
pthread_mutex_unlock (&dev->console.lock);
- *chr = b;
-
- return 0;
+ return bytes_copied;
}
-
-static error_t
-console_io_write (device_t *dev, int chr)
+static size_t
+console_io_write (device_t *dev, unsigned char *string, size_t count)
{
- putchar ((unsigned char) chr);
-
- return 0;
+ int i;
+ for (i = 0; i < count; i++)
+ {
+ putchar (string[i]);
+ }
+ return count;
}
Index: device-serial.c
===================================================================
RCS file: /cvsroot/hurd/hurd-l4/deva/device-serial.c,v
retrieving revision 1.2
diff -a -u -p -r1.2 device-serial.c
--- device-serial.c 3 Feb 2005 21:56:26 -0000 1.2
+++ device-serial.c 8 Feb 2005 23:06:23 -0000
@@ -118,6 +118,9 @@ serial_irq_handler (void *_serial)
if (result)
panic ("setting serial irq pager failed: %i", result);
+ /* Enable interrupts for TX. */
+ outb (0x3, UART_IER);
+
#define l4_reply_receive(tid) \
({ l4_thread_id_t dummy; \
l4_ipc (tid, tid, l4_timeouts (L4_ZERO_TIME, L4_NEVER), &dummy); })
@@ -180,31 +183,28 @@ serial_irq_handler (void *_serial)
/* Update the status. */
b = inb (UART_LSR);
}
-
+
+ dev->serial.fifo_not_full = (b & (1 << 5));
+
if (tx != 0)
{
- if (len == SERIAL_MAX && dev->serial.output_wait)
- {
- /* There is now some more space. */
- pthread_cond_broadcast (&dev->serial.cond);
- dev->serial.output_wait = 0;
- }
-
- len -= tx;
- dev->serial.output_len = len;
-
- if (len == 0)
- /* Disable interrupts for TX. */
- outb (0x1, UART_IER);
-
- else
- memmove (&dev->serial.output[0],
- &dev->serial.output[tx], len);
+ if (len == SERIAL_MAX && dev->serial.output_wait)
+ {
+ /* There is now some more space. */
+ pthread_cond_broadcast (&dev->serial.cond);
+ dev->serial.output_wait = 0;
+ }
+
+ len -= tx;
+ dev->serial.output_len = len;
+
+ memmove (&dev->serial.output[0],
+ &dev->serial.output[tx], len);
}
- }
+ }
pthread_mutex_unlock (&dev->serial.lock);
}
-
+
l4_load_mr (0, 0);
l4_reply_receive (irq);
}
@@ -243,82 +243,99 @@ serial_init (device_t *dev)
}
-static error_t
-serial_io_read (device_t *dev, int *chr)
+static size_t
+serial_io_read (device_t *dev, unsigned char *string, size_t count)
{
+ size_t bytes_copied = 0;
+ int ret = 0;
+
pthread_mutex_lock (&dev->serial.lock);
- while (!dev->serial.input_len)
+ while (!ret && bytes_copied < count)
{
- dev->serial.input_wait = 1;
- pthread_cond_wait (&dev->serial.cond, &dev->serial.lock);
+ while (!dev->serial.input_len)
+ {
+ dev->serial.input_wait = 1;
+ pthread_cond_wait (&dev->serial.cond, &dev->serial.lock);
+ }
+ size_t size_max = ((count - bytes_copied) < dev->serial.input_len
+ ? (count - bytes_copied) : dev->serial.input_len);
+ int i;
+ for (i = 0; i < size_max; i++)
+ {
+ string[bytes_copied++] = dev->serial.input[i];
+ dev->serial.input_len --;
+ if (dev->serial.input[i] == '\n')
+ {
+ ret = 1;
+ break;
+ }
+ }
+
+ if (dev->serial.input_len)
+ {
+ memmove (&dev->serial.input[0], &dev->serial.input[size_max],
+ dev->serial.input_len);
+ }
}
- *chr = dev->serial.input[0];
- dev->serial.input_len--;
- memmove (&dev->serial.input[0], &dev->serial.input[1],
- dev->serial.input_len);
pthread_mutex_unlock (&dev->serial.lock);
-
- return 0;
+ return bytes_copied;
}
-static error_t
-serial_io_write (device_t *dev, int chr)
+static size_t
+serial_io_write (device_t *dev, unsigned char* buffer, size_t buffer_len)
{
- int old_len;
+ size_t bytes_copied = 0;
pthread_mutex_lock (&dev->serial.lock);
- while (dev->serial.output_len == SERIAL_MAX)
+ while (bytes_copied < buffer_len)
{
- dev->serial.output_wait = 1;
- pthread_cond_wait (&dev->serial.cond, &dev->serial.lock);
- }
-
- old_len = dev->serial.output_len;
-
- if (old_len == 0)
- {
- /* The output queue was empty, this means that the TX interrupts
- are disabled (the irq handler thread disables them after
- sending the last character. We have to refill the buffer and
- at the same time re-activate the transmit interrupts. For
- this, we need to enable the IRQ and write one character to
- get things rolling. This makes only sense if we are sending
- at least two characters. Another approach would be to cancel
- the pending IPC operation in the IRQ thread and let it
- reactivate the IRQ in this case. */
-
- /* Right now, only one character is sent always. No need for
- IRQs. */
-#if 0
- if (buffer_len > 1)
+ while (dev->serial.output_len == SERIAL_MAX)
{
- memcpy (&dev->serial.output[dev->serial.output_len],
- &buffer[1], buffer_len - 1);
+ dev->serial.output_wait = 1;
+ pthread_cond_wait (&dev->serial.cond, &dev->serial.lock);
+ }
- /* Enable interrupts for TX. */
- outb (0x3, UART_IER);
+ int old_len = dev->serial.output_len;
+ size_t output_room = SERIAL_MAX - old_len;
+ if (old_len == 0 && dev->serial.fifo_not_full)
+ {
+ /* The output queue is empty, so we have to refill the
+ buffer and trigger a transmit interrupts, so that the
+ interrupt thread can handle the transmission of the
+ message. For this, we need to write one character to get
+ things rolling. We can do this since the fifo is not
+ full. If the fifo is full, we already know that
+ interrupts will be triggered. */
+ size_t bytes_to_copy = ((buffer_len - bytes_copied - 1) < output_room
+ ? (buffer_len - bytes_copied - 1) :
output_room);
+ memcpy (&dev->serial.output[0],
+ &buffer[bytes_copied + 1], bytes_to_copy);
+
+ dev->serial.output_len = bytes_to_copy;
+
/* Send first character to make sure the next TX IRQ comes
soon. */
- outb (buffer[0], UART_DR);
+ outb (buffer[bytes_copied], UART_DR);
+
+ bytes_copied += bytes_to_copy + 1;
}
else
-#endif
{
- /* Only one character, enabling IRQs makes no sense, as the
- next IRQ would only come after sending this first
- character. */
- outb (chr, UART_DR);
+ /* Appending the buffer to the serial output buffer */
+ size_t bytes_to_copy = ((buffer_len - bytes_copied) < output_room
+ ? (buffer_len - bytes_copied) : output_room);
+
+ memcpy (&dev->serial.output[dev->serial.output_len],
+ &buffer[bytes_copied], bytes_to_copy);
+ dev->serial.output_len += bytes_to_copy;
+ bytes_copied += bytes_to_copy;
}
}
- else
- {
- dev->serial.output[++dev->serial.output_len] = chr;
- }
pthread_mutex_unlock (&dev->serial.lock);
- return 0;
+ return bytes_copied;
}
@@ -334,6 +351,7 @@ device_serial_init (device_t *dev)
dev->serial.input_wait = 0;
dev->serial.output_len = 0;
dev->serial.output_wait = 0;
+ dev->serial.fifo_not_full = 0;
/* We know this is only called once, so what the hell. */
serial_init (dev);
Index: device.c
===================================================================
RCS file: /cvsroot/hurd/hurd-l4/deva/device.c,v
retrieving revision 1.2
diff -a -u -p -r1.2 device.c
--- device.c 3 Feb 2005 14:23:20 -0000 1.2
+++ device.c 8 Feb 2005 23:06:23 -0000
@@ -44,17 +44,24 @@ device_reinit (hurd_cap_class_t cap_clas
static error_t
device_io_read (hurd_cap_rpc_context_t ctx)
{
- error_t err;
device_t *dev = hurd_cap_obj_to_user (device_t *, ctx->obj);
- int chr;
- err = (*dev->io_read) (dev, &chr);
- if (err)
- return err;
+ unsigned char buffer[IO_READ_MAX];
+
+ size_t bytes_read;
+ size_t nbytes = l4_msg_word (ctx->msg, 1);
+ size_t max_len = (nbytes < IO_READ_MAX ? nbytes : IO_READ_MAX);
+
+ bytes_read = (*dev->io_read) (dev, buffer, max_len);
/* Prepare reply message. */
l4_msg_clear (ctx->msg);
- l4_msg_append_word (ctx->msg, (l4_word_t) chr);
+ l4_msg_append_word (ctx->msg, (l4_word_t) bytes_read);
+ if (bytes_read > 0)
+ {
+ l4_string_item_t string_item = l4_string_item (bytes_read, buffer);
+ l4_msg_append_simple_string_item (ctx->msg, string_item);
+ }
return 0;
}
@@ -64,11 +71,40 @@ static error_t
device_io_write (hurd_cap_rpc_context_t ctx)
{
device_t *dev = hurd_cap_obj_to_user (device_t *, ctx->obj);
- int chr;
- chr = (int) l4_msg_word (ctx->msg, 1);
+ if ((l4_untyped_words (l4_msg_msg_tag (ctx->msg)) == 2)
+ && (l4_typed_words (l4_msg_msg_tag (ctx->msg)) > 0))
+ {
+ l4_word_t wanted_length = l4_msg_word (ctx->msg, 1);
+ l4_string_item_t string_item;
+ l4_msg_get_simple_string_item (ctx->msg, 0, &string_item);
+
+ /* Useless test for now, since we do not accept other items */
+ if (l4_is_string_item (&string_item))
+ {
+ int string_item_length = l4_simple_string_item_size (&string_item);
+
+ l4_word_t buffer_address;
+ l4_store_br (2, &buffer_address);
+
+ unsigned char *string_buffer = (unsigned char *) buffer_address;
+
+ size_t length = (string_item_length < wanted_length
+ ? string_item_length : wanted_length);
+
+ size_t bytes_written = (*dev->io_write) (dev, string_buffer, length);
+
+ /* Prepare reply message */
+ l4_msg_clear (ctx->msg);
+ l4_msg_append_word (ctx->msg, (l4_word_t) bytes_written);
+
+ return 0;
+ }
+ }
+
+ /* Bad message */
+ return (-1);
- return (*dev->io_write) (dev, chr);
}
Index: device.h
===================================================================
RCS file: /cvsroot/hurd/hurd-l4/deva/device.h,v
retrieving revision 1.2
diff -a -u -p -r1.2 device.h
--- device.h 3 Feb 2005 14:23:19 -0000 1.2
+++ device.h 8 Feb 2005 23:06:23 -0000
@@ -29,11 +29,11 @@ typedef struct device device_t;
struct device
{
- /* Read a single character from DEV and return it in CHR. */
- error_t (*io_read) (device_t *dev, int *chr);
+ /* Read up to COUNT characters from DEV and put it in BUFFER */
+ size_t (*io_read) (device_t *dev, unsigned char *buffer, size_t count);
- /* Write the single character CHR to DEV. */
- error_t (*io_write) (device_t *dev, int chr);
+ /* Write COUNT characters of BUFFER to DEV */
+ size_t (*io_write) (device_t *dev, unsigned char *buffer, size_t count);
/* Private data area. */
union
@@ -60,8 +60,10 @@ struct device
unsigned char output[SERIAL_MAX];
unsigned int output_len;
unsigned int output_wait;
+ unsigned int fifo_not_full;
} serial;
};
+#define IO_READ_MAX 256
};
;
2005-02-04 Matthieu Lemerre <address@hidden>
* l4/gnu/ipc.h (l4_msg_get_simple_string_item): New function.
(l4_simple_string_item_size): New function.
* l4/ipc.h (_L4_msg_get_simple_string_item): New function.
(_L4_simple_string_item_size): New function.
diff -aurp hurd-l4-bak/libl4/l4/gnu/ipc.h hurd-l4/libl4/l4/gnu/ipc.h
--- hurd-l4-bak/libl4/l4/gnu/ipc.h 2005-02-04 00:13:42.000000000 +0100
+++ hurd-l4/libl4/l4/gnu/ipc.h 2005-02-04 00:47:43.000000000 +0100
@@ -254,6 +254,14 @@ l4_substring (l4_string_item_t *string_i
}
+static inline int
+_L4_attribute_always_inline
+l4_simple_string_item_size (l4_string_item_t *string_item)
+{
+ return _L4_simple_string_item_size (string_item);
+}
+
+
static inline l4_string_item_t *
_L4_attribute_always_inline
l4_add_substring_address_to (l4_string_item_t *string_item, void *source)
@@ -633,6 +641,15 @@ l4_msg_get_string_item (l4_msg_t msg, l4
}
+static inline l4_word_t
+_L4_attribute_always_inline
+l4_msg_get_simple_string_item (l4_msg_t msg, l4_word_t nr,
+ l4_string_item_t *string_item)
+{
+ return _L4_msg_get_simple_string_item (msg, nr, string_item);
+}
+
+
/* IPC interface. */
Only in hurd-l4/libl4/l4/gnu: semantic.cache
Only in hurd-l4/libl4/l4/gnu: semantic.cache~
diff -aurp hurd-l4-bak/libl4/l4/ipc.h hurd-l4/libl4/l4/ipc.h
--- hurd-l4-bak/libl4/l4/ipc.h 2005-02-04 00:13:42.000000000 +0100
+++ hurd-l4/libl4/l4/ipc.h 2005-02-04 01:06:54.000000000 +0100
@@ -373,6 +373,17 @@ _L4_substring (_L4_string_item_t *string
}
+/* Not part of the L4 official ABI.
+ Gets the size of a simple string item */
+static inline int
+_L4_attribute_always_inline
+_L4_simple_string_item_size (_L4_string_item_t *string_item)
+{
+ __L4_string_item_t *_string_item = (__L4_string_item_t *) string_item;
+ return (int) _string_item->length;
+}
+
+
/* Append the string described by string item SOURCE to the string
described by string item STRING_ITEM. */
static inline _L4_string_item_t *
@@ -1188,6 +1199,20 @@ _L4_msg_get_string_item (_L4_msg_t msg,
return ((_L4_word_t *) _string_item) - ((_L4_word_t *) string_item);
}
+/* Not in L4 official ABI */
+static inline _L4_word_t
+_L4_attribute_always_inline
+_L4_msg_get_simple_string_item (_L4_msg_t msg, _L4_word_t nr,
+ _L4_string_item_t *string_item)
+{
+ __L4_msg_t *_msg = (__L4_msg_t *) msg;
+ _L4_word_t pos = 1 + _msg->tag.untyped + nr;
+ __L4_string_item_t *_string_item = (__L4_string_item_t *) string_item;
+ _string_item->mr[0] = msg[pos];
+ _string_item->mr[1] = msg[pos+1];
+ return 2;
+}
+
/* l4_ipc convenience interface. */
2005-02-05 Matthieu Lemerre <address@hidden>
* hurd-l4/sysdeps/l4/hurd/read.c (deva_read): New function.
(__libc_read): Now use deva_read.
(deva_getchar): Removed this function.
* hurd-l4/sysdeps/l4/hurd/write.c (deva_write): New function.
(__libc_read): Now use deva_write.
(deva_putchar): Removed this function.
Index: sysdeps/l4/hurd/read.c
===================================================================
RCS file: /cvsroot/hurd/hurd-l4/libc/hurd-l4/sysdeps/l4/hurd/read.c,v
retrieving revision 1.1
diff -a -u -p -r1.1 read.c
--- sysdeps/l4/hurd/read.c 30 Jan 2005 19:48:10 -0000 1.1
+++ sysdeps/l4/hurd/read.c 8 Feb 2005 22:22:23 -0000
@@ -32,29 +32,35 @@
#include <hurd/types.h>
#include <hurd/startup.h>
-/* Echo the character CHR on the manager console. */
-static inline int
+
+
+static inline size_t
__attribute__((always_inline))
-deva_getchar (void)
+deva_read (char *buffer, size_t nbytes)
{
- l4_word_t chr;
+ l4_word_t bytes_read;
l4_msg_tag_t tag;
extern struct hurd_startup_data *_hurd_startup_data;
hurd_cap_handle_t deva_cap_handle
= _hurd_startup_data->deva_console.cap_handle;
l4_thread_id_t deva_thread_id = _hurd_startup_data->deva_console.server;
- // l4_accept (L4_UNTYPED_WORDS_ACCEPTOR);
-
- tag = l4_niltag;
- l4_msg_tag_set_label (&tag, 768 /* DEVA_IO_READ */);
- l4_msg_tag_set_untyped_words (&tag, 1);
- l4_set_msg_tag (tag);
- l4_load_mr (1, (l4_word_t) deva_cap_handle);
+ l4_string_item_t string_item = l4_string_item (nbytes, buffer);
+ l4_msg_buffer_t msg_buffer;
+ l4_msg_buffer_clear (msg_buffer);
+ l4_msg_buffer_append_simple_rcv_string (msg_buffer, string_item);
+ l4_accept_strings (L4_STRING_ITEMS_ACCEPTOR, msg_buffer);
+
+ l4_msg_t msg;
+ l4_msg_clear (msg);
+ l4_set_msg_label (msg, 768 /* DEVA_IO_READ */);
+ l4_msg_append_word (msg, deva_cap_handle);
+ l4_msg_append_word (msg, nbytes);
+ l4_msg_load (msg);
tag = l4_call (deva_thread_id);
- l4_store_mr (1, &chr);
- return (int) chr;
+ l4_store_mr (1, &bytes_read);
+ return (size_t) bytes_read;
}
@@ -85,18 +91,10 @@ __libc_read (int fd, void *buf, size_t n
return -1;
}
- res = 0;
- while (res < nbytes)
- {
- int chr = deva_getchar ();
-
- buffer[res] = (char) chr;
- res++;
- if (chr == '\n')
- break;
- }
-
+ res = deva_read (buffer, nbytes);
+
return res;
+
}
libc_hidden_def (__libc_read)
stub_warning (read)
Index: sysdeps/l4/hurd/write.c
===================================================================
RCS file: /cvsroot/hurd/hurd-l4/libc/hurd-l4/sysdeps/l4/hurd/write.c,v
retrieving revision 1.1
diff -a -u -p -r1.1 write.c
--- sysdeps/l4/hurd/write.c 30 Jan 2005 19:48:10 -0000 1.1
+++ sysdeps/l4/hurd/write.c 8 Feb 2005 22:22:23 -0000
@@ -33,10 +33,10 @@
#include <hurd/types.h>
#include <hurd/startup.h>
-/* Echo the character CHR on the manager console. */
-static inline void
+/* Prints NBYTES of the buffer BUFFER on the manager console. */
+static inline size_t
__attribute__((always_inline))
-deva_putchar (int chr)
+deva_write (const void* buf, size_t nbytes)
{
l4_msg_tag_t tag;
extern struct hurd_startup_data *_hurd_startup_data;
@@ -45,16 +45,25 @@ deva_putchar (int chr)
l4_thread_id_t deva_thread_id = _hurd_startup_data->deva_console.server;
// l4_accept (L4_UNTYPED_WORDS_ACCEPTOR);
-
- tag = l4_niltag;
- l4_msg_tag_set_label (&tag, 769 /* DEVA_IO_WRITE */);
- l4_msg_tag_set_untyped_words (&tag, 2);
- l4_set_msg_tag (tag);
- l4_load_mr (1, (l4_word_t) deva_cap_handle);
- l4_load_mr (2, (l4_word_t) chr);
+
+ l4_msg_t msg;
+ l4_msg_clear (msg);
+ l4_set_msg_label (msg, 769 /* DEVA_IO_WRITE */);
+ l4_msg_append_word (msg, deva_cap_handle);
+ l4_msg_append_word (msg, nbytes);
+ l4_string_item_t string_item = l4_string_item (nbytes, (char *) buf);
+ l4_msg_append_simple_string_item (msg, string_item);
+
+ l4_msg_load (msg);
tag = l4_call (deva_thread_id);
+
+ size_t bytes_written;
+
+ l4_store_mr (1, &bytes_written);
+ return (size_t) bytes_written;
}
+
/* Write NBYTES of BUF to FD. Return the number written, or -1. */
ssize_t
@@ -82,10 +91,8 @@ __libc_write (int fd, const void *buf, s
return -1;
}
- res = nbytes;
- while (nbytes--)
- deva_putchar (*(((char *)buf)++));
-
+ res = deva_write (buf, nbytes);
+
return res;
}
libc_hidden_def (__libc_write)
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Implementing string items support in deva and the libc,
Matthieu Lemerre <=