Redesign of the server communication protocol to allow arbitrary sized
data to be exchanged.
Split request and reply structures to make backwards compatibility
easier.
Moved many console functions to dlls/kernel, added code page support,
changed a few requests to behave properly with the new protocol.

diff --git a/server/request.c b/server/request.c
index ba420a0..9f2b8ad 100644
--- a/server/request.c
+++ b/server/request.c
@@ -142,31 +142,105 @@
     exit(1);
 }
 
-/* call a request handler */
-static inline void call_req_handler( struct thread *thread, union generic_request *request )
+/* allocate the reply data */
+void *set_reply_data_size( size_t size )
 {
-    enum request req = request->header.req;
+    assert( size <= get_reply_max_size() );
+    if (size && !(current->reply_data = mem_alloc( size ))) size = 0;
+    current->reply_size = size;
+    return current->reply_data;
+}
 
-    current = thread;
-    clear_error();
+/* write the remaining part of the reply */
+void write_reply( struct thread *thread )
+{
+    int ret;
 
-    if (debug_level) trace_request( thread, request );
-
-    if (request->header.var_size)
+    if ((ret = write( thread->reply_fd,
+                      (char *)thread->reply_data + thread->reply_size - thread->reply_towrite,
+                      thread->reply_towrite )) >= 0)
     {
-        if ((unsigned int)request->header.var_offset +
-                          request->header.var_size > MAX_REQUEST_LENGTH)
+        if (!(thread->reply_towrite -= ret))
         {
-            fatal_protocol_error( current, "bad request offset/size %d/%d\n",
-                                  request->header.var_offset, request->header.var_size );
+            free( thread->reply_data );
+            thread->reply_data = NULL;
+            /* sent everything, can go back to waiting for requests */
+            change_select_fd( &thread->obj, thread->request_fd, POLLIN );
+        }
+        return;
+    }
+    if (errno == EPIPE)
+        kill_thread( thread, 0 );  /* normal death */
+    else if (errno != EWOULDBLOCK && errno != EAGAIN)
+        fatal_protocol_perror( thread, "reply write" );
+}
+
+/* send a reply to the current thread */
+static void send_reply( union generic_reply *reply )
+{
+    int ret;
+
+    if (!current->reply_size)
+    {
+        if ((ret = write( current->reply_fd, reply, sizeof(*reply) )) != sizeof(*reply)) goto error;
+    }
+    else
+    {
+        struct iovec vec[2];
+
+        vec[0].iov_base = reply;
+        vec[0].iov_len  = sizeof(*reply);
+        vec[1].iov_base = current->reply_data;
+        vec[1].iov_len  = current->reply_size;
+
+        if ((ret = writev( current->reply_fd, vec, 2 )) < sizeof(*reply)) goto error;
+
+        if ((current->reply_towrite = current->reply_size - (ret - sizeof(*reply))))
+        {
+            /* couldn't write it all, wait for POLLOUT */
+            change_select_fd( &current->obj, current->reply_fd, POLLOUT );
             return;
         }
     }
+    if (current->reply_data)
+    {
+        free( current->reply_data );
+        current->reply_data = NULL;
+    }
+    return;
+
+ error:
+    if (ret >= 0)
+        fatal_protocol_error( current, "partial write %d\n", ret );
+    else if (errno == EPIPE)
+        kill_thread( current, 0 );  /* normal death */
+    else
+        fatal_protocol_perror( current, "reply write" );
+}
+
+/* call a request handler */
+static void call_req_handler( struct thread *thread )
+{
+    union generic_reply reply;
+    enum request req = thread->req.request_header.req;
+
+    current = thread;
+    current->reply_size = 0;
+    clear_error();
+    memset( &reply, 0, sizeof(reply) );
+
+    if (debug_level) trace_request();
 
     if (req < REQ_NB_REQUESTS)
     {
-        req_handlers[req]( request );
-        if (current) send_reply( current, request );
+        req_handlers[req]( &current->req, &reply );
+        if (current)
+        {
+            reply.reply_header.error = current->error;
+            reply.reply_header.reply_size = current->reply_size;
+            if (debug_level) trace_reply( req, &reply );
+            send_reply( &reply );
+        }
         current = NULL;
         return;
     }
@@ -176,14 +250,39 @@
 /* read a request from a thread */
 void read_request( struct thread *thread )
 {
-    union generic_request req;
     int ret;
 
-    if ((ret = read( thread->obj.fd, &req, sizeof(req) )) == sizeof(req))
+    if (!thread->req_toread)  /* no pending request */
     {
-        call_req_handler( thread, &req );
-        return;
+        if ((ret = read( thread->obj.fd, &thread->req,
+                         sizeof(thread->req) )) != sizeof(thread->req)) goto error;
+        if (!(thread->req_toread = thread->req.request_header.request_size))
+        {
+            /* no data, handle request at once */
+            call_req_handler( thread );
+            return;
+        }
+        if (!(thread->req_data = malloc( thread->req_toread )))
+            fatal_protocol_error( thread, "no memory for %d bytes request\n", thread->req_toread );
     }
+
+    /* read the variable sized data */
+    for (;;)
+    {
+        ret = read( thread->obj.fd, ((char *)thread->req_data +
+                                     thread->req.request_header.request_size - thread->req_toread),
+                    thread->req_toread );
+        if (ret <= 0) break;
+        if (!(thread->req_toread -= ret))
+        {
+            call_req_handler( thread );
+            free( thread->req_data );
+            thread->req_data = NULL;
+            return;
+        }
+    }
+
+error:
     if (!ret)  /* closed pipe */
         kill_thread( thread, 0 );
     else if (ret > 0)
@@ -192,26 +291,6 @@
         fatal_protocol_perror( thread, "read" );
 }
 
-/* send a reply to a thread */
-void send_reply( struct thread *thread, union generic_request *request )
-{
-    int ret;
-
-    if (debug_level) trace_reply( thread, request );
-
-    request->header.error = thread->error;
-
-    if ((ret = write( thread->reply_fd, request, sizeof(*request) )) != sizeof(*request))
-    {
-        if (ret >= 0)
-            fatal_protocol_error( thread, "partial write %d\n", ret );
-        else if (errno == EPIPE)
-            kill_thread( thread, 0 );  /* normal death */
-        else
-            fatal_protocol_perror( thread, "reply write" );
-    }
-}
-
 /* receive a file descriptor on the process socket */
 int receive_fd( struct process *process )
 {
@@ -439,6 +518,7 @@
 
     /* make sure no request is larger than the maximum size */
     assert( sizeof(union generic_request) == sizeof(struct request_max_size) );
+    assert( sizeof(union generic_reply) == sizeof(struct request_max_size) );
 
     create_server_dir();
     if ((fd = socket( AF_UNIX, SOCK_STREAM, 0 )) == -1) fatal_perror( "socket" );