diff options
-rw-r--r-- | Makefile | 31 | ||||
-rw-r--r-- | inc/cursesview.h | 9 | ||||
-rw-r--r-- | inc/recover.h | 11 | ||||
-rwxr-xr-x | inc/slices.h | 50 | ||||
-rw-r--r-- | inc/slices_evt.h | 19 | ||||
-rwxr-xr-x | inc/utils.h | 17 | ||||
-rw-r--r-- | src/cursesview.c | 291 | ||||
-rw-r--r-- | src/ddhardrescue.c | 133 | ||||
-rwxr-xr-x | src/essais/compil.sh | 4 | ||||
-rw-r--r-- | src/essais/test.c | 86 | ||||
-rw-r--r-- | src/essais/test2.c | 121 | ||||
-rw-r--r-- | src/essais/test3.c | 169 | ||||
-rw-r--r-- | src/recover.c | 128 | ||||
-rw-r--r-- | src/slices.c | 282 | ||||
-rw-r--r-- | src/slices_evt.c | 29 | ||||
-rw-r--r-- | src/utils.c | 27 | ||||
-rw-r--r-- | todo.txt | 12 |
17 files changed, 0 insertions, 1419 deletions
diff --git a/Makefile b/Makefile deleted file mode 100644 index fccc821..0000000 --- a/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -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 deleted file mode 100644 index f4d343a..0000000 --- a/inc/cursesview.h +++ /dev/null @@ -1,9 +0,0 @@ -#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 deleted file mode 100644 index eee8141..0000000 --- a/inc/recover.h +++ /dev/null @@ -1,11 +0,0 @@ -#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 deleted file mode 100755 index b994636..0000000 --- a/inc/slices.h +++ /dev/null @@ -1,50 +0,0 @@ -#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 deleted file mode 100644 index d5772b3..0000000 --- a/inc/slices_evt.h +++ /dev/null @@ -1,19 +0,0 @@ -#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 deleted file mode 100755 index 8492179..0000000 --- a/inc/utils.h +++ /dev/null @@ -1,17 +0,0 @@ -#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 deleted file mode 100644 index fcfa2d2..0000000 --- a/src/cursesview.c +++ /dev/null @@ -1,291 +0,0 @@ -#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 deleted file mode 100644 index c83fffd..0000000 --- a/src/ddhardrescue.c +++ /dev/null @@ -1,133 +0,0 @@ -#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 deleted file mode 100755 index a867c5a..0000000 --- a/src/essais/compil.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/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 deleted file mode 100644 index 3a3e73f..0000000 --- a/src/essais/test.c +++ /dev/null @@ -1,86 +0,0 @@ -#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 deleted file mode 100644 index 3512329..0000000 --- a/src/essais/test2.c +++ /dev/null @@ -1,121 +0,0 @@ - -#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 deleted file mode 100644 index c763cf3..0000000 --- a/src/essais/test3.c +++ /dev/null @@ -1,169 +0,0 @@ - -#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 deleted file mode 100644 index 2131542..0000000 --- a/src/recover.c +++ /dev/null @@ -1,128 +0,0 @@ -#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 deleted file mode 100644 index e8f1d49..0000000 --- a/src/slices.c +++ /dev/null @@ -1,282 +0,0 @@ -#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 deleted file mode 100644 index 4ed07c6..0000000 --- a/src/slices_evt.c +++ /dev/null @@ -1,29 +0,0 @@ -#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 deleted file mode 100644 index 7305b22..0000000 --- a/src/utils.c +++ /dev/null @@ -1,27 +0,0 @@ -#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 deleted file mode 100644 index 67f9930..0000000 --- a/todo.txt +++ /dev/null @@ -1,12 +0,0 @@ - -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. |