summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Pouzenc <ludovic@pouzenc.fr>2011-10-09 12:33:48 +0000
committerLudovic Pouzenc <ludovic@pouzenc.fr>2011-10-09 12:33:48 +0000
commit0f2c685db3d3790ce9bdc9598df8dae7d6b67eae (patch)
treea2c2a7f8941e87368ee46163e028c553d1b6bcdf
download2011-ddhardrescue-origin/0.x.tar.gz
2011-ddhardrescue-origin/0.x.tar.bz2
2011-ddhardrescue-origin/0.x.zip
On range tout le code actuel du trunk dans une branche nommée "0.x" et on crée une branche 1.x qui contiendra une nouvelle mouture (re-conception, méta info de packaging...).origin/0.x
git-svn-id: file:///var/svn/2011-ddhardrescue/branches/0.x@29 d3078510-dda0-49f1-841c-895ef4b7ec81
-rw-r--r--Makefile31
-rw-r--r--inc/cursesview.h9
-rw-r--r--inc/recover.h11
-rwxr-xr-xinc/slices.h50
-rw-r--r--inc/slices_evt.h19
-rwxr-xr-xinc/utils.h17
-rw-r--r--src/cursesview.c291
-rw-r--r--src/ddhardrescue.c133
-rwxr-xr-xsrc/essais/compil.sh4
-rw-r--r--src/essais/test.c86
-rw-r--r--src/essais/test2.c121
-rw-r--r--src/essais/test3.c169
-rw-r--r--src/recover.c128
-rw-r--r--src/slices.c282
-rw-r--r--src/slices_evt.c29
-rw-r--r--src/utils.c27
-rw-r--r--todo.txt12
17 files changed, 1419 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..fccc821
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,31 @@
+BIN=ddhardrescue
+CFLAGS=-Iinc -Wall -Werror -Wfatal-errors -g -pg
+LIBS=-lpanel -lncurses -lpthread
+
+#FIXME : liste des dépendances = tous les .c trouvés. C'est nul.
+
+all: $(BIN)
+$(BIN): bin/$(BIN)
+
+bin/$(BIN): obj/$(BIN).o $(patsubst src/%.c,obj/%.o,$(wildcard src/*.c))
+ gcc -o $@ $^ $(CFLAGS) $(LIBS)
+
+obj/%.o:
+ gcc -c -o $@ $< $(CFLAGS)
+
+deps/%.d: src/%.c
+ gcc -MM -o $@ $< $(CFLAGS) -MG -MT $(patsubst src/%.c,obj/%.o,$<)
+
+Makefile src/%.c inc/%.h: ;
+
+.PHONY: clean
+
+clean:
+ -rm -f obj/*.o deps/*.d bin/$(BIN)
+
+cont:
+ while true; do clear; date; LANG=C make; echo "make returns '$$?'";\
+ inotifywait -e modify -r . --exclude '(~$$|\.swp$$)' 2>/dev/null; done
+
+include $(patsubst src/%.c,deps/%.d,$(wildcard src/*.c))
+
diff --git a/inc/cursesview.h b/inc/cursesview.h
new file mode 100644
index 0000000..f4d343a
--- /dev/null
+++ b/inc/cursesview.h
@@ -0,0 +1,9 @@
+#ifndef CURSESVIEW_H
+#define CURSESVIEW_H
+
+#include "slices_evt.h"
+
+void cursesMainLoop(slices_evt_t *slicesEvt);
+
+#endif /*CURSESVIEW_H*/
+
diff --git a/inc/recover.h b/inc/recover.h
new file mode 100644
index 0000000..eee8141
--- /dev/null
+++ b/inc/recover.h
@@ -0,0 +1,11 @@
+#ifndef RECOVER_H
+#define RECOVER_H
+
+#include "slices_evt.h"
+
+extern int end;
+
+void recover(slices_evt_t *slicesEvt, char *src, char *dst, char*ddOpts);
+int tryRecoverUntilError(slice_t *sliceToRead, address_t *firstError, char *src, char *dst, char *ddOpts);
+
+#endif /*RECOVER_H*/
diff --git a/inc/slices.h b/inc/slices.h
new file mode 100755
index 0000000..b994636
--- /dev/null
+++ b/inc/slices.h
@@ -0,0 +1,50 @@
+#ifndef SLICES_H
+#define SLICES_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+/* IMPORTANT NOTES
+Slice are inclusive intervals. Let say sliceNew(1,2,S_UNKNOWN,NULL) return a [1;2] interval,
+ so interval lenght is end-begin+1. Here, it is 2 sectors lenght slice.
+*/
+
+typedef enum { S_UNKNOWN, S_RECOVERED, S_UNREADABLE } sliceStatus_t;
+typedef unsigned long long int address_t;
+
+typedef struct _slice {
+ address_t begin, end;
+ sliceStatus_t status;
+ struct _slice *next;
+} slice_t;
+
+typedef struct {
+ int count;
+ slice_t *first, *last;
+ address_t min, max;
+ pthread_mutex_t writeOrConsistentReadMutex;
+} slices_t;
+
+slice_t *sliceNew(address_t begin, address_t end, sliceStatus_t status, slice_t *next);
+void sliceDelete(slice_t *s);
+
+// Return the numbers of slices after split (3 in the general case, 2 or 1 in particular cases. -1 is memory error)
+int sliceSplit(slices_t *slices, slice_t *initialSlice, address_t splitAt, sliceStatus_t statusBefore, sliceStatus_t statusAt, sliceStatus_t statusAfter);
+
+void sliceDumpUpdate(char *dump, slice_t *s, address_t blockSize, unsigned int charCount, address_t begin, address_t end);
+
+slices_t *slicesNewEmpty();
+
+slices_t *slicesNewSingleton(address_t begin, address_t end, sliceStatus_t status);
+void slicesDelete(slices_t *slices);
+
+void slicesAppend(slices_t *slices, slice_t *slice);
+
+slice_t *slicesFindLargest(slices_t *slices, sliceStatus_t status);
+
+slice_t *slicesFindLargestFast(slices_t *slices, address_t *foundMax, sliceStatus_t status, address_t knownMax, slice_t *firstToTry);
+
+char *slicesDump(slices_t *slices, address_t *blockSize, unsigned int charCount, address_t begin, address_t end);
+
+#endif /*SLICES_H*/
diff --git a/inc/slices_evt.h b/inc/slices_evt.h
new file mode 100644
index 0000000..d5772b3
--- /dev/null
+++ b/inc/slices_evt.h
@@ -0,0 +1,19 @@
+#ifndef SLICES_EVT_H
+#define SLICES_EVT_H
+
+#include "slices.h"
+
+//typedef enum { EV_BOUNDARY, EV_TYPE } sliceEvtKind_t;
+
+typedef struct _slices_evt_t {
+ slices_t *data;
+ void (*eventListener)(/*sliceEvtKind_t evtKind,*/ struct _slices_evt_t *slicesEvt, slice_t *slice);
+ pthread_mutex_t eventListenerMutex;
+} slices_evt_t;
+
+int sliceEvtSplit(slices_evt_t *slicesEvt, slice_t *initialSlice, address_t splitAt, sliceStatus_t statusBefore, sliceStatus_t statusAt, sliceStatus_t statusAfter);
+
+//void sliceEvtPutEvent(slices_evt_t *slicesEvt, slice_t *modifiedSlice);
+
+#endif /*SLICES_EVT_H */
+
diff --git a/inc/utils.h b/inc/utils.h
new file mode 100755
index 0000000..8492179
--- /dev/null
+++ b/inc/utils.h
@@ -0,0 +1,17 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <panel.h>
+#include "slices.h"
+
+struct progArgs {
+ char *src, *dst, *ddOpts, *dump;
+ address_t beginSector, endSector, blockSize;
+};
+
+int parseArgs(int argc, char **argv, struct progArgs *args);
+void usage(char *progname);
+int cursesInit(WINDOW *wins[], PANEL *panels[], int count);
+void cursesUnInit(WINDOW *wins[], PANEL *panels[], int count);
+
+#endif /*UTILS_H*/
diff --git a/src/cursesview.c b/src/cursesview.c
new file mode 100644
index 0000000..fcfa2d2
--- /dev/null
+++ b/src/cursesview.c
@@ -0,0 +1,291 @@
+#include "cursesview.h"
+
+#include <pthread.h>
+#include <curses.h>
+#include <panel.h>
+#include <string.h>
+
+#define CURSESWIN_COUNT 3
+
+// Global variable shared by all threads to say "finish current operation and go away"
+extern int end;
+
+// window updated by cursesUpdateSliceDump callback from "worker" thread, with a mutex for prevent fuzzy concurrent updates
+WINDOW *winUpdateSliceDump=NULL;
+static pthread_mutex_t ncursesWriteMutex = PTHREAD_MUTEX_INITIALIZER;
+address_t sliceDumpBegin, sliceDumpEnd, sliceDumpMin, sliceDumpMax;
+
+// Helpers declaration (below the interesting code of this file)
+int cursesInit(WINDOW *wins[], PANEL *panels[], int count);
+void cursesUnInit(WINDOW *wins[], PANEL *panels[], int count);
+void cursesUpdateSliceDump(slices_evt_t *slicesEvt, slice_t *modifiedSlice);
+//void printInMiddle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
+void makeWin(WINDOW **win, PANEL **panel, int h, int w, int y, int x, char title[]);
+
+void cursesMainLoop(slices_evt_t *slicesEvt) {
+ char msg[255];
+ int ch, i;
+
+ WINDOW *wins[CURSESWIN_COUNT];
+ PANEL *panels[CURSESWIN_COUNT];
+
+#ifdef NCURSES_MOUSE_VERSION
+ int dispatched, res;
+ int (*mouseEventListener)(MEVENT mevent, WINDOW *winDebug);
+ MEVENT mevent;
+ PANEL *p;
+#endif
+
+ cursesInit(wins, panels, CURSESWIN_COUNT);
+
+ sliceDumpBegin=sliceDumpMin=slicesEvt->data->min;
+ sliceDumpEnd=sliceDumpMax=slicesEvt->data->max;
+
+ wattron(wins[CURSESWIN_COUNT-1], COLOR_PAIR(4));
+ mvwprintw(wins[CURSESWIN_COUNT-1], 1, 0, "F2:Exit F5:Zoom left F6:Unzoom left F7:Unzoom right F8:Zoom right");
+// wattroff(wins[CURSESWIN_COUNT-1], COLOR_PAIR(4));
+
+// update_panels();
+ doupdate();
+
+ /* Enable worker listener */
+ //FIXME : check pointers ?
+ winUpdateSliceDump=wins[1];
+ slicesEvt->eventListener=cursesUpdateSliceDump;
+
+ while((ch = getch()) != KEY_F(2)) {
+
+ pthread_mutex_lock(&ncursesWriteMutex);
+
+ switch(ch) {
+ // Zoom handling
+ //FIXME : Do something less stupid. Try to reuse mouse event zoom code for keyboard
+ case KEY_F(5):
+ sliceDumpEnd=(sliceDumpBegin+sliceDumpEnd)/2;
+ break;
+ case KEY_F(6):
+ sliceDumpBegin=sliceDumpMin;
+ break;
+ case KEY_F(7):
+ sliceDumpEnd=sliceDumpMax;
+ break;
+ case KEY_F(8):
+ sliceDumpBegin=(sliceDumpBegin+sliceDumpEnd)/2;
+ break;
+
+ // Panel focus
+ case '1':
+ case '2':
+ case '3':
+ i=ch-'0';
+ if (i>0 && i<=CURSESWIN_COUNT) {
+ top_panel(panels[i-1]);
+ update_panels();
+ }
+ break;
+#ifdef NCURSES_MOUSE_VERSION
+ case KEY_MOUSE:
+ // Seems to have a mouse event
+ res = getmouse(&mevent);
+ if ( res == OK ) {
+ // Try to find in which panel (search first in top-level panel and go down)
+ p=NULL;
+ while ( ( p=panel_below(p) ) != NULL) {
+ if ( wenclose(panel_window(p), mevent.y, mevent.x) ) {
+ break;
+ }
+ }
+
+ // If we found a panel, dispatch the event if a listener is set
+ if (p != NULL) {
+ mouseEventListener=(int (*)(MEVENT mevent, WINDOW *winDebug)) panel_userptr(p);
+ dispatched=(mouseEventListener==NULL)?0:mouseEventListener(mevent, wins[0]);
+ if ( ! dispatched ) {
+ // If no listener or event not consumed, use the default behavior : set the panel on top if BUTTON1_CLICKED
+ if ( (mevent.bstate & BUTTON1_CLICKED) == BUTTON1_CLICKED) {
+ top_panel(p);
+ }
+ }
+ }
+ }
+ break;
+#endif
+ default:
+ sprintf(msg, "Unhandled key ch==0x%x", ch);
+ wattron(wins[0], COLOR_PAIR(4));
+ mvwprintw(wins[0], 2, 0, msg);
+
+ }
+
+ doupdate();
+
+ sprintf(msg, "Viewing [%lli-%lli] of [%lli-%lli] working region ", sliceDumpBegin, sliceDumpEnd, slicesEvt->data->min, slicesEvt->data->max);
+ wattron(wins[0], COLOR_PAIR(4));
+ mvwprintw(wins[0], 1, 0, msg);
+
+ pthread_mutex_unlock(&ncursesWriteMutex);
+ }
+
+ pthread_mutex_lock(&(slicesEvt->eventListenerMutex));
+ slicesEvt->eventListener=NULL;
+ pthread_mutex_unlock(&(slicesEvt->eventListenerMutex));
+
+ end=1;
+ cursesUnInit(wins, panels, CURSESWIN_COUNT);
+}
+
+// TODO : faire une structure avec tous les éléments graphiques utiles au listener et tout passer par référence de la mainloop à ce listener
+#define DEFAULT_ZOOM_FACTOR 2.f
+
+int winSlicesMouseEventListener(MEVENT mevent, WINDOW *winDebug) {
+ bool resb;
+ int pX, pY, maxX, maxY;
+ address_t delta;
+ float pos;
+ // For debugging char buf[255];
+
+ pY=mevent.y;
+ pX=mevent.x;
+ resb=wmouse_trafo(winUpdateSliceDump, &pY, &pX, false);
+ if ( resb == TRUE ) {
+ getmaxyx(winUpdateSliceDump, maxY, maxX);
+ // First ligne (pY) is line 1 but first char (pX) is char 0 so we have to translate Y coords by -1
+ pos=(0.0f+(pY-1)*maxX+pX)/((maxY-1)*maxX-1);
+ delta=sliceDumpEnd-sliceDumpBegin;
+ sliceDumpBegin+=delta*pos/DEFAULT_ZOOM_FACTOR*(DEFAULT_ZOOM_FACTOR-1);
+ sliceDumpEnd-=delta*(1-pos)/DEFAULT_ZOOM_FACTOR*(DEFAULT_ZOOM_FACTOR-1);
+ }
+
+/*
+ // Debug code
+ sprintf(buf, "Debug : pX==%i, pY==%i, maxX==%i, maxY==%i, pos==%f, sliceDumpEnd==%lli, sliceDumpBegin=%lli, delta==%lli", pX, pY, maxX, maxY, pos, sliceDumpEnd, sliceDumpBegin, delta);
+ wattron(winDebug, COLOR_PAIR(4));
+ mvwprintw(winDebug, 2, 0, buf);
+*/
+ return 1;
+}
+
+int cursesInit(WINDOW *wins[], PANEL *panels[], int count) {
+ int screenH, screenW;
+#ifdef NCURSES_MOUSE_VERSION
+ char buf[255];
+ mmask_t mmask;
+#endif
+
+ /* Initialize curses */
+ initscr();
+ start_color();
+ raw();
+ keypad(stdscr, TRUE);
+ noecho();
+#ifdef NCURSES_MOUSE_VERSION
+ mmask = mousemask(REPORT_MOUSE_POSITION|BUTTON1_CLICKED, NULL);
+#endif
+
+ /* Initialize all the colors */
+ init_pair(1, COLOR_WHITE, COLOR_BLACK);
+ init_pair(2, COLOR_WHITE, COLOR_BLUE);
+ init_pair(3, COLOR_BLUE, COLOR_BLACK);
+ init_pair(4, COLOR_CYAN, COLOR_BLACK);
+
+ /* Initialize windows and panels */
+ getmaxyx(stdscr, screenH, screenW);
+ if ( screenH < 8 || screenW < 40 ) return 1;
+
+ makeWin(wins+0, panels+0, 3 , screenW, 0 , 0, "Menu");
+ makeWin(wins+1, panels+1, screenH-6 , screenW, 3 , 0, "Main Win");
+ makeWin(wins+2, panels+2, 2 , screenW, screenH-3 , 0, "Commands");
+
+ /* Set up the user pointers to the next panel
+ set_panel_userptr(panels[0], panels[1]);
+ set_panel_userptr(panels[1], panels[2]);
+ set_panel_userptr(panels[2], panels[0]);
+ */
+
+ set_panel_userptr(panels[1], winSlicesMouseEventListener);
+
+#ifdef NCURSES_MOUSE_VERSION
+ sprintf(buf, "Debug infos : mmask=%lx", mmask);
+ mvprintw(LINES - 3, 0, buf);
+#endif
+
+ /* Update the stacking order. 2nd panel will be on top */
+ update_panels();
+
+ return 0;
+}
+
+void cursesUnInit(WINDOW *wins[], PANEL *panels[], int count) {
+ int i;
+
+ for (i=0;i<count;i++) {
+ del_panel(panels[i]);
+ delwin(wins[i]);
+ }
+ endwin();
+}
+
+void cursesUpdateSliceDump(slices_evt_t *slicesEvt, slice_t *modifiedSlice) {
+// char *strProgress="|/-\\";
+// static int progress=0;
+ char *toPrint;
+ address_t blockSize=0;
+ unsigned int charCount=(getmaxx(winUpdateSliceDump)-getbegx(winUpdateSliceDump))*(getmaxy(winUpdateSliceDump)-getbegy(winUpdateSliceDump)+2);
+
+ //TODO : refesh only right parts of the representation
+ // This need a representation that not depends on what is already drawn because of consistency and because it not possible to re-read printed ASCII representation in curse windows
+
+ pthread_mutex_lock(&ncursesWriteMutex);
+
+
+ toPrint=slicesDump(slicesEvt->data, &blockSize, charCount, sliceDumpBegin, sliceDumpEnd);
+ if (toPrint != NULL) {
+ attron(COLOR_PAIR(4));
+ mvwprintw(winUpdateSliceDump, 1, 0, toPrint);
+// attroff(COLOR_PAIR(4));
+
+ update_panels();
+ doupdate();
+
+ free(toPrint);
+ }
+
+/* sprintf(toPrint, "%c - %p %p", strProgress[progress], slicesEvt, modifiedSlice);
+ progress=(progress+1)%strlen(strProgress);
+*/
+ pthread_mutex_unlock(&ncursesWriteMutex);
+}
+
+/*
+void printInMiddle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
+{ int length, x, y;
+ float temp;
+
+ if(win == NULL)
+ win = stdscr;
+ getyx(win, y, x);
+ if(startx != 0)
+ x = startx;
+ if(starty != 0)
+ y = starty;
+ if(width == 0)
+ width = 80;
+
+ length = strlen(string);
+ temp = (width - length)/ 2;
+ x = startx + (int)temp;
+ wattron(win, color);
+ mvwprintw(win, y, x, "%s", string);
+ wattroff(win, color);
+ refresh();
+}
+*/
+void makeWin(WINDOW **win, PANEL **panel, int h, int w, int y, int x, char title[]) {
+ int i;
+ *win = newwin(h, w, y, x);
+ mvwprintw(*win, 0, 0, "%s", title);
+ mvwchgat(*win, 0, 0, -1, A_BOLD, 2, NULL);
+ for(i=1;i<h;i++) mvwchgat(*win, i, 0, -1, A_STANDOUT, 1, NULL);
+ *panel = new_panel(*win);
+}
+
diff --git a/src/ddhardrescue.c b/src/ddhardrescue.c
new file mode 100644
index 0000000..c83fffd
--- /dev/null
+++ b/src/ddhardrescue.c
@@ -0,0 +1,133 @@
+#include <signal.h>
+#include <pthread.h>
+#include <string.h> /* For memset() */
+
+#include "slices_evt.h"
+#include "utils.h"
+#include "recover.h"
+#include "cursesview.h"
+
+// Global variable shared by all threads to say "finish current operation and go away"
+int end=0;
+
+void sigHookAbrt() {
+ end=1;
+}
+
+// Main thread procedures declaration and a struct for passing arguments in a clean way
+void *procWorker(void *a);
+void *procViewer(void *a);
+
+struct threadArgs {
+ struct progArgs *progArgs;
+ slices_evt_t *slices;
+};
+
+int main(int argc, char **argv) {
+ // System structures
+ struct sigaction sa;
+ pthread_t tWorker;
+
+ // Main data structure
+ slices_evt_t slices;
+
+ // Progam and threads arguments
+ struct progArgs args;
+ struct threadArgs tArgs;
+
+ // Algorithmic needs
+ int res;
+
+ // Parse command-line arguments
+ res=parseArgs(argc, argv, &args);
+ if (res!=0) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ // Set signals behavior
+ memset(&sa,0,sizeof(sa));
+ sa.sa_handler=sigHookAbrt;
+ res=sigaction(SIGABRT, &sa, NULL);
+ if (res!=0) {
+ return 2;
+ }
+
+ //XXX Remove srand : only for simulation
+ srand(4);
+
+ // Initialize main data structure
+ //XXX provide a method to do that ?
+ memset(&slices, 0, sizeof(slices));
+ slices.data=slicesNewSingleton(args.beginSector, args.endSector, S_UNKNOWN);
+ if ( slices.data == NULL ) {
+ return 3;
+ }
+ slices.data->min=args.beginSector;
+ slices.data->max=args.endSector;
+ res=pthread_mutex_init(&(slices.eventListenerMutex), NULL);
+ if (res!=0) {
+ return 4;
+ }
+
+ // Threads preparation, creation and start
+ memset(&tArgs, 0, sizeof(tArgs));
+ tArgs.progArgs=&args;
+ tArgs.slices=&slices;
+
+
+ res=pthread_create(&tWorker, NULL, procWorker, &tArgs);
+ if (res!=0) {
+ return 5;
+ }
+
+ // Ncurses interface run in the main thread
+ (void) procViewer((void*)&tArgs);
+
+ // Thread join point (wait worker thread when viewer is done)
+ (void) pthread_join(tWorker, NULL);
+
+ //Final dump of datas
+ address_t blockSize=0;
+ char *dump;
+ dump=slicesDump(slices.data, &blockSize, 10000, args.beginSector, args.endSector);
+ if (dump != NULL) {
+ puts(dump);
+ free(dump);
+ }
+ printf("blockSize==%lld\n", blockSize);
+ printf("slices->count==%d\n", slices.data->count);
+
+
+ //Resources desallocation
+ (void) pthread_mutex_destroy(&(slices.eventListenerMutex));
+ slicesDelete(slices.data);
+
+ return 0;
+}
+
+void *procWorker(void *a) {
+ struct threadArgs *tArgs = (struct threadArgs *)a;
+
+ //XXX : We will need something more controlable than just a blocking call to the main algorithm
+ recover(
+ tArgs->slices,
+ tArgs->progArgs->src,
+ tArgs->progArgs->dst,
+ tArgs->progArgs->ddOpts
+ );
+
+ return a;
+}
+
+
+void *procViewer(void *a) {
+ struct threadArgs *tArgs = (struct threadArgs *)a;
+
+ cursesMainLoop(
+ tArgs->slices
+ );
+
+ return a;
+}
+
diff --git a/src/essais/compil.sh b/src/essais/compil.sh
new file mode 100755
index 0000000..a867c5a
--- /dev/null
+++ b/src/essais/compil.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+gcc -Wall -o ../../bin/test -g -lncurses test.c
+gcc -Wall -o ../../bin/test2 -g -lncurses -lpanel test2.c
+gcc -Wall -o ../../bin/test3 -g -lncurses -lpanel test3.c
diff --git a/src/essais/test.c b/src/essais/test.c
new file mode 100644
index 0000000..3a3e73f
--- /dev/null
+++ b/src/essais/test.c
@@ -0,0 +1,86 @@
+#include <ncurses.h>
+
+WINDOW *create_newwin(int height, int width, int starty, int startx)
+{ WINDOW *local_win;
+
+ local_win = newwin(height, width, starty, startx);
+ wborder(local_win, '|', '|', '-', '-', '+', '+', '+', '+');
+// box(local_win,0,0);
+ wrefresh(local_win);
+ return local_win;
+}
+
+int main() {
+
+/*
+ A_NORMAL Normal display (no highlight)
+ A_STANDOUT Best highlighting mode of the terminal.
+ A_UNDERLINE Underlining
+ A_REVERSE Reverse video
+ A_BLINK Blinking
+ A_DIM Half bright
+ A_BOLD Extra bright or bold
+ A_PROTECT Protected mode
+ A_INVIS Invisible or blank mode
+ A_ALTCHARSET Alternate character set
+ A_CHARTEXT Bit-mask to extract a character
+ COLOR_PAIR(n) Color-pair number n
+*/
+ int end, ch;
+ int row,col;
+ char msg[256];
+ WINDOW *w1;
+ MEVENT event;
+
+ initscr();
+ getmaxyx(stdscr,row,col);
+ raw();
+ keypad(stdscr, TRUE);
+ noecho();
+
+ start_color();
+ init_pair(1, COLOR_WHITE, COLOR_BLUE);
+
+ mousemask(ALL_MOUSE_EVENTS, NULL);
+//http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/windows.html
+ //w1 = create_newwin(row/2, col/2, row/4, col/4);
+ w1 = create_newwin(10, 10, 10, 10);
+ attron(COLOR_PAIR(1) | A_BOLD);
+ mvprintw(row-2,0,"This screen has %d rows and %d columns\n",row,col);
+ attroff(COLOR_PAIR(1) | A_BOLD);
+ move(0,0);
+
+
+ end=0;
+ while (!end) {
+ refresh();
+ ch=getch();
+ switch(ch) {
+ case KEY_MOUSE:
+ if(getmouse(&event) == OK) { /* When the user clicks left mouse button */
+ if(event.bstate & BUTTON1_PRESSED) {
+ attrset(A_NORMAL);
+ printw("mouse button1\n");
+ }
+ }
+ break;
+ case KEY_F(2):
+ attrset(A_NORMAL);
+ printw("F2 key\n");
+ break;
+ case 'q':
+ end=1;
+ break;
+ case 'b':
+ mvwchgat(w1,1,0,-1, A_REVERSE, 0, NULL);
+ break;
+ default:
+ sprintf(msg, "%c key\n", ch);
+ wattrset(w1,A_BOLD | A_UNDERLINE);
+ wprintw(w1, msg);
+ }
+ }
+ endwin();
+
+ return 0;
+}
diff --git a/src/essais/test2.c b/src/essais/test2.c
new file mode 100644
index 0000000..3512329
--- /dev/null
+++ b/src/essais/test2.c
@@ -0,0 +1,121 @@
+
+#include <string.h>
+#include <panel.h>
+
+#define NLINES 10
+#define NCOLS 40
+
+void init_wins(WINDOW **wins, int n);
+void win_show(WINDOW *win, char *label, int label_color);
+void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
+
+int main()
+{ WINDOW *my_wins[3];
+ PANEL *my_panels[3];
+ PANEL *top;
+ int ch;
+
+ /* Initialize curses */
+ initscr();
+ start_color();
+ cbreak();
+ noecho();
+ keypad(stdscr, TRUE);
+
+ /* Initialize all the colors */
+ init_pair(1, COLOR_RED, COLOR_BLACK);
+ init_pair(2, COLOR_GREEN, COLOR_BLACK);
+ init_pair(3, COLOR_BLUE, COLOR_BLACK);
+ init_pair(4, COLOR_CYAN, COLOR_BLACK);
+
+ init_wins(my_wins, 3);
+
+ /* Attach a panel to each window */ /* Order is bottom up */
+ my_panels[0] = new_panel(my_wins[0]); /* Push 0, order: stdscr-0 */
+ my_panels[1] = new_panel(my_wins[1]); /* Push 1, order: stdscr-0-1 */
+ my_panels[2] = new_panel(my_wins[2]); /* Push 2, order: stdscr-0-1-2 */
+
+ /* Set up the user pointers to the next panel */
+ set_panel_userptr(my_panels[0], my_panels[1]);
+ set_panel_userptr(my_panels[1], my_panels[2]);
+ set_panel_userptr(my_panels[2], my_panels[0]);
+
+ /* Update the stacking order. 2nd panel will be on top */
+ update_panels();
+
+ /* Show it on the screen */
+ attron(COLOR_PAIR(4));
+ mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F2 to Exit)");
+ attroff(COLOR_PAIR(4));
+ doupdate();
+
+ top = my_panels[2];
+ while((ch = getch()) != KEY_F(2))
+ { switch(ch)
+ { case 9:
+ top = (PANEL *)panel_userptr(top);
+ top_panel(top);
+ break;
+ }
+ update_panels();
+ doupdate();
+ }
+ endwin();
+ return 0;
+}
+
+/* Put all the windows */
+void init_wins(WINDOW **wins, int n)
+{
+ int x, y, i;
+ char label[80];
+
+ y = 2;
+ x = 10;
+ for(i = 0; i < n; ++i)
+ { wins[i] = newwin(NLINES, NCOLS, y, x);
+ sprintf(label, "Window Number %d", i + 1);
+ win_show(wins[i], label, i + 1);
+ y += 3;
+ x += 7;
+ }
+}
+
+/* Show the window with a border and a label */
+void win_show(WINDOW *win, char *label, int label_color)
+{ int startx, starty, height, width;
+
+ getbegyx(win, starty, startx);
+ getmaxyx(win, height, width);
+
+ box(win, 0, 0);
+ mvwaddch(win, 2, 0, ACS_LTEE);
+ mvwhline(win, 2, 1, ACS_HLINE, width - 2);
+ mvwaddch(win, 2, width - 1, ACS_RTEE);
+
+ print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color));
+}
+
+void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
+{ int length, x, y;
+ float temp;
+
+ if(win == NULL)
+ win = stdscr;
+ getyx(win, y, x);
+ if(startx != 0)
+ x = startx;
+ if(starty != 0)
+ y = starty;
+ if(width == 0)
+ width = 80;
+
+ length = strlen(string);
+ temp = (width - length)/ 2;
+ x = startx + (int)temp;
+ wattron(win, color);
+ mvwprintw(win, y, x, "%s", string);
+ wattroff(win, color);
+ refresh();
+}
+
diff --git a/src/essais/test3.c b/src/essais/test3.c
new file mode 100644
index 0000000..c763cf3
--- /dev/null
+++ b/src/essais/test3.c
@@ -0,0 +1,169 @@
+
+#include <string.h>
+#include <panel.h>
+
+#define NLINES 10
+#define NCOLS 40
+
+void init_wins(WINDOW **wins, int n);
+void win_show(WINDOW *win, char *label, int label_color);
+void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);
+
+int main()
+{ WINDOW *my_wins[3];
+ PANEL *my_panels[3];
+ PANEL *top, *p, *pe;
+ int ch, res;
+ mmask_t mmask;
+ MEVENT mevent;
+ char buf[255];
+
+ /* Initialize curses */
+ initscr();
+ start_color();
+ cbreak();
+ noecho();
+ keypad(stdscr, TRUE);
+ mmask = mousemask(REPORT_MOUSE_POSITION|BUTTON1_CLICKED, NULL);
+
+ /* Initialize all the colors */
+ init_pair(1, COLOR_RED, COLOR_BLACK);
+ init_pair(2, COLOR_GREEN, COLOR_BLACK);
+ init_pair(3, COLOR_BLUE, COLOR_BLACK);
+ init_pair(4, COLOR_CYAN, COLOR_BLACK);
+
+ init_wins(my_wins, 3);
+
+ /* Attach a panel to each window */ /* Order is bottom up */
+ my_panels[0] = new_panel(my_wins[0]); /* Push 0, order: stdscr-0 */
+ my_panels[1] = new_panel(my_wins[1]); /* Push 1, order: stdscr-0-1 */
+ my_panels[2] = new_panel(my_wins[2]); /* Push 2, order: stdscr-0-1-2 */
+
+ /* Set up the user pointers to the next panel */
+ set_panel_userptr(my_panels[0], my_panels[1]);
+ set_panel_userptr(my_panels[1], my_panels[2]);
+ set_panel_userptr(my_panels[2], my_panels[0]);
+
+ /* Update the stacking order. 2nd panel will be on top */
+ update_panels();
+
+ /* Show it on the screen */
+ attron(COLOR_PAIR(4));
+ mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F2 to Exit)");
+ sprintf(buf, "Debug infos : mmask=%lx", mmask);
+ mvprintw(LINES - 3, 0, buf);
+ attroff(COLOR_PAIR(4));
+ doupdate();
+
+ top = my_panels[2];
+ while((ch = getch()) != KEY_F(2))
+ { switch(ch)
+ { case 9: // This is tab key, but ive not found the right symbol... other that '\t'
+ top = (PANEL *)panel_userptr(top);
+ top_panel(top);
+ break;
+ case KEY_MOUSE:
+ // Seems to have a mouse event
+ res = getmouse(&mevent);
+ if ( res == OK ) {
+ // Try to find in which panel (search first in top-level panel and go down)
+ pe=NULL;
+ p=top;
+ do {
+ if ((p == NULL) || wenclose(panel_window(p), mevent.y, mevent.x) ) {
+ pe = p;
+ break;
+ }
+ p = (PANEL *)panel_userptr(top);
+ } while (p != top);
+
+ // If we found a panel
+ if (pe != NULL) {
+ // "switch" depending on event kind
+ if ( (mevent.bstate & BUTTON1_CLICKED) == BUTTON1_CLICKED) {
+ // A panel was clicked, set that panel on top
+ top = pe;
+ top_panel(top);
+ // FIXME : the userptr chain of panel must reflect panel order !
+ // Or may be we can crawl panels by order with ncurses method
+ }
+ }
+/*
+ if ( (mevent.bstate & BUTTON1_CLICKED) == BUTTON1_CLICKED) {
+ attron(COLOR_PAIR(4));
+ sprintf(buf, "BUTTON1_CLICKED at %i/%i/%i ", mevent.x, mevent.y, mevent.z);
+ mvprintw(LINES - 4, 0, buf);
+ attroff(COLOR_PAIR(4));
+
+ }
+ */
+ }
+ break;
+ default:
+ attron(COLOR_PAIR(4));
+ sprintf(buf, "Unknown key : 0x%x ", ch);
+ mvprintw(LINES - 3, 0, buf);
+ attroff(COLOR_PAIR(4));
+ }
+ update_panels();
+ doupdate();
+ }
+ endwin();
+ return 0;
+}
+
+/* Put all the windows */
+void init_wins(WINDOW **wins, int n)
+{
+ int x, y, i;
+ char label[80];
+
+ y = 2;
+ x = 10;
+ for(i = 0; i < n; ++i)
+ { wins[i] = newwin(NLINES, NCOLS, y, x);
+ sprintf(label, "Window Number %d", i + 1);
+ win_show(wins[i], label, i + 1);
+ y += 3;
+ x += 7;
+ }
+}
+
+/* Show the window with a border and a label */
+void win_show(WINDOW *win, char *label, int label_color)
+{ int startx, starty, height, width;
+
+ getbegyx(win, starty, startx);
+ getmaxyx(win, height, width);
+
+ box(win, 0, 0);
+ mvwaddch(win, 2, 0, ACS_LTEE);
+ mvwhline(win, 2, 1, ACS_HLINE, width - 2);
+ mvwaddch(win, 2, width - 1, ACS_RTEE);
+
+ print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color));
+}
+
+void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)
+{ int length, x, y;
+ float temp;
+
+ if(win == NULL)
+ win = stdscr;
+ getyx(win, y, x);
+ if(startx != 0)
+ x = startx;
+ if(starty != 0)
+ y = starty;
+ if(width == 0)
+ width = 80;
+
+ length = strlen(string);
+ temp = (width - length)/ 2;
+ x = startx + (int)temp;
+ wattron(win, color);
+ mvwprintw(win, y, x, "%s", string);
+ wattroff(win, color);
+ refresh();
+}
+
diff --git a/src/recover.c b/src/recover.c
new file mode 100644
index 0000000..2131542
--- /dev/null
+++ b/src/recover.c
@@ -0,0 +1,128 @@
+#include "recover.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h> /* for usleep - to be removed */
+
+// Main algorithm for recover datas
+void recover(slices_evt_t *slicesEvt, char *src, char *dst, char *ddOpts) {
+ slice_t *sliceToRead;
+ address_t firstError=0, median, foundMax=0;
+ int res;
+
+ //sliceToRead=slicesEvt->data->first;
+ sliceToRead=slicesFindLargest(slicesEvt->data, S_UNKNOWN);
+ while (!end) {
+ // try to recover sliceToRead and split it if read error
+ switch ( tryRecoverUntilError(sliceToRead, &firstError, src, dst, ddOpts) ) {
+ case 0:
+ // slice recovery has been executed without read error
+ sliceToRead->status=S_RECOVERED;
+ break;
+ case EIO:
+ // slice recovery has encountered a readerror
+ res=sliceEvtSplit(slicesEvt, sliceToRead, firstError, S_RECOVERED, S_UNREADABLE, S_UNKNOWN);
+ if (res<1) {
+ //TODO
+ printf("sliceEvtSplit return %d\n", res);
+ exit(5);
+ }
+ break;
+ default:
+ exit(2); //TODO
+ }
+
+ /* Now, search the largest S_UNKNOWN zone
+ split it in two parts */
+ //sliceToRead=slicesFindLargest(slices, S_UNKNOWN);
+ sliceToRead=slicesFindLargestFast(slicesEvt->data, &foundMax, S_UNKNOWN, foundMax, sliceToRead->next);
+ if ( sliceToRead == NULL ) {
+ // There is nothing more to recover, bailout
+ end=1;
+ continue;
+ }
+
+
+ median=(sliceToRead->begin+sliceToRead->end)/2;
+ res=sliceEvtSplit(slicesEvt, sliceToRead, median, S_UNKNOWN, S_UNKNOWN, S_UNKNOWN);
+ switch (res) {
+ case 1:
+ // No split, try analyse this zone
+ // Should be a slice of length 1
+ break;
+ case 2:
+ /* After splitting an S_UNKNOWN zone in two parts
+ take the second for further analysis.
+ We already now that this first one is just preceded by
+ a read error, and errors are frequently grouped in zones,
+ so trying to read a sector just after a faulty sector is
+ most likely a waste of time.
+ */
+ sliceToRead=sliceToRead->next;
+ break;
+ case 3:
+ // Internal error of sliceSlpit because this set of parameters prevent split by 3
+ exit(6); // TODO
+ break;
+ case -1:
+ // Memory error
+ exit(5); //TODO
+ break;
+ default:
+ // API error, all necessary cases are already listed
+ exit(7); // TODO
+ }
+ }
+}
+
+// Method tha read source (and clone to dest) until the first read error
+int tryRecoverUntilError(slice_t *sliceToRead, address_t *firstError, char *src, char *dst, char*ddOpts) {
+ //TODO : implement realy that
+ //TODO : bail out hardly if WRITE error (on dest)
+
+// char ddinvocation[256];
+ int res;
+ address_t seek, count;
+
+ seek=sliceToRead->begin;
+ count=sliceToRead->end - seek + 1;
+// res=snprintf(ddinvocation, 255, "dd %s %s %s seek=%lld skip=%lld count=%lld", src, dst, ddOpts, seek, seek, count);
+//TODO : listener to put that info on the interface puts(ddinvocation);
+
+/*
+ // Simulate that we have systematically a read error at first sector
+ *firstError=sliceToRead->begin;
+ res=EIO;
+*/
+/*
+ // Simulate that we have systematically a read error at last sector
+ *firstError=sliceToRead->end;
+ res=EIO;
+*/
+/*
+ // Simulate that we have systematically a read error at first sector
+ // Simulate for each read, tha we have an error just in the middle if read for mor than one sector
+ if ( sliceToRead->begin == sliceToRead->end ) {
+ res=0;
+ } else {
+ *firstError=(sliceToRead->begin + sliceToRead->end)/2;
+ res=EIO;
+ }
+*/
+
+ // Simulate for each read a pseudo random error position and generate some cases of full read without error
+ //address_t error=sliceToRead->begin + rand()%(count);
+ address_t error=sliceToRead->begin + rand()%(count/3);
+ if ( error % 42 == 0 ) {
+ res=0;
+ } else {
+ res=EIO;
+ *firstError=error;
+ }
+
+ // Keep things humanly understandable (to be removed when real reads will be done)
+ //usleep(10000);
+
+ return res;
+}
+
diff --git a/src/slices.c b/src/slices.c
new file mode 100644
index 0000000..e8f1d49
--- /dev/null
+++ b/src/slices.c
@@ -0,0 +1,282 @@
+#include "slices.h"
+
+#include <string.h>
+#include <stdio.h> /* For perror() */
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+slice_t *sliceNew(address_t begin, address_t end, sliceStatus_t status, slice_t *next) {
+ slice_t *s;
+
+ s = malloc(1*sizeof(slice_t));
+ if (s!=NULL) {
+ s->begin=begin;
+ s->end=end;
+ s->status=status;
+ s->next=next;
+ }
+
+ return s;
+}
+
+void sliceDelete(slice_t *s) {
+ free(s);
+}
+
+// Return the numbers of slices after split (3 in the general case, 2 or 1 in particular cases. -1 is memory error)
+int sliceSplit(slices_t *slices, slice_t *initialSlice, address_t splitAt, sliceStatus_t statusBefore, sliceStatus_t statusAt, sliceStatus_t statusAfter) {
+ slice_t *secondSlice, *thirdSlice, *rightSlice;
+ int splitAfterSingularity, splitBeforeSingularity;
+
+ /* Basically, we want to split the slice in 3 :
+ [a;b] shoud be transformed in : [a;splitAt-1], [splitAt;splitAt], [splitAt+1;b]
+ There is exceptions and singularities :
+ * If splitAt is not within [a;b], bail out, no coherent solution
+ * If splitAt==a, the first slice should not exists
+ * If splitAt==b, the last slice shoud not exists
+ * If a==b (and so, ==splitAt), there is nothing to split, just change status
+ But, if statusBefore==statusAt, we don't want an interval [splitAt;splitAt], we want just split in 2.
+ This unwanted interval should be kept merged with the first interval.
+
+ For pratical reasons with pointer mess-up, the first action is to split between the second and the last slice
+ and then between he first and second if needed.
+ */
+ pthread_mutex_lock(&(slices->writeOrConsistentReadMutex));
+
+ if ( splitAt < initialSlice->begin || splitAt > initialSlice->end ) {
+ pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex));
+ return -2;
+ }
+
+ // Test before act because we'll change values of the initialSlice because
+ // it would become the firstSlice or even the second one if the first is zero-lenght
+ splitAfterSingularity=(splitAt != initialSlice->end);
+ splitBeforeSingularity=(splitAt != initialSlice->begin) && (statusBefore != statusAt);
+
+ if ( splitAfterSingularity ) {
+ thirdSlice = sliceNew(splitAt+1, initialSlice->end, statusAfter, initialSlice->next);
+ if ( thirdSlice == NULL ) {
+ pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex));
+ return -1;
+ }
+
+ initialSlice->end = splitAt;
+ // No status change because we'll split again in 2 parts or not
+ initialSlice->next = thirdSlice;
+ if ( initialSlice == slices->last ) slices->last = thirdSlice;
+ (slices->count)++;
+
+ rightSlice=thirdSlice;
+ } else {
+ rightSlice=initialSlice->next;
+ }
+
+ if ( splitBeforeSingularity ) {
+ secondSlice = sliceNew(splitAt, splitAt, statusAt, rightSlice);
+ if ( secondSlice == NULL ) {
+ pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex));
+ return -1;
+ }
+
+ initialSlice->end = splitAt-1;
+ initialSlice->status=statusBefore;
+ initialSlice->next = secondSlice;
+ if ( initialSlice == slices->last ) slices->last = secondSlice;
+ (slices->count)++;
+ } else {
+ initialSlice->status=statusAt; // Two cases : a==splitAt or statusAt==statusBefore
+ }
+
+ pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex));
+ return 1 + (splitBeforeSingularity?1:0) + (splitAfterSingularity?1:0);
+}
+
+void sliceDumpUpdate(char *dump, slice_t *s, address_t blockSize, unsigned int charCount, address_t begin, address_t end) {
+ address_t sb,se,i;
+ char ci;
+
+ // If "s" slice is (partially) contained/visible in the [begin,end] display interval
+ if ( !(s->end < begin) && !(s->begin > end) ) {
+ // Draw the slice on the right number of characters
+ // Mathematically correct, but crashes because address_t is UNSIGNED
+ // sb=MAX(0, (s->begin - begin) / *blockSize);
+ sb=(s->begin < begin)?0:(s->begin - begin) / blockSize;
+ se=MIN((s->end - begin) / blockSize, charCount-1);
+
+ /* Debug "assertion"
+ if (sb >= charCount || se >= charCount) {
+ printf("BUG : sb==%lli, se=%lli, charCount==%i\n", sb, se, charCount);
+ printf("BUG : MAX(0, (%lli - %lli) / %lli)", s->begin, begin, blockSize);
+ exit(42);
+ }*/
+
+ // Choose from the sent slice status the right char to draw
+ switch (s->status) {
+ case S_UNKNOWN: ci='_'; break;
+ case S_UNREADABLE: ci='!'; break;
+ case S_RECOVERED: ci='.'; break;
+ default: ci='~'; break;
+ }
+
+ // Draw on the right number of characters, paying attention with information collision
+ for (i=sb;i<=se;i++) {
+ if (dump[i] == ' ' ) {
+ // This is a new information
+ dump[i]=ci;
+ } else if ( dump[i] == ci || dump[i] == '!' ) {
+ // Already the right information or error, don't modify
+ } else {
+ // Multiple information on the same character
+ dump[i]='#';
+ }
+ }
+ }
+}
+
+slices_t *slicesNewEmpty() {
+ int res;
+ slices_t *ss = malloc(1*sizeof(slices_t));
+
+ if (ss==NULL) {
+ return NULL;
+ }
+
+ memset(ss, 0, sizeof(slices_t));
+ res=pthread_mutex_init(&(ss->writeOrConsistentReadMutex), NULL);
+ if (res!=0) {
+ free(ss);
+ return NULL;
+ }
+
+ return ss;
+}
+
+slices_t *slicesNewSingleton(address_t begin, address_t end, sliceStatus_t status) {
+ slice_t *s=NULL;
+ slices_t *ss = slicesNewEmpty();
+ if (ss==NULL) {
+ return NULL;
+ }
+ s=sliceNew(begin,end,status,NULL);
+ if (s==NULL) {
+ free(ss);
+ return NULL;
+ }
+ slicesAppend(ss,s);
+
+ return ss;
+}
+
+void slicesDelete(slices_t *ss) {
+ slice_t *curr, *toFree;
+
+ curr=ss->first;
+ while (curr!=NULL) {
+ toFree=curr;
+ curr=curr->next;
+ sliceDelete(toFree);
+ }
+ free(ss);
+}
+
+void slicesAppend(slices_t *slices, slice_t *slice) {
+ pthread_mutex_lock(&(slices->writeOrConsistentReadMutex));
+
+ slice->next=NULL; //XXX Could be generalized
+ if (slices->first==NULL || slices->last==NULL) {
+ slices->first = slice;
+ } else {
+ slices->last->next=slice;
+ }
+ slices->last=slice;
+ (slices->count)++;
+
+ pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex));
+}
+
+slice_t *slicesFindLargest(slices_t *slices, sliceStatus_t status) {
+ slice_t *curr, *sMax = NULL;
+ address_t i, iMax = 0;
+
+ pthread_mutex_lock(&(slices->writeOrConsistentReadMutex));
+
+ curr = slices->first;
+ while (curr != NULL) {
+ i=curr->end - curr->begin + 1;
+ if ( curr->status == status && i > iMax ) {
+ iMax = i;
+ sMax = curr;
+ }
+ curr=curr->next;
+ }
+
+ pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex));
+ return sMax;
+}
+
+slice_t *slicesFindLargestFast(slices_t *slices, address_t *foundMax, sliceStatus_t status, address_t knownMax, slice_t *firstToTry) {
+ slice_t *curr, *sMax = NULL;
+ address_t i, iMax = 0;
+
+//FIXME : pthread_lock à faire avant l'appel là :-s (car argument firstToTry peut pointer vers n'importe quoi si autre thread modifie
+ curr = firstToTry;
+ while (curr != NULL) {
+ i=curr->end - curr->begin + 1;
+ if ( curr->status == status ) {
+ if ( knownMax == i ) { *foundMax=i; return curr; }
+ if ( i > iMax ) {
+ iMax = i;
+ sMax = curr;
+ }
+ }
+ curr=curr->next;
+ }
+ curr = slices->first;
+ while (curr != firstToTry) {
+ i=curr->end - curr->begin + 1;
+ if ( curr->status == status && i > iMax ) {
+ iMax = i;
+ sMax = curr;
+ }
+ curr=curr->next;
+ }
+ *foundMax=iMax;
+ return sMax;
+}
+
+char *slicesDump(slices_t *slices, address_t *blockSize, unsigned int charCount, address_t begin, address_t end) {
+ int res;
+ slice_t *curr;
+ char *dump;
+
+ res=pthread_mutex_lock(&(slices->writeOrConsistentReadMutex));
+ if (res!=0) {
+ //FIXME Trashy code
+ perror("slicesDump, pb lock mutex");
+ exit(42);
+ }
+
+ // If blockSize is 0, try to autodetect to display entire slice chain
+ if (*blockSize == 0) {
+ *blockSize=(end-begin+1)/(charCount-1);
+ // If we have a too big zoom factor, draw it at 1:1 scale
+ if (*blockSize==0) *blockSize=1;
+ }
+
+ dump = malloc(charCount+1);
+ if (dump==NULL) { return NULL; }
+ memset(dump, ' ', charCount);
+ dump[charCount]=0;
+
+ //For each slice write in dump ASCII representation
+ curr = slices->first;
+ while (curr != NULL) {
+ sliceDumpUpdate(dump, curr, *blockSize, charCount, begin, end);
+ curr=curr->next;
+ }
+
+ pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex));
+ return dump;
+}
+
diff --git a/src/slices_evt.c b/src/slices_evt.c
new file mode 100644
index 0000000..4ed07c6
--- /dev/null
+++ b/src/slices_evt.c
@@ -0,0 +1,29 @@
+#include "slices_evt.h"
+
+#include <stdio.h>
+#include <pthread.h>
+
+// Event-aware version of sliceSplit (reusing it, of course)
+int sliceEvtSplit(slices_evt_t *slicesEvt, slice_t *initialSlice, address_t splitAt, sliceStatus_t statusBefore, sliceStatus_t statusAt, sliceStatus_t statusAfter) {
+ int res;
+ res=sliceSplit(slicesEvt->data, initialSlice, splitAt, statusBefore, statusAt, statusAfter);
+
+ pthread_mutex_lock(&(slicesEvt->eventListenerMutex));
+ if ( slicesEvt->eventListener != NULL ) {
+ switch(res) {
+ case 3:
+ slicesEvt->eventListener(slicesEvt, initialSlice->next->next);
+ case 2:
+ slicesEvt->eventListener(slicesEvt, initialSlice->next);
+ case 1:
+ slicesEvt->eventListener(slicesEvt, initialSlice);
+ break;
+ default:
+ // No events on split errors
+ break;
+ }
+ }
+ pthread_mutex_unlock(&(slicesEvt->eventListenerMutex));
+ return res;
+}
+
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..7305b22
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "utils.h"
+
+int parseArgs(int argc, char **argv, struct progArgs *args) {
+ //TODO : implement that
+ args->src="/dev/sdb";
+ args->dst="./test.img";
+ args->ddOpts="";
+ args->beginSector=0;
+ args->endSector=21474836480ULL; //10 Tio
+
+ return 0;
+}
+
+void usage(char *progname) {
+ printf(
+"Usage %s <src> <dst> [<beginSector> <endSector> [ddOpts]]\n\
+ <src>\t\t\n\
+ <dst>\t\t\n\
+ <beginSector>\t\n\
+ <endSector>\t\n\
+ <ddOpts>\t\n\
+", progname);
+}
+
diff --git a/todo.txt b/todo.txt
new file mode 100644
index 0000000..67f9930
--- /dev/null
+++ b/todo.txt
@@ -0,0 +1,12 @@
+
+Limitations de l'implémentation actuelle :
+
+Il n'y a pas de mise à jour incrémentale du ascii dump car le buffer de la WINDOW curse n'est pas readable et que la méthodesliceDump embarque le calcul des paramètres de combien de secteurs représente 1 char, etc...
+
+Le seul event qui remonte c'est au moment d'un sliceSplit. Ces évènements, sur un disque dur pas trop trop malade seront très infréquents.
+
+Il n'y a pas de controle des paramètres de l'algo dans l'IHM, il n'y a pas de controle de l'exécution de l'algo non plus.
+
+Il n'y a pas de sauvegarde périodique de l'état d'avancement (slices mais aussi avancement dans la lecture du splice courant).
+
+Il n'y a pas de packaging de fait.