new file mode 100644
@@ -0,0 +1,198 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#include "client.h"
+
+#define HGPATH "hg"
+#define CHANGESET "\\0{rev}\\n{node}\\n{tags}\\n{branch}\\n{author}\
+ \\n{date|isodate}\\n{desc}"
+
+/* utils.h command*/
+/**
+ * \brief A helper for building the command arguments.
+ * \param command The name for mercurial command.
+ * \param options An array pointer to command option list.
+ * \param template Template to be used for this command.
+ * \retval new_string An array of pointers, where the command it's almost ready
+ * to be send to mercurial cmdserver.
+ * */
+char **cmdbuilder(char *command, char *options[], char *template)
+{
+ int cmd_size, size;
+ char **new_cmd;
+ char *temp = "--template";
+
+ for(size = 0; options[size] != NULL; size++);
+ cmd_size = size + 1;
+ new_cmd = malloc(sizeof(char *) * (cmd_size + 3));
+
+ new_cmd[0] = command;
+ memcpy(new_cmd + 1, options, (cmd_size - 1) * sizeof(char *));
+ if(template){
+ new_cmd[cmd_size] = temp;
+ new_cmd[cmd_size + 1] = template;
+ new_cmd[cmd_size + 2] = NULL;
+ }else{
+ new_cmd[cmd_size] = NULL;
+ }
+
+ return new_cmd;
+}
+
+/**
+ * \brief 'Parse a changeset'. It's more like pointing to the correct position.
+ *
+ * The changeset could be found on buff pointer. To not duplicate the data I
+ * choose to point every log_entry field to the right position.
+ * \param buff The pointer where changeset could be found.
+ * \param le The log_entry_t structure where the changeset will be parse.
+ * \retval 0 if successful.
+ * */
+int parse_changeset(char *cset, hg_log_entry_t *le)
+{
+ char *position = cset;
+ /* set pointer for revision position */
+ le->rev = cset;
+ position = strstr(position, "\n");
+ cset[position - cset] = '\0';
+
+ /* set pointer for node position */
+ le->node = position + 1;
+ position = strstr(position + 1, "\n");
+ cset[position - cset] = '\0';
+
+ /* set pointer for tag position */
+ le->tags = position + 1;
+ position = strstr(position + 1, "\n");
+ cset[position - cset] = '\0';
+
+ /* set pointer for branch position */
+ le->branch = position + 1;
+ position = strstr(position + 1, "\n");
+ cset[position - cset] = '\0';
+
+ /* set pointer for author position */
+ le->author = position + 1;
+ position = strstr(position + 1, "\n");
+ cset[position - cset] = '\0';
+
+ /* set pointer for data position */
+ le->date = position + 1;
+ position = strstr(position + 1, "\n");
+ cset[position - cset] = '\0';
+
+ /* set pointer for description position */
+ le->desc = position + 1;
+ /* */
+ return 0;
+}
+
+/* Adding to the destination pointer the source pointer. */
+int adding_data(char **dest, char *source, int dsize, int ssize)
+{
+ if(*dest == NULL){
+ *dest = malloc(ssize + 1);
+ memcpy(*dest, source, ssize + 1);
+ } else {
+ *dest = realloc(*dest, dsize + ssize + 1);
+ memcpy(*dest + dsize, source, ssize + 1);
+ }
+ return 0;
+}
+
+/* Erase the top cset from cset pointer. */
+int erase_cset(char **cset, int cset_size, int first_cset_size)
+{
+ int new_cset_size = cset_size - first_cset_size;
+ char *new_cset = malloc(new_cset_size + 1);
+ memcpy(new_cset, *cset + first_cset_size, new_cset_size + 1);
+ free(*cset);
+ *cset = new_cset;
+ return new_cset_size;
+}
+
+
+/* The high level log command for hglib API. */
+hg_cset_iterator *hg_log(hg_handle *handle, char *option[])
+{
+ hg_cset_iterator *iterator = malloc(sizeof(hg_cset_iterator));
+
+ iterator->handle = handle;
+ iterator->command = cmdbuilder("log", option, CHANGESET);
+
+ if(hg_rawcommand(handle, iterator->command) < 0){
+ return NULL;
+ }
+
+ iterator->cset = NULL;
+ iterator->cset_size = 0;
+ iterator->cset_send = 0;
+
+ return iterator;
+}
+
+/* The iterator next step. Getting the next changeset. */
+int hg_fetch_cset_entry(hg_cset_iterator *iter, hg_cset_entry_t *le)
+{
+ hg_header head = hg_head(iter->handle);
+ int exitcode;
+ char *get_data;
+ int read_size;
+
+ /* Erase the first cset from cset pointer.
+ * This cset was already pass to user.*/
+ if(iter->cset_send && iter->cset_size){
+ iter->cset_size = erase_cset(&iter->cset, iter->cset_size,
+ iter->top_cset_size);
+ iter->cset_send = 0;
+ }
+ while(head.channel != 'r'){
+ /* If there is a cset in cset pointer, then parse it and send
+ * it to user.*/
+ if(iter->cset && strlen(iter->cset + 1) < iter->cset_size -1){
+ iter->top_cset_size = strlen(iter->cset + 1) + 1;
+ parse_changeset(iter->cset + 1, le);
+ iter->cset_send = 1;
+ return head.length;
+ }
+ else{
+ /* Getting the next data from cmdserver and put on the
+ * end of the cset pointer. */
+ get_data = malloc(head.length + 1);
+ if(read_size = hg_rawread(iter->handle, get_data,
+ head.length), read_size < 0){
+ return -1;
+ }
+ adding_data(&iter->cset, get_data, iter->cset_size,
+ read_size);
+ iter->cset_size += read_size;
+ free(get_data);
+ }
+ }
+ /* After, receiveing the last message, there still could be some
+ * csets on cset pointer. */
+ if(iter->cset && strlen(iter->cset + 1) == iter->cset_size -1){
+ iter->top_cset_size = strlen(iter->cset + 1) + 1;
+ parse_changeset(iter->cset + 1, le);
+ iter->cset_size = 0;
+ iter->cset_send = 1;
+ return head.length;
+ /* Parse first cset from the remaining data. */
+ }else if(iter->cset_size && iter->cset_send){
+ iter->top_cset_size = strlen(iter->cset + 1) + 1;
+ parse_changeset(iter->cset + 1, le);
+ iter->cset_send = 1;
+ return head.length;
+ }
+
+ exitcode = hg_exitcode(iter->handle);
+ free(iter->command);
+ free(iter->cset);
+ free(iter);
+ return exitcode;
+
+}
@@ -65,6 +65,25 @@
int protect;
} hg_handle;
+typedef struct hg_cset_iterator{
+ hg_handle *handle;
+ char **command;
+ char *changeset;
+ int cset_size;
+ int cset_send;
+ int top_cset_size;
+}hg_cset_iterator;
+
+typedef struct hg_cset_entry_t{
+ char *author;
+ char *branch;
+ char *date;
+ char *desc;
+ char *node;
+ char *rev;
+ char *tags;
+}hg_cset_entry_t;
+
/**
* \brief Open the connection with the mercurial command server.
*
@@ -196,6 +215,63 @@
* */
int hg_exitcode(hg_handle *handle);
+/**
+ * \brief hg_log command for hglib API.
+ *
+ * It's an advance function to get revision history. It's more like the start
+ * point of the action, this function will prepare the query question and will
+ * send it to the cmd-server.
+ *
+ * Return the revision history of the specified files or the entire project.
+ * File history is shown without following rename or copy history of files.
+ * Use follow with a filename to follow history across renames and copies.
+ * follow without a filename will only show ancestors or descendants of the
+ * starting revision. followfirst only follows the first parent of merge
+ * revisions.
+ *
+ * If revrange isn't specified, the default is "tip:0" unless follow is set,
+ * in which case the working directory parent is used as the starting
+ * revision.
+ *
+ * \param handle The handle of the connection, wherewith I want to communicate
+ * \param option The option list for mercurial log command.
+ * \retval hg_log_iterator A pointer to hg_log_iterator structure if successful
+ * \retval NULL to indicate an error, with errno set appropriately.
+ *
+ * errno can be:
+ * - hg_rawcommand errors
+ * */
+hg_cset_iterator *hg_log(hg_handle *handle, char *option[]);
+
+/**
+ * \brief The iterator step. Getting the next changeset.
+ *
+ * The revision history could have a huge mass of data. You can pass the entire
+ * history in one call, so we use an iterator-like mechanism. Calling the
+ * hg_fetch_log_entry, the next changeset will be read from cmd-server, parse
+ * and pass to hg_log_entry_t structure.
+ * The log_entry structure will handle a changeset with the following string
+ * fields:
+ * - rev
+ * - node
+ * - tags (space delimited)
+ * - branch
+ * - author
+ * - desc
+ *
+ * \param log_iterator The iterator for log command.
+ * \param log_entry The hg_log_entry_t structure where the changeset will be
+ * pass
+ * \retval number The lenght for the pass changeset.
+ * \retval exitcode To indicate the end of log_command.
+ * \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
+ * - read_header error
+ * */
+int hg_fetch_cset_entry(hg_cset_iterator *iterator, hg_cset_entry_t *entry_t);
#endif
new file mode 100644
@@ -0,0 +1,129 @@
+#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. *******/
+
+/**
+ * \brief Create and setup the tmp directory where the acction will happends.
+ * */
+void setup_tmp()
+{
+ system("hg init tmp");
+ chdir("tmp");
+}
+
+/**
+ * \brief Remove the tmp directory and all his files.
+ * */
+void clean_tmp()
+{
+ chdir("..");
+ system("rm -rf tmp");
+}
+
+/**
+ * \brief Fill the current repository with commits for log command.
+ * */
+void setup_log()
+{
+ system("touch foo ; hg add foo ; hg commit -m 'foo file'");
+ 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'");
+}
+
+/******* Examples using level 1 implementations. ******/
+
+/**
+ * \brief Log command example.
+ *
+ * \param handle The handle of the connection, wherewith I want to communicate
+ * \retval exitcode
+ * */
+int hg_log_example(hg_handle *handle)
+{
+ char *option[] = {NULL};
+ int nc;
+
+ /* hg_log function will a iterator. */
+ hg_cset_iterator *iterator = hg_log(handle, option);
+
+ /* you need to alloc some space for log_entry_t structure */
+ hg_cset_entry_t *le = malloc(sizeof(hg_cset_entry_t));
+
+ /* Getting the next changeset using the iterator-like mechanism.
+ Print the changest from log_entry structure.*/
+ while(nc = hg_fetch_cset_entry(iterator, le), nc > 0){
+ printf("rev = %s \n", le->rev);
+ printf("node = %s \n", le->node);
+ printf("tags = %s \n", le->tags);
+ printf("branch = %s \n", le->branch);
+ printf("author = %s \n", le->author);
+ printf("date = %s \n", le->date);
+ printf("desc = %s \n", le->desc);
+ printf("\n");
+ }
+
+ free(le);
+ /* last call for hg_fetch_log_entry will pass the exitcode */
+ return nc;
+}
+
+/** \brief Printing the welcome message.
+ *
+ * Will print the options that you will have in this example.
+ **/
+void print_select_case()
+{
+ printf("Select test case to run:\n");
+ printf("1) log \n");
+ printf("\n");
+ printf("Your choice: ");
+}
+
+
+/***** Main function. *******/
+/**
+ * \brief The main function
+ * */
+int main(int argc, char **argv)
+{
+ int select_case;
+ hg_handle *handle;
+
+ print_select_case();
+ scanf("%d", &select_case);
+ if(select_case < 1 || select_case > 1){
+ printf("Your choice is not an option...\n");
+ return -1;
+ }
+
+ switch(select_case){
+ case 1:
+ setup_tmp();
+ setup_log();
+ handle = hg_open(NULL, "");
+
+ hg_log_example(handle);
+
+ hg_close(&handle);
+ clean_tmp();
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}