atom feed4 messages in com.selenic.mercurial-devel[PATCH c-hglib] Initial commit
FromSent OnAttachments
Iulian StanaJul 1, 2013 2:31 pm 
Iulian StanaJul 1, 2013 2:32 pm 
Matt MackallJul 2, 2013 10:29 am 
Iulian StanaJul 2, 2013 11:54 am 
Subject:[PATCH c-hglib] Initial commit
From:Iulian Stana (juli@gmail.com)
Date:Jul 1, 2013 2:31:23 pm
List:com.selenic.mercurial-devel

# HG changeset patch # User Iulian Stana <juli@gmail.com> # Date 1372688980 -10800 # Mon Jul 01 17:29:40 2013 +0300 # Node ID 815459ad3e8f3b979084d621729fa189acd9c348 # Parent 0000000000000000000000000000000000000000 Initial commit

Level 0 : raw level

"pass a raw command string, get unparsed results"

Establish a connection between hgclient and mercurial command server. After the connection has been established, the messages were exchanged with the purpose of verification. In the main file will be found a short example.

I've followed some of Matt's remarks:

s/chg/hg_/, make sure everything is prefixed

Also, the prototypes all need to be in the header.

Make sure your char args are char *.

You have exactly open/close in your .h file. The example in my email has open/close/command/read. You're not going to be able to do useful work with less than that. Note that I specifically chose hg log as my example because it's likely to generate megabytes of output: you can't simply return it all in one call.

I've also change: - the data structure name from "client" to "handle" - the data structure name from "channel" to "header" - the name of readchannel function to readheader - some of the return statements

I've also erase the mercurial commands for this phase. I'll come back later with a better aproach.

diff --git a/hglib/Makefile b/hglib/Makefile new file mode 100644 --- /dev/null +++ b/hglib/Makefile @@ -0,0 +1,19 @@ +CC = gcc +CFLAGS = -Wall -g +TARGET = main + +build: main + +main: main.c utils.o client.o + $(CC) $(CFLAGS) utils.o client.o -o $(TARGET) main.c + +client.o: client.c + $(CC) $(CFLAGS) -c client.c + +utils.o: utils.c + $(CC) $(CFLAGS) -c utils.c + +.PHONY: clean + +clean: + rm -f *.o *~ $(TARGET) diff --git a/hglib/client.c b/hglib/client.c new file mode 100644 --- /dev/null +++ b/hglib/client.c @@ -0,0 +1,144 @@ +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include "client.h" +#include "utils.h" + +#define HGPATH "hg" + +struct hg_handle{ + int wpipe[2]; + int rpipe[2]; + pid_t childpid; + + #define p_read rpipe[0] + #define c_write rpipe[1] + #define c_read wpipe[0] + #define p_write wpipe[1] +}; + +/* + * Read the channel and the datasize into the header structure. + * */ +hg_header* readheader(hg_handle *handle) +{ + hg_header *ch = malloc(sizeof(hg_header)); + uint32_t length; + char ch_char; + + read(handle->p_read, &ch_char, 1); + read(handle->p_read, &length, sizeof(uint32_t)); + ch->channel = ch_char; + ch->length = swap_uint32(length); + + return ch; +} + +/* + * Read the hello message, the server sends when started + * Watch for a bad server starting. + * */ +int readhello(hg_handle *handle) +{ + hg_header *ch = readheader(handle); + char *buffer = malloc(ch->length); + read(handle->p_read, buffer, ch->length); + + //TODO: Make a verification !!! + + printf("%c%u", ch->channel, ch->length); + printf("%s\n", buffer); + free(ch); + free(buffer); + + return 0; +} + +/* + * Send the "command" to the cmdserver through handle. + * */ +int hg_rawcommand(hg_handle *handle, const char *command) +{ + char runcommand[] = "runcommand\n"; + uint32_t cmdsize = swap_uint32(strlen(command)); + write(handle->p_write, runcommand, strlen(runcommand)); + write(handle->p_write, &cmdsize, sizeof(uint32_t)); + write(handle->p_write, command, strlen(command)); + + return 0; +} + +/* + * Receive unparse data from the server through handle. + * Put the data into the buffer. + * */ +int hg_rawread(hg_handle *handle, char *buffer, size_t sizebuff) +{ + hg_header *ch = readheader(handle); + int length = ch->length; + + if(ch->channel == 'r'){ + free(ch); + return 0; + } + else{ + read(handle->p_read, buffer, ch->length); + buffer[ch->length] = '\0'; + free(ch); + return length; + } +} + +/* + * Create the connection with the mercurial cmdserver + * return the handle for this connection + * */ +hg_handle* hg_open(const char *path, char *encoding, char *configs) +{ + hg_handle *handle = malloc(sizeof(hg_handle)); + char command[100]; + + sprintf(command,"%s serve --cmdserver pipe --config ui.interactive=True", + HGPATH); + + if(path) + sprintf(command, "%s -R %s", command, path); + + if(pipe(handle->wpipe) < 0 || pipe(handle->rpipe) < 0){ + printf("The connection failed\n"); + } + + if((handle->childpid = fork()) < 0 ){ + printf("Fork failed\n"); + }else if( handle->childpid == 0){ //child + close(handle->p_write); + close(handle->p_read); + dup2(handle->c_read, STDIN_FILENO); close(handle->c_read); + dup2(handle->c_write, STDOUT_FILENO); close(handle->c_write); + execl("/bin/sh", "sh", "-c", command, NULL); + + }else{ //parent + close(handle->c_read); + close(handle->c_write); + } + + readhello(handle); + + return handle; +} + +/* + * Close the connection for a specific handle. + * */ +int hg_close(hg_handle *handle) +{ + close(handle->p_read); + close(handle->p_write); + free(handle); + + return 0; +} diff --git a/hglib/client.h b/hglib/client.h new file mode 100644 --- /dev/null +++ b/hglib/client.h @@ -0,0 +1,37 @@ +#ifndef _CLIENT_H_ +#define _CLIENT_H_ +#include <stdint.h> + + +struct hg_handle; +typedef struct hg_handle hg_handle; + +typedef struct hg_header{ + char channel; + uint32_t length; +} hg_header; + +/* + * Create the connection with the mercurial cmdserver + * return the handle for this connection + * */ +hg_handle* hg_open(const char *path, char *encoding, char *configs); + +/* + * Close the connection for a specific handle. + * */ +int hg_close(hg_handle *handle); + +/* + * Send the "command" to the cmdserver through handle. + * */ +int hg_rawcommand(hg_handle *handle, const char *command); + +/* + * Receive unparse data from the server through handle. + * Put the data into the buffer. + * */ +int hg_rawread(hg_handle *handle, char *buffer, size_t sizebuff); + + +#endif diff --git a/hglib/main.c b/hglib/main.c new file mode 100644 --- /dev/null +++ b/hglib/main.c @@ -0,0 +1,21 @@ +#include <stdio.h> + +#include "client.h" + + +int main(int argc, char ** argv) +{ + char buffer[4096]; + hg_handle* handle = hg_open(NULL, "", ""); + + printf("\n\n"); + hg_rawcommand(handle, "log"); + + while(hg_rawread(handle, buffer, 4096)){ + printf("%s", buffer); + } + + hg_close(handle); + + return 0; +} diff --git a/hglib/utils.c b/hglib/utils.c new file mode 100644 --- /dev/null +++ b/hglib/utils.c @@ -0,0 +1,13 @@ +#include<stdint.h> +#include<stdlib.h> + +#include "utils.h" + +void cmdbuilder(char **arg, size_t argsize); + +//! Byte swap unsigned int +uint32_t swap_uint32( uint32_t val ) +{ + val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); + return (val << 16) | (val >> 16); +} diff --git a/hglib/utils.h b/hglib/utils.h new file mode 100644 --- /dev/null +++ b/hglib/utils.h @@ -0,0 +1,26 @@ +#ifndef _UTILS_CHG_H_ +#define _UTILS_CHG_H_ + +#include<stdint.h> +#include<stdlib.h> +/* + * Build the command arguments. + * */ +void cmdbuilder(char **arg, size_t argsize); + +uint32_t swap_uint32( uint32_t val ); + +/* useful macro for handling error codes */ +#define DIE(assertion, call_description) \ + do { \ + if (assertion) { \ + fprintf(stderr, "(%s, %d): ", \ + __FILE__, __LINE__); \ + perror(call_description); \ + exit(EXIT_FAILURE); \ + } \ + } while(0) + + + +#endif