[PATCH 02 of 55 RFC c-hglib:level1] c-hglib: all level 0 functions, provided for context

Iulian Stana julian.stana at gmail.com
Fri Sep 13 19:35:14 CDT 2013


# HG changeset patch
# User Iulian Stana <julian.stana at gmail.com>
# Date 1379107586 -10800
#      Sat Sep 14 00:26:26 2013 +0300
# Node ID 5277ca53b1457bfd051f2493f1bfde149cedc57e
# Parent  02484cf0647286223358d7ec6df194d704f02c47
c-hglib: all level 0 functions, provided for context

I am putting in this commit the level 0 function, that I already showed you in
other patchbomb, with the purpose to give you the entire code and to give you
the possibility to build the level 1 from this patchbomb.

diff --git a/client.c b/client.c
new file mode 100644
--- /dev/null
+++ b/client.c
@@ -0,0 +1,313 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "client.h"
+#include "utils.h"
+
+#define HGPATH "hg"
+
+/* Release data from handle pointers. */
+void free_data(hg_handle *handle){
+	if(handle->out_data){
+		free(handle->out_data);
+		handle->out_data = NULL;
+		handle->out_data_size = 0;
+	}
+}
+
+/*
+ * The function will read the header from the command server and will save it to
+ * */
+hg_header *hg_read_header(hg_handle *handle)
+{
+	uint32_t length;
+	char ch_char;
+
+	if(!handle) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if(handle->bytes_on_pipe == 0){
+		if(read(handle->p_read, &ch_char, 1) < 0){
+			return NULL;
+		}
+		if(read(handle->p_read, &length, sizeof(uint32_t)) < 0){
+			return NULL;
+		}
+		handle->header->length = swap_uint32(length);
+		handle->bytes_on_pipe = handle->header->length;
+		switch(ch_char){
+			case 'o':
+				handle->header->channel = o;
+				break;
+			case 'e':
+				handle->header->channel = e;
+				break;
+			case 'r':
+				handle->header->channel = r;
+				break;
+			case 'I':
+				handle->header->channel = I;
+				break;
+			case 'L':
+				handle->header->channel = L;
+				break;
+			default:
+				handle->header->channel = wrong_channel;
+				break;
+		}
+	}
+	return handle->header;
+}
+
+/**
+ * \brief Reading the hello msg from cmd server.
+ *
+ * After the connection, the command server sends a hello message.
+ * The message contains the command server capabilities and the messages
+ * encoding.
+ * \param handle The handle of the connection, wherewith I want to communicate
+ * \retval  0 if succesfull 
+ * \retval -1 to indicate an error, with errno set appropriately
+ * 
+ * errno can be:
+ *       - EINVAL  - Invalid argument (handle it's set to a null pointer)
+ *       - read(2) command errors
+ * */
+int read_hello(hg_handle *handle)
+{
+	char *buffer;
+	hg_header *ch;
+	int nc;
+
+	if(!handle) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	ch = hg_read_header(handle);
+
+	if(ch->length == 0){
+		return -1;
+	}
+
+	buffer = malloc(ch->length + 1);
+
+	if(nc = read(handle->p_read, buffer, ch->length), nc < 0){
+		free(buffer);
+		return -1;
+	}
+	buffer[ch->length] = '\0';
+	handle->bytes_on_pipe -= nc;
+
+	free(buffer);
+
+	return 0;
+}
+
+/*
+ * Open the connection with the mercurial command server.
+ * */
+hg_handle *hg_open(const char *path, char *encoding)
+{
+	hg_handle *handle = malloc(sizeof(hg_handle));
+	handle->header = malloc(sizeof(hg_header));
+	char command[100];
+	int wpipe[2];
+	int rpipe[2];
+	int c_write;
+	int c_read;
+
+	sprintf(command,
+		"%s serve --cmdserver pipe --config ui.interactive=True",
+		HGPATH);
+
+	if (path)
+		sprintf(command, "%s -R %s", command, path);
+
+	if (pipe(wpipe) < 0 || pipe(rpipe) < 0) {
+		return NULL;
+	}
+	handle->p_read = rpipe[0];
+	c_write = rpipe[1];
+	c_read = wpipe[0];
+	handle->p_write = wpipe[1];
+	handle->protect = 0;
+	handle->bytes_on_pipe = 0;
+
+	if ((handle->childpid = fork()) < 0) {
+		return NULL;
+
+	} else if (handle->childpid == 0) {	/* child */
+		close(handle->p_write);
+		close(handle->p_read);
+		if(dup2(c_read, STDIN_FILENO) < 0){
+			return NULL;
+		}
+		close(c_read);
+		if(dup2(c_write, STDOUT_FILENO) < 0){
+			return NULL;
+		}
+		close(c_write);
+		if(execl("/bin/sh", "sh", "-c", command, NULL) < 0){
+			return NULL;
+		}
+
+	} else {	/* parent */
+		close(c_read);
+		close(c_write);
+	}
+
+	if(read_hello(handle) < 0)
+		return NULL;
+
+	handle->out_data = NULL;
+	handle->out_data_size = 0;
+	return handle;
+}
+
+/*
+ * Close the connection for the given handle.
+ * */
+int hg_close(hg_handle **handle)
+{
+	if(!(*handle)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	close((*handle)->p_read);
+	close((*handle)->p_write);
+
+	free((*handle)->header);
+	free_data(*handle);
+	free(*handle);
+	*handle = NULL;
+
+	return 0;
+}
+
+/*
+ * Sending a command to the mercurial command server, through the given handle.
+ * */
+int hg_rawcommand(hg_handle *handle, char *const command[])
+{
+	if(!handle) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if(handle->protect){
+		errno = EPERM;
+		return -1;
+	}
+
+	char runcommand[] = "runcommand\n";
+	int cmd_size = 0;
+	char *cmd_send = cmd_prepare(command, &cmd_size);
+	uint32_t big_endian_size = swap_uint32(cmd_size);
+
+	if(write(handle->p_write, runcommand, strlen(runcommand)) < 0){
+		free(cmd_send);
+		return -1;
+	}
+	if(write(handle->p_write, &big_endian_size, sizeof(uint32_t)) < 0){
+		free(cmd_send);
+		return -1;
+	}
+	if(write(handle->p_write, cmd_send, cmd_size) < 0){
+		free(cmd_send);
+		return -1;
+	}
+
+	free_data(handle);
+	handle->protect = 1;
+	free(cmd_send);
+	return 0;
+}
+
+/*
+ * Reading some unparse data from the server.
+ * */
+int hg_rawread(hg_handle *handle, char *buffer, size_t sizebuff)
+{
+	int length = handle->bytes_on_pipe;
+	hg_header *ch = handle->header;
+
+	if(!handle) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	/* If the current channel is not an input channel return 0. */
+	if(!(ch->channel == o || ch->channel == e))
+		return 0;
+
+	length = (length > sizebuff)? sizebuff : length;
+
+	if(read(handle->p_read, buffer, length) < 0){
+		return -1;
+	}
+
+	buffer[length] = '\0';
+	handle->bytes_on_pipe -= length;
+
+	return length;
+}
+
+/*
+ * Will write the buffer to the server.
+ * */
+int hg_rawwrite(hg_handle *handle, const char *buffer, size_t buff_size)
+{
+	int length = handle->bytes_on_pipe;
+	uint32_t swap_size;
+
+	if(!handle) {
+		errno = EINVAL;
+		return -1;
+	}
+	length = (length > buff_size)? buff_size : length;
+	swap_size = swap_uint32(length);
+
+	if(write(handle->p_write, &swap_size, sizeof(uint32_t)) < 0){
+		return -1;
+	}
+	if(write(handle->p_write, buffer, length) < 0){
+		return -1;
+	}
+
+	handle->bytes_on_pipe = 0;
+	return length;
+}
+
+/*
+ * The exitcode for the current command.
+ * */
+int hg_exitcode(hg_handle *handle)
+{
+	int exitcode;
+
+	if(handle == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if(read(handle->p_read, &exitcode, sizeof(int)) < 0){
+		return -1;
+	}
+	handle->protect = 0;
+	return swap_uint32(exitcode);
+}
+
+/* return the output data. */
+char *get_output_data(hg_handle *handle)
+{
+	return handle->out_data;
+}
diff --git a/utils.c b/utils.c
new file mode 100644
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,43 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "utils.h"
+
+/* 
+ * Byte swap unsigned int
+ * */
+uint32_t swap_uint32(uint32_t val)
+{
+	val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
+	return (val << 16) | (val >> 16);
+}
+
+/*
+ * Prepare the command for sending process. 
+ * */
+char *cmd_prepare(char *const command[], int *cmd_size)
+{
+	size_t cmd_length = 0;
+	char *new_cmd;
+	int i = 0;
+
+	while(command[i]){
+		cmd_length += strlen(command[i]) + 1;
+		++i;
+	}
+
+	new_cmd = malloc(cmd_length + 1);
+	i = 0;
+	while(command[i]){
+		strcpy(new_cmd, command[i]);
+		new_cmd += strlen(command[i]) + 1; 
+		++i;
+	}
+	new_cmd -= cmd_length;
+
+	*cmd_size = cmd_length - 1;
+	return new_cmd;
+}


More information about the Mercurial-devel mailing list