Patchwork [2,of,3,RFC,-V3] Implementation of basic hg commands via low level API (stories)

login
register
mail settings
Submitter Iulian Stana
Date July 25, 2013, 5:27 p.m.
Message ID <a97753ae4036ba4ea34c.1374773266@doppler>
Download mbox | patch
Permalink /patch/1960/
State Deferred
Headers show

Comments

Iulian Stana - July 25, 2013, 5:27 p.m.
# HG changeset patch
# User Iulian Stana <julian.stana@gmail.com>
# Date 1374753623 -10800
#      Thu Jul 25 15:00:23 2013 +0300
# Node ID a97753ae4036ba4ea34ccb67971d31c01f97ed4b
# Parent  2cc0845928f01bf161cb1d4983bd029c7c3e70aa
Implementation of basic hg commands via low level API (stories)

The main.c file is not “real code”, just a showcase of examples. It is more of a
documentation than actual code. It contains the "stories" that Matt asked me to
prepare. Here I created some scenarios to test those stories.

Those examples stands with purpose to show that you can implement and handle in
some ways all the mercurial commands, using just the level 0 of c-hglib API.

a) The init command, which starts with no repository. For this story I use the
following approach: first I create a new process where I will execute the init
command with plain hg, and after the child ends I establish the connection
with the new repository.

b) The log command, which can produce huge data. The data is read in chunks. In
my example I just print this data to the standard output, but the user is free
to do what ever he wants with this data. (write it into a file or process it)

c) The import command, which can deal with the command server input channels. On
this task, I was able to show that you can import a patch using stdin. In the
same time you can specify a file to simulate the stdin process.

d) The merge command, which can deal prompts. Sometimes when a conflict appears,
the merge command could use those prompts to interact with user desires.
In my function I gave an example on how a user could pass a prompt procedure, a
procedure where the interation is handled. (in my procedure function the answer
will be received from stdin)

e) The verify command, where the command server could send output and error
data. The received data will be split in two. You will know the type of data
(output data or error data)  with the channel help, who can be found in the
header structure of the handle.

f) Export-import without saving data into ram or disk memory. There will be two
connections, one for the export repository (export handle) and one for the
import repository ( import handle). Immediately after you receive any kind of
data from the export handle, you will pass it to the import handle.

g) Add 'filename with spaces' example.

All of those examples are build with level 0 function witout using fancy
functions.
I refer to those implementation as 'by hand' ones, as opposed to 'using a lot of
technology and abstraction.
Giovanni Gherdovich - Aug. 5, 2013, 5:03 a.m.
Hello Iulian,

from your code it looks like you forgot that after you feed some
input into the 'L' channel, you have to terminate your input with
a zero-length message.

Look here: http://mercurial.selenic.com/wiki/CommandServer

"length = 0 sent by the client is interpreted as EOF by the server."

My comments interleaved in your code.

:::: # HG changeset patch
:::: # User Iulian Stana <julian.stana at gmail.com>
:::: # Date 1374753623 -10800
:::: #      Thu Jul 25 15:00:23 2013 +0300
:::: # Node ID a97753ae4036ba4ea34ccb67971d31c01f97ed4b
:::: # Parent  2cc0845928f01bf161cb1d4983bd029c7c3e70aa
:::: Implementation of basic hg commands via low level API (stories)
::::
:::: The main.c file is not “real code”, just a showcase of examples. It is
more of a
:::: documentation than actual code. It contains the "stories" that Matt
asked me to
:::: prepare. Here I created some scenarios to test those stories.
::::
:::: Those examples stands with purpose to show that you can implement and
handle in
:::: some ways all the mercurial commands, using just the level 0 of
c-hglib API.
::::
:::: a) The init command, which starts with no repository. For this story I
use the
:::: following approach: first I create a new process where I will execute
the init
:::: command with plain hg, and after the child ends I establish the
connection
:::: with the new repository.
::::
:::: b) The log command, which can produce huge data. The data is read in
chunks. In
:::: my example I just print this data to the standard output, but the user
is free
:::: to do what ever he wants with this data. (write it into a file or
process it)
::::
:::: c) The import command, which can deal with the command server input
channels. On
:::: this task, I was able to show that you can import a patch using stdin.
In the
:::: same time you can specify a file to simulate the stdin process.
::::
:::: d) The merge command, which can deal prompts. Sometimes when a
conflict appears,
:::: the merge command could use those prompts to interact with user
desires.
:::: In my function I gave an example on how a user could pass a prompt
procedure, a
:::: procedure where the interation is handled. (in my procedure function
the answer
:::: will be received from stdin)
::::
:::: e) The verify command, where the command server could send output and
error
:::: data. The received data will be split in two. You will know the type
of data
:::: (output data or error data)  with the channel help, who can be found
in the
:::: header structure of the handle.
::::
:::: f) Export-import without saving data into ram or disk memory. There
will be two
:::: connections, one for the export repository (export handle) and one for
the
:::: import repository ( import handle). Immediately after you receive any
kind of
:::: data from the export handle, you will pass it to the import handle.
::::
:::: g) Add 'filename with spaces' example.
::::
:::: All of those examples are build with level 0 function witout using
fancy
:::: functions.
:::: I refer to those implementation as 'by hand' ones, as opposed to
'using a lot of
:::: technology and abstraction.
::::
:::: diff --git a/main.c b/main.c
:::: new file mode 100644
:::: --- /dev/null
:::: +++ b/main.c
:::: @@ -0,0 +1,444 @@
:::: +#include <stdio.h>
:::: +#include <stdlib.h>
:::: +#include <string.h>
:::: +
:::: +#include <sys/wait.h>
:::: +#include <sys/types.h>
:::: +#include <unistd.h>
:::: +#include <sys/stat.h>
:::: +#include <fcntl.h>
:::: +
:::: +#include "client.h"
:::: +#include "utils.h"
:::: +
:::: +#define INIT_REPO  "init_test_repo"
:::: +
:::: +/****** Convenience functions. *******/
:::: +
:::: +/* Setup the tmp directory where the magic happends.*/
:::: +void setup_tmp()
:::: +{
:::: +    system("hg init tmp");
:::: +    chdir("tmp");
:::: +}
:::: +
:::: +void clean_tmp()
:::: +{
:::: +    chdir("..");
:::: +    system("rm -rf tmp");
:::: +}
:::: +
:::: +/* Setup for init command. */
:::: +void setup_init()
:::: +{
:::: +    system("mkdir tmp");
:::: +    chdir("tmp");
:::: +}
:::: +
:::: +/* Create some commits in the created repo. */
:::: +void post_init_setup()
:::: +{
:::: +    chdir(INIT_REPO);
:::: +    system("touch foo ; hg add foo ; hg commit -m foo");
:::: +    system("echo baloo > foo ; hg commit -m 'baloo text'");
:::: +    chdir("..");
:::: +}
:::: +
:::: +/* Setup for log command. */
:::: +void setup_log()
:::: +{
:::: +    system("touch foo ; hg add foo ; hg commit -m foo");
:::: +    system("echo baloo > foo ; hg commit -m 'baloo text'");
:::: +    system("touch voodoo ; hg add voodoo ; hg commit -m voodoo");
:::: +    system("echo voodoo > voodoo ; hg commit -m 'voodoo text'");
:::: +}
:::: +
:::: +/* Setup the merge command. Without conflicts.*/
:::: +void setup_merge()
:::: +{
:::: +    system("touch foo ; hg add foo ; hg commit -m foo");
:::: +    system("echo baloo > foo ; hg commit -m 'baloo text'");
:::: +    system("hg up 0");
:::: +    system("touch boo ; hg add boo ; hg commit -m boo");
:::: +    system("echo voodoo > boo ; hg commit -m 'voodoo text'");
:::: +}
:::: +
:::: +/* Setup the merge command. Create a conflict that will have
prompts.*/
:::: +void setup_conflict_merge()
:::: +{
:::: +    system("touch foo ; hg add foo ; hg commit -m foo");
:::: +    system("echo baloo > foo ; hg commit -m 'baloo text'");
:::: +    system("hg up 0");
:::: +    system("echo voodoo > foo ; hg commit -m 'voodoo text'");
:::: +}
:::: +
:::: +/* Setup for export_import process.*/
:::: +void setup_export_import()
:::: +{
:::: +    system("hg init export");
:::: +    chdir("export");
:::: +    system("touch foo ; echo baloo > foo; hg add foo ; hg commit -m
foo");
:::: +    chdir("..");
:::: +    system("hg init import");
:::: +}
:::: +
:::: +void clean_export_import()
:::: +{
:::: +    system("rm -rf export import");
:::: +}
:::: +
:::: +/* Setup for filename_with_space example.*/
:::: +void setup_filename_with_space()
:::: +{
:::: +    system("touch 'foo bar'");
:::: +    printf("---- ls command ----\n");
:::: +    system("ls -l");
:::: +}
:::: +
:::: +/******* By hand implementations. ******/
:::: +
:::: +/*
:::: + * Create a repo to the specific path, and then will open the
connection with
:::: + * this new repo.
:::: + * The clone command will follow the same steps. Clone repo; open
connection.
:::: + * */
:::: +hg_handle *hg_init_by_hand()
:::: +{
:::: +    pid_t cpid;
:::: +    int status;
:::: +    hg_handle *handle;
:::: +    char command[50];
:::: +
:::: +    sprintf(command, "hg init %s", INIT_REPO);
:::: +
:::: +    if((cpid = fork()) < 0) {
:::: +        printf("Fork failed\n");
:::: +        return NULL;
:::: +
:::: +    } else if(cpid == 0) {
:::: +        execl("/bin/sh", "sh", "-c", command, NULL);
:::: +        printf("dadads\n\n");
:::: +    } else {
:::: +         waitpid( cpid, &status, 0);
:::: +    }
:::: +
:::: +    handle = hg_open(INIT_REPO, "");
:::: +
:::: +    return handle;
:::: +}
:::: +
:::: +/*
:::: + * Will read a huge mass of data in chunks and will print this data
on stdout.
:::: + * */
:::: +int hg_log_by_hand(hg_handle *handle)
:::: +{
:::: +    char buff[4096];
:::: +    char *comm[] = {"log", "-v"};
:::: +    int exitcode;
:::: +    int ns;
:::: +
:::: +    hg_rawcommand(handle, comm, 2);
:::: +
:::: +    while(hg_channel(handle) != 'r'){
:::: +        while(ns = hg_rawread(handle, buff, 4096), ns > 0){
:::: +            printf("%s", buff);
:::: +        }
:::: +    }
:::: +
:::: +    exitcode = hg_exitcode(handle);
:::: +    printf("exitcode = %d\n", exitcode);
:::: +
:::: +    return exitcode;
:::: +}
:::: +
:::: +/*
:::: + * The purpose is to show the functionality, it's not the import
function.
:::: + * This function will import to the handle the import_patch, which is
a file or
:::: + * will use the stdin if the import_patch is NULL.
:::: + * */
:::: +int hg_import_by_hand(hg_handle *handle, char *import_patch)
:::: +{
:::: +    char *comm[] = {"import", "-"};
:::: +    char buff[4096];
:::: +    int exitcode = 0;
:::: +    int fd, ns;
:::: +    hg_header header;
:::: +
:::: +    fd = open(import_patch, O_RDONLY);
:::: +    hg_rawcommand(handle, comm, 2);
:::: +
:::: +    while(hg_channel(handle) != 'r'){
:::: +        /* outchannels 'o' or 'e'. */
:::: +        while(ns = hg_rawread(handle, buff, 4096), ns > 0){
:::: +            printf("%s", buff);
:::: +        }
:::: +        if(hg_channel(handle) == 'L'){
:::: +            header = hg_head(handle);
:::: +            int length = read(fd, buff, header.length);
:::: +            hg_rawwrite(handle, buff, length);
:::: +        }
:::: +    }

The while loop above, inside which you manage input to the 'L' channel,
works for a subtle reason, and I am not sure it was what you intended:
after you're done feeding the patch into the command server, this latter
will ask for more input via prompting you with a ('L', 4096) line.

So, your code will enter the 'if' branch once again; but the file
with the patch has all been consumed, and the read in

int length = read(fd, buff, header.length);

will return 0. Then you feed this zero-length message into

hg_rawwrite(handle, buff, length);

which terminates the 'L' channel input session. Not bad,
only you didn't realize that I think :)

:::: +
:::: +    exitcode = hg_exitcode(handle);
:::: +    return exitcode;
:::: +}
:::: +
:::: +/* The prompt function for the merge funtion.*/
:::: +char prompt_function(char *output)
:::: +{
:::: +    char option;
:::: +    scanf("\n%c", &option);
:::: +    return option;
:::: +}
:::: +
:::: +/*
:::: + * The merge function gets a prompt function that deals with promps.
:::: + * It's just a default function, to show the mechanism(is't not a
level 0 issue)
:::: + * This function show that you can create a merge function that
handle prompts
:::: + * with level 0 API.
:::: + * */
:::: +int hg_merge_by_hand(hg_handle *handle, char (*prompt)(char *))
:::: +{
:::: +    char *comm[] = {"merge", "--tool=internal:prompt"};
:::: +    char buff[4096];
:::: +    int exitcode = 0;
:::: +    int ns;
:::: +
:::: +    hg_rawcommand(handle, comm, 2);
:::: +
:::: +    while(hg_channel(handle) != 'r'){
:::: +        /* outchannels 'o' or 'e'. */
:::: +        while(ns = hg_rawread(handle, buff, 4096), ns > 0){
:::: +            printf("%s", buff);
:::: +        }
:::: +        if(hg_channel(handle) == 'I'){
:::: +            printf("INPUT\n");
:::: +        }
:::: +        else if(hg_channel(handle) == 'L'){
:::: +            /* In stand of the default prompt_msg, the argument for
:::: +             * prompt function will be the output data received
:::: +             * until this moment.
:::: +             * */
:::: +            char option = (*prompt)(NULL);
:::: +            printf("\noption = %c\n", option);
:::: +            hg_rawwrite(handle, &option, 1);
:::: +            /* TODO: is a small issue, but not for this level.
:::: +             * The second write, must not be necessary.
:::: +             * */
:::: +            hg_rawwrite(handle, &option, 0);
:::: +        }
:::: +    }

Here you do some voodoo programming
http://www.catb.org/jargon/html/V/voodoo-programming.html
by adding the line

hg_rawwrite(handle, &option, 0);

why is that needed? Because, again, after you send stuff into ('L', 4096)
you need to tell command server that you're done via a zero length message.
Which is,

hg_rawwrite(handle, '', 0);

would have also worked.

If you don't have already something like this, here is what I use
to have interactive conversation with the command server:

first off, a couple of function that I put in a file named util.py:

# -- -- -- -- -- -- -- -- util.py -- -- -- --
import sys, struct, subprocess

class server:

    def __init__(self):
        self.server = subprocess.Popen(['hg',
                                        '--config',
                                        'ui.interactive=True',
                                        'serve',
                                        '--cmdserver',
                                        'pipe'],
                                       stdin=subprocess.PIPE,
                                       stdout=subprocess.PIPE)

    def readchannel(self):
        channel, length = struct.unpack('>cI',
                                        self.server.stdout.read(5))
        if channel in 'IL': # input
            return channel, length
        return channel, length, self.server.stdout.read(length)

    def writeblock(self, data):
        self.server.stdin.write(struct.pack('>I', len(data)))
        self.server.stdin.write(data)
        self.server.stdin.flush()
# -- -- -- -- -- -- -- -- end of util.py -- -- -- --

(it is basically the client at
http://mercurial.selenic.com/wiki/CommandServer#Example_client )

Then I run sessions on the python shell like this one:

>>> # -- -- -- -- -- -- -- -- interactive python session -- -- -- --
>>> import sys
>>>
>>> from util import *
>>>
>>> s = server()
>>>
>>> hello = s.readchannel()
>>> hello
('o', 52, 'capabilities: getencoding runcommand\nencoding: UTF-8')
>>>
>>> s.server.stdin.write('runcommand\n')
>>> s.writeblock('\0'.join('import -'.split()))
>>> ans = s.readchannel()
>>> repr(ans)
"('o', 26, 'applying patch from stdin\\n')"
>>> ans = s.readchannel()
>>> repr(ans)
"('L', 4096)"
>>>
>>> patch = """# HG changeset patch
... # User gghh
... # Date 1375612762 -7200
... #      Sun Aug 04 12:39:22 2013 +0200
... # Node ID 644e1273b89bf82704dbe8521243d77bfa4756f2
... # Parent  0000000000000000000000000000000000000000
... foo
...
... diff -r 000000000000 -r 644e1273b89b foo
... --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
... +++ b/foo    Sun Aug 04 12:39:22 2013 +0200
... @@ -0,0 +1,1 @@
... +baloo
... """
>>>
>>> s.writeblock(patch)
>>> ans = s.readchannel()
>>> repr(ans)
"('L', 4096)"
>>>
>>> s.writeblock('')
>>> ans = s.readchannel()
>>> repr(ans)
"('r', 4, '\\x00\\x00\\x00\\x00')"
>>>

you can see that I have to send a empty message with s.writeblock('').

That's it; you will have to consider this when implementing level 1 stuff.

Cheers,
Giovanni

Patch

diff --git a/main.c b/main.c
new file mode 100644
--- /dev/null
+++ b/main.c
@@ -0,0 +1,444 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "client.h"
+#include "utils.h"
+
+#define INIT_REPO  "init_test_repo"
+
+/****** Convenience functions. *******/
+
+/* Setup the tmp directory where the magic happends.*/
+void setup_tmp()
+{
+	system("hg init tmp");
+	chdir("tmp");
+}
+
+void clean_tmp()
+{
+	chdir("..");
+	system("rm -rf tmp");
+}
+
+/* Setup for init command. */
+void setup_init()
+{
+	system("mkdir tmp");
+	chdir("tmp");
+}
+
+/* Create some commits in the created repo. */
+void post_init_setup()
+{
+	chdir(INIT_REPO);
+	system("touch foo ; hg add foo ; hg commit -m foo");
+	system("echo baloo > foo ; hg commit -m 'baloo text'");
+	chdir("..");
+}
+
+/* Setup for log command. */
+void setup_log()
+{
+	system("touch foo ; hg add foo ; hg commit -m foo");
+	system("echo baloo > foo ; hg commit -m 'baloo text'");
+	system("touch voodoo ; hg add voodoo ; hg commit -m voodoo");
+	system("echo voodoo > voodoo ; hg commit -m 'voodoo text'");
+}
+
+/* Setup the merge command. Without conflicts.*/
+void setup_merge()
+{
+	system("touch foo ; hg add foo ; hg commit -m foo");
+	system("echo baloo > foo ; hg commit -m 'baloo text'");
+	system("hg up 0");
+	system("touch boo ; hg add boo ; hg commit -m boo");
+	system("echo voodoo > boo ; hg commit -m 'voodoo text'");
+}
+
+/* Setup the merge command. Create a conflict that will have prompts.*/
+void setup_conflict_merge()
+{
+	system("touch foo ; hg add foo ; hg commit -m foo");
+	system("echo baloo > foo ; hg commit -m 'baloo text'");
+	system("hg up 0");
+	system("echo voodoo > foo ; hg commit -m 'voodoo text'");
+}
+
+/* Setup for export_import process.*/
+void setup_export_import()
+{
+	system("hg init export");
+	chdir("export");
+	system("touch foo ; echo baloo > foo; hg add foo ; hg commit -m foo");
+	chdir("..");
+	system("hg init import");
+}
+
+void clean_export_import()
+{
+	system("rm -rf export import");
+}
+
+/* Setup for filename_with_space example.*/
+void setup_filename_with_space()
+{
+	system("touch 'foo bar'");
+	printf("---- ls command ----\n");
+	system("ls -l");
+}
+
+/******* By hand implementations. ******/
+
+/*
+ * Create a repo to the specific path, and then will open the connection with 
+ * this new repo.
+ * The clone command will follow the same steps. Clone repo; open connection.
+ * */
+hg_handle *hg_init_by_hand()
+{
+	pid_t cpid;
+	int status;
+	hg_handle *handle;
+	char command[50];
+
+	sprintf(command, "hg init %s", INIT_REPO);
+
+	if((cpid = fork()) < 0) {
+		printf("Fork failed\n");
+		return NULL;
+
+	} else if(cpid == 0) {
+		execl("/bin/sh", "sh", "-c", command, NULL);
+		printf("dadads\n\n");
+	} else {
+		 waitpid( cpid, &status, 0);
+	}
+
+	handle = hg_open(INIT_REPO, "");
+
+	return handle;
+}
+
+/*
+ * Will read a huge mass of data in chunks and will print this data on stdout.
+ * */
+int hg_log_by_hand(hg_handle *handle)
+{
+	char buff[4096];
+	char *comm[] = {"log", "-v"};
+	int exitcode;
+	int ns;
+
+	hg_rawcommand(handle, comm, 2);
+
+	while(hg_channel(handle) != 'r'){
+		while(ns = hg_rawread(handle, buff, 4096), ns > 0){
+			printf("%s", buff);
+		}
+	}
+
+	exitcode = hg_exitcode(handle);
+	printf("exitcode = %d\n", exitcode);
+
+	return exitcode;
+}
+
+/*
+ * The purpose is to show the functionality, it's not the import function.
+ * This function will import to the handle the import_patch, which is a file or
+ * will use the stdin if the import_patch is NULL.
+ * */
+int hg_import_by_hand(hg_handle *handle, char *import_patch)
+{
+	char *comm[] = {"import", "-"};
+	char buff[4096];
+	int exitcode = 0;
+	int fd, ns;
+	hg_header header;
+
+	fd = open(import_patch, O_RDONLY);
+	hg_rawcommand(handle, comm, 2);
+
+	while(hg_channel(handle) != 'r'){
+		/* outchannels 'o' or 'e'. */
+		while(ns = hg_rawread(handle, buff, 4096), ns > 0){
+			printf("%s", buff);
+		}
+		if(hg_channel(handle) == 'L'){
+			header = hg_head(handle);
+			int length = read(fd, buff, header.length);
+			hg_rawwrite(handle, buff, length);
+		}
+	}
+
+	exitcode = hg_exitcode(handle);
+	return exitcode;
+}
+
+/* The prompt function for the merge funtion.*/
+char prompt_function(char *output)
+{
+	char option;
+	scanf("\n%c", &option);
+	return option;
+}
+
+/*
+ * The merge function gets a prompt function that deals with promps.
+ * It's just a default function, to show the mechanism(is't not a level 0 issue)
+ * This function show that you can create a merge function that handle prompts
+ * with level 0 API.
+ * */
+int hg_merge_by_hand(hg_handle *handle, char (*prompt)(char *))
+{
+	char *comm[] = {"merge", "--tool=internal:prompt"};
+	char buff[4096];
+	int exitcode = 0;
+	int ns;
+
+	hg_rawcommand(handle, comm, 2);
+
+	while(hg_channel(handle) != 'r'){
+		/* outchannels 'o' or 'e'. */
+		while(ns = hg_rawread(handle, buff, 4096), ns > 0){
+			printf("%s", buff);
+		}
+		if(hg_channel(handle) == 'I'){
+			printf("INPUT\n");
+		}
+		else if(hg_channel(handle) == 'L'){
+			/* In stand of the default prompt_msg, the argument for 
+			 * prompt function will be the output data received
+			 * until this moment.
+			 * */
+			char option = (*prompt)(NULL);
+			printf("\noption = %c\n", option);
+			hg_rawwrite(handle, &option, 1);
+			/* TODO: is a small issue, but not for this level.
+			 * The second write, must not be necessary.
+			 * */
+			hg_rawwrite(handle, &option, 0);
+		}
+	}
+
+	exitcode = hg_exitcode(handle);
+	printf("exitcode = %d\n", exitcode);
+
+	return exitcode;
+}
+
+/*
+ * Will receive data, either from output channel, either from error channel.
+ * The data is printed on stdout. 
+ * */
+int hg_verify_by_hand(hg_handle *handle)
+{
+	char *comm[] = {"verify"};
+	char buff[4096];
+	int exitcode = 0;
+	int ns;
+
+	hg_rawcommand(handle, comm, 1);
+
+	while(hg_channel(handle) != 'r'){
+		while(ns = hg_rawread(handle, buff, 4096), ns > 0){
+			if(hg_channel(handle) == 'o'){
+				printf("out = %s", buff);
+			}
+			else if(hg_channel(handle) == 'e'){
+				printf("err = %s", buff);
+			}
+		}
+	}
+
+	exitcode = hg_exitcode(handle);
+	printf("exitcode = %d\n", exitcode);
+
+	return exitcode;
+}
+
+/* An export-import process that is not buffering the entire path in to memory*/
+void hg_export_import_by_hand(hg_handle *ehandle, hg_handle *ihandle)
+{
+	char *export_comm[] = {"export", "-r", "0"};
+	char *import_comm[] = {"import", "-"};
+	char ebuff[4096], ibuff[4096];
+	int es, is;
+
+	hg_rawcommand(ehandle, export_comm, 3);
+	hg_rawcommand(ihandle, import_comm, 2);
+
+	while(hg_channel(ehandle) != 'r'){
+		while(es = hg_rawread(ehandle, ebuff, 4096), es > 0){
+			while(is = hg_rawread(ihandle, ibuff, 4096), is > 0){
+				printf("%s", ibuff);
+			}
+			if(hg_channel(ihandle) == 'L'){
+				hg_rawwrite(ihandle, ebuff, strlen(ebuff));
+			}
+		}
+	}
+	/* Send a message to ihandle to understand that the import process
+	 * was ending*/
+	hg_rawwrite(ihandle, ebuff, 0);
+
+	while(hg_channel(ihandle) != 'r'){
+		while(is = hg_rawread(ihandle, ibuff, 4096), is > 0){
+			printf("%s", ibuff);
+		}
+	}
+
+	printf("exitcode for export process is %d \n", hg_exitcode(ehandle));
+	printf("exitcode for import process is %d \n", hg_exitcode(ihandle));
+
+}
+
+int hg_add_filename_with_space_by_hand(hg_handle *handle)
+{
+	char buff[4096];
+	char *comm[] = {"add", "foo bar"};
+	int exitcode;
+	int ns;
+
+	hg_rawcommand(handle, comm, 2);
+
+	while(hg_channel(handle) != 'r'){
+		while(ns = hg_rawread(handle, buff, 4096), ns > 0){
+			printf("%s", buff);
+		}
+	}
+
+	exitcode = hg_exitcode(handle);
+	printf("exitcode = %d\n", exitcode);
+
+	return exitcode;
+}
+
+/* Print the start prompt, what choice you have. */
+void print_select_case()
+{
+	printf("Select test case to run:\n");
+	printf("0) init - 'make some commits' & log \n");
+	printf("1) log \n");
+	printf("2) import - from stdin (simulate from a file) \n");
+	printf("3) merge - (solve conflicts from stdin) \n");
+	printf("4) merge - (without conflicts)\n");
+	printf("5) verify \n");
+	printf("6) verify a corrupt repo.\n");
+	printf("7) export-import example.\n");
+	printf("8) add 'filename with space'\n");
+	printf("\n");
+	printf("Your choice: ");
+}
+
+
+/***** Main function. *******/
+
+int main(int argc, char **argv)
+{
+	int select_case;
+	hg_handle *handle;
+	hg_handle *ehandle, *ihandle;
+
+	print_select_case();
+	scanf("%d", &select_case);
+	if(select_case < 0 || select_case > 8){
+		printf("Your choice is not an option...\n");
+		return -1;
+	}
+
+	switch(select_case){
+		case 0:
+			setup_init();
+			handle = hg_init_by_hand();
+			post_init_setup();
+			hg_log_by_hand(handle);
+
+			hg_close(&handle);
+			clean_tmp();
+			break;
+		case 1:
+			setup_tmp();
+			setup_log();
+			handle = hg_open(NULL, "");
+			hg_log_by_hand(handle);
+
+			hg_close(&handle);
+			clean_tmp();
+			break;
+		case 2:
+			setup_export_import();
+			ihandle = hg_open("import", "");
+
+			chdir("export");
+			system("hg export -r 0 > r0.diff");
+			chdir("..");
+			hg_import_by_hand(ihandle, "export/r0.diff");
+
+			printf("\n");
+			hg_log_by_hand(ihandle);
+
+			hg_close(&ihandle);
+			clean_export_import();
+			break;
+		case 3:
+		case 4:
+			setup_tmp();
+			if(select_case == 3)
+				setup_conflict_merge();
+			else
+				setup_merge();
+			handle = hg_open(NULL, "");
+			hg_merge_by_hand(handle, prompt_function);
+			system("hg ci -m 'merge'");
+			system("hg glog");
+
+			hg_close(&handle);
+			clean_tmp();
+			break;
+		case 5:
+		case 6:
+			setup_tmp();
+			setup_log();
+			handle = hg_open(NULL, "");
+			printf("\n");
+			if(select_case == 6)
+				system("rm .hg/store/data/foo.i");
+			hg_verify_by_hand(handle);
+
+			hg_close(&handle);
+			clean_tmp();
+			break;
+		case 7:
+			setup_export_import();
+			ehandle = hg_open("export", "");
+			ihandle = hg_open("import", "");
+			hg_export_import_by_hand(ehandle, ihandle);
+			hg_close(&ehandle);
+			hg_close(&ihandle);
+			clean_export_import();
+			break;
+		case 8:
+			setup_tmp();
+			setup_filename_with_space();
+			printf("\n");
+			handle = hg_open(NULL, "");
+			hg_add_filename_with_space_by_hand(handle);
+			printf("\n---- hs status command ----\n");
+			system("hg status");
+			clean_tmp();
+			break;
+		default:
+			break;
+	}
+
+	return 0;
+}