[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