Anonymous avatar Anonymous committed 5cd27c4

Implement some kind of a fix for a major bug possibly leading to ruining of partitions. I hope it works this way. Also implemented help display and updated exception handler display. (fdisk@sv.gnu.org/fdisk--main--0--patch-17)
fdisk@sv.gnu.org/fdisk--main--0--patch-17
Keywords:

Comments (0)

Files changed (4)

 .B -z, --new-table
 create a new partition table on the disk. This is useful if you want to change the partition table type or want to repartition you entire drive. Note that this does not delete the old table on the disk until you commit the changes.
 .TP
-.B -u, --units \fIunit\fP
-sets the default display units to \fIunit\fP. A list of possible units is given below.
+.B -u, --units=\fIUNIT\fP
+sets the default display units to \fIUNIT\fP. A list of possible units is given below.
 .TP
 .B -l, --list-partition-types
 displays a list of supported partition types and features.
 
 
 
-#define K	1000
+
+static const char* prog_name = "cfdisk (GNU fdisk) " VERSION "\n";
+static const char* license_msg = N_(
+	"Copyright (C) 2006 Free Software Foundation, Inc.\n"
+	"This is free software.  You may redistribute copies of it under the terms of\n"
+	"the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"
+	"There is NO WARRANTY, to the extent permitted by law.\n"
+);
+static const char* usage_msg = N_(
+	"Usage: cfdisk [OPTION]... [DEVICE]\n"
+);
+
+static struct { const char* lopt; const char opt; const char *arg; const char *help; } options_help[] = 
+{
+	{"help",	'h', NULL,	N_("displays this help message")},
+	{"version",	'v', NULL,	N_("displays the version")},
+	{"new-table",	'z', NULL,	N_("create a new partition table on the disk")},
+	{"units",	'u', N_("UNIT"),N_("sets the default display units to UNIT")},
+	{"list-partition-types", 'l', NULL, N_("displays a list of supported partition types")},
+        {NULL, 0, NULL, NULL}
+};
+
+
+static const char *help = N_(
+     "Join us now and share the software;\n"
+     "You'll be free, hackers, you'll be free.\n"
+     "Join us now and share the software;\n"
+     "You'll be free, hackers, you'll be free.\n"
+     "\n"
+     "Hoarders may get piles of money,\n"
+     "That is true, hackers, that is true.\n"
+     "But they cannot help their neighbors;\n"
+     "That's not good, hackers, that's not good.\n"
+     "\n"
+     "When we have enough free software\n"
+     "At our call, hackers, at our call,\n"
+     "We'll throw out those dirty licenses\n"
+     "Ever more, hackers, ever more.\n"
+     "\n"
+     "Join us now and share the software;\n"
+     "You'll be free, hackers, you'll be free.\n"
+     "Join us now and share the software;\n"
+     "You'll be free, hackers, you'll be free.\n"
+     "\n"
+     "Hoarders may get piles of money,\n"
+     "That is true, hackers, that is true.\n"
+     "But they cannot help their neighbors;\n"
+     "That's not good, hackers, that's not good.\n"
+     "\n"
+     "When we have enough free software\n"
+     "At our call, hackers, at our call,\n"
+     "We'll throw out those dirty licenses\n"
+     "Ever more, hackers, ever more.\n"
+);
 
 #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
 #define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
 
 typedef enum _MenuOptions MenuOptions;
 
-#define TINYBUF 16	/* NOTE: Changing this may lead to segfaults. Double-check. */
+#define TINYBUF 16	/* NOTE: Changing this may lead to buffer overflows. Double-check. */
 #define FSBUF 19	/* Don't touch unless you know what you are doing!! */
 #define SMALLBUF 128
 
 	mvaddstr(LINES - 2 - MENUSIZE, MENUDIV, title);	
 }
 
+#if 0
 /* TODO: Improve this. */
 void
 display_message (const char *msg) {
 		clrtoeol();
 	}
 }
+#endif
+
+
+/* This is an ancient magic that turns a magic into a StrList, so we can display it */ 
+static StrList*
+_message_display_strlist(const char *msg, int *lines, int double_new_line) {
+	int x = 0, y = 0, lastword = 0, isnewline = 0, i;
+	StrList *message;
+	char buf[SMALLBUF];
+	/* We save the lines in a string list, counting them */
+	/* TODO: It doesn't deal with tabs correctly, I hope we don't need this anyway */
+	for (i = 0; msg[i]; i++) {
+		/* If we reached the end of the line, move to the next line */
+		if (x >= SMALLBUF - 1 || x >= COLS-2) {
+			if (isblank(msg[i]) || msg[i] == '\r' || msg[i] == '\n' || !lastword) {
+				buf[x] = '\0';
+				message = str_list_append(message,buf);
+				y++;
+				/* Real new line is two lines. */
+				if (msg[i] == '\n') { 
+					if (double_new_line) message = str_list_append(message,"");
+					y++;
+					isnewline = 1;
+				}
+				lastword = 0;
+				x = 0;
+			}
+			/* If this is not the end of the word, go back to the beginning of the word. */
+			else {
+				buf[lastword-1] = '\0';
+				message = str_list_append(message,buf);
+				y++;
+				i = i-x+lastword;
+				lastword = 0;
+				x = 0;
+			}
+		}
+		/* If the line begins with a blank, skip it */
+		if (msg[i] == '\r') {
+			continue;
+		}
+		else if (isblank(msg[i])) {
+			if (x == 0) continue;
+			else lastword = x+1;
+		}
+		else if (msg[i] == '\n') {
+			if (!isnewline) {
+				buf[x] = '\0';
+				message = str_list_append(message,buf);
+				if (double_new_line) message = str_list_append(message,"");
+				y += 2;
+			}
+			x = 0;
+			lastword = 0;
+			continue;
+		}
+		isnewline = 0;
+		buf[x] = msg[i];
+		x++;
+	}
+	buf[x] = '\0'; /* This doesn't produce an error, trust me */
+	if (strlen(buf)) { 
+		message = str_list_append(message,buf);
+		y++;
+	}
+	if (lines) *lines = y;
+	return message;
+}
+
+static void 
+display_message (const char *msg) {
+	int x, y, i;
+	StrList *message = _message_display_strlist(msg,&y,1);
+	/* TODO: Write this, for now I will test it this way */
+	y=LINES-3-MENUSIZE-y;
+	/*  */
+	i=INFOSIZE+1;
+	move(i++,0); clrtoeol();
+	mvaddch(i,0,' ');
+	for (x = 1; x < COLS-1; x++) {
+		mvaddch(i,x,'-'); 
+        }
+	mvaddch(i,0,' ');
+	for (i++; i < y; i++) {
+		move(i,0); clrtoeol();
+	}
+	StrList *current;
+	for (current = message; current && y < LINES-MENUSIZE-3; current = current->next, y++) {
+		char *temp = str_list_convert_node(current);
+		move(y,0); clrtoeol();
+		mvaddstr(y,1,temp);
+		free(temp);
+	}
+	str_list_destroy(message);
+	
+}
 
 /* Menu drawing function */
 void
 
 /* String list choice */
 /* The last two parameters specify the number of the selected element and the first element displayed 
-   last time */
+   last time. If opts & 1, we assume it is a fullscreen text, not choice*/
 static void
-strlist_draw (const char *prompt, const StrList *strlist, const StrList* selected, int selnum, int *start) {
+strlist_draw (const char *prompt, const StrList *strlist, int opts, const StrList* selected, 
+              int selnum, int *start) {
 	/*char buf[SMALLBUF]; *//* FIXME: Not needed */
 	char *temp;
-	int n,x,y = INFOSIZE,i;
+	int n,x,y,i;
 	const StrList *current;
-	n = LINES-MENUSIZE-3;
-	if (selnum - *start < 0)
+	
+	if (opts & 1) {
 		*start = selnum;
-	else if (selnum - *start >= n - INFOSIZE - 4)
-		*start = selnum - n + INFOSIZE + 5;
+		y = 0;
+		n = LINES - 2;
+	}
+	else {
+		n = LINES-MENUSIZE-3;
+		y = INFOSIZE;
+		if (selnum - *start < 0)
+			*start = selnum;
+		else if (selnum - *start >= n - INFOSIZE - 4)
+			*start = selnum - n + INFOSIZE + 5;
+	}
 	move(y++,0); clrtoeol();
 	move(y,0); clrtoeol(); mvaddstr(y++,5,prompt);
 	/* FIXME: Using buf here not needed. This appears another time in source. */
 	//if (i < SMALLBUF) buf[i] = '\0';
 	//mvaddstr(y++,0,buf);
 	move(y++,0); clrtoeol();
-	
+	x = (opts & 1 ? 1 : 7);
 	for (current = strlist, i = 0; current && y < n; current = current->next, i++) {
 		if (i < *start)
 			continue;
-		if (current == selected)
+		if (current == selected && !opts & 1)
 			attron(A_STANDOUT);
 		move(y,0); clrtoeol();
 		temp = str_list_convert_node(current);
-		mvaddstr(y,7,temp);
+		mvaddstr(y,x,temp);
 		if (temp) free(temp);
-		if (current == selected)
+		if (current == selected && !opts & 1)
 			attroff(A_STANDOUT);
 		y++;
+	}
+	/* We clear the next line. No need to clean all lines, unless we implement page up and down */
+	if (y < n) {
+		move(y,0); clrtoeol();
 	} 
 }
 static void show_info();
 static void plist_draw (PedDisk *cdisk, PedPartition *selected, int selnum, int *start);
-static int do_plist (PedDisk *cdisk, PedPartition **part, PedPartitionType have, PedPartitionType havent);
+static int do_plist (PedDisk **cdisk, PedPartition **part, PedPartitionType have, PedPartitionType havent);
 
+/* TODO: Implement page up and page down */
 static const StrList*
-do_strlist (const char *prompt, const StrList *strlist) {
+do_strlist (const char *prompt, const StrList *strlist, int opts) {
 	const StrList *selected = strlist, *temp;
 	int redraw = 1, key = 0, done = 0, start = 0, selnum = 0;
+	const char* end_warning = opts & 1 ? _("No more text") : _("No more choices");
 	while (!done) {
 		if (redraw) {
-			strlist_draw(prompt, strlist, selected,selnum,&start);
 			clear_menu();
+			strlist_draw(prompt, strlist, opts, selected, selnum,&start);
 			redraw = 0;
 		}
 		refresh();
 			key = getch();
 				if (key == 'B') { /* That's down arrow */
 					if (!selected->next)
-						print_warning(_("No more choices"),0);
+						print_warning(end_warning,0);
 					else {
 						selected = selected->next;
 						selnum++;
 				}
 				else if (key == 'A') { /* That's up arrow */
 					if (strlist == selected)
-						print_warning(_("No more choices"),0);
+						print_warning(end_warning,0);
 					else {
 					/* FIXME: Edit strlist and make this faster */
 					/* Users should not use the up arrow much anyway */
 						}
 					}
 				}
+				else if (opts & 1)
+					done = 1;
 				else
 					print_warning(_("Invalid key"),0);
 			}
+			else if (opts & 1)
+				done = 1;
 			else
 				print_warning(_("Invalid key"),0);
 		}
 		else if (key == CR) {
 			done = 1;
 		}
-		else 
+		else if (opts & 1) {
+			done = 1;
+		}
+		else {
 			print_warning(_("Invalid key"),0);
+		}
 	}
 	if (disk) { 
 		show_info();
 	const StrList *selected;
 	if (words || locwords) {
 		if(locwords)
-			selected = do_strlist(prompt,locwords);
+			selected = do_strlist(prompt,locwords,0);
 		else
-			selected = do_strlist(prompt,words);
+			selected = do_strlist(prompt,words,0);
 		if (*value) ped_free(*value);
 		*value = str_list_convert_node(selected);
 		return (*value != NULL);
 		return 0; /* TODO, although it might not get used */
 	}
 	menu_title(prompt);
-	return do_plist(*disk,value,0,PED_PARTITION_EXTENDED | PED_PARTITION_FREESPACE);
+	return do_plist(disk,value,0,PED_PARTITION_EXTENDED | PED_PARTITION_FREESPACE);
 }
 
 
 	
 }
 
+/* Emergency function. If some copy, move or resize fail, reread the disk from the device
+   They commited it before failing, anyway. It is a bit like a hack, I might make the functions
+   make it themselves. I actually did, but I had a little trouble with the UI, so for now it is this way
+ */
+static int
+_disk_reread(PedPartition **part) {
+	int partnum;
+	if (*part) {
+		partnum = (*part)->num;
+	}
+	ped_disk_destroy(disk);
+	disk = ped_disk_new(dev);
+	uiquery.need_commit = 0;
+	if (part) {
+		*part = ped_disk_get_partition(disk,partnum);
+	}
+}
+
+
 /* Here we define our menu operations */
 static int
 action_new (PedPartition **part) {
 	return 1;	
 }
 
+static int
 action_resize (PedPartition **part) {
 	static MenuItem part_position[] = {
 		{ 'f', N_("Fixed start"), N_("Don't move the beginning of the partition") },
 	if (start != (*part)->geom.start || end != (*part)->geom.end)
 		if (!do_resize(disk,*part,start,end,opts,&uiquery)) {
 			warning_waitkey(N_("Couldn't resize partition."));
+			/* Ooopsy */
+			if (uiquery.need_commit) _disk_reread(part);
 			return 0;
 		}
 	return 1;
 	}
 	ped_file_system_close (fs);
 	menu_title(_("Select free space to move the partition to"));
-	if (!do_plist(disk, &dst, PED_PARTITION_FREESPACE, 0)) {
+	if (!do_plist(&disk, &dst, PED_PARTITION_FREESPACE, 0)) {
 		ped_constraint_destroy (constraint);
 		return 0;
 	}
 	ped_constraint_destroy (constraint);
 	if (!do_move(disk,*part,start,end,opts,&uiquery)) {
 		warning_waitkey(N_("Partition move failed"));
+		/* Oooops */
+		if (uiquery.need_commit) _disk_reread(part);
 		return 0;
 	}
 	return 1;
 static int
 action_copy(PedPartition **part) {
 	if (!do_cp(disk,*part, UI_WARN_COMMIT, &uiquery)) {
+		if (uiquery.need_commit) _disk_reread(part);
 		warning_waitkey(N_("Partition not copied successfully"));
 		return 0;
 	}
 	return 1;
 }
 
+static int
+action_help () {
+	StrList *message = _message_display_strlist(help,NULL,0);
+	do_strlist(_("cfdisk help"),message,1);
+	str_list_destroy(message);
+}
+
 /* Define column positions. The first three will be fixed. */
 #define col_name 3
 #define col_flags 12
 			break;
 		case 't':
 			action_type(part);
+			//redraw = 0; //??
+			key = 0;
+			break;
+		case 'h':
+			action_help();
 			//redraw = 0;
 			key = 0;
 			break;
 /* Okay, this is our partition list. If there is no pointer to partition given, ASSume main menu
    have and havent aren't perfect, but work for our purposes */
 static int
-do_plist (PedDisk *cdisk, PedPartition **part, PedPartitionType have, PedPartitionType havent) {
+do_plist (PedDisk **cdisk, PedPartition **part, PedPartitionType have, PedPartitionType havent) {
 	int key = 0, i, redraw = 1, selnum = 0, start = 0;
 	const char* keys;
 	PedPartition *selected = NULL;
 		menu_opts |= MENU_TITLE;
 	}
 	do {
-		selected = ped_disk_next_partition(cdisk,selected);
+		selected = ped_disk_next_partition(*cdisk,selected);
 	} while (selected && selected->type & PED_PARTITION_METADATA);
 	while (!key) {
 		if(redraw) {
 			show_info();
-			plist_draw(cdisk,selected, selnum, &start);
+			plist_draw(*cdisk,selected, selnum, &start);
 			redraw = 0;
 		}
 		if (part) {
 		menu_opts &= ~MENU_LEAVE_WARNING;
 		if (key == UP_KEY) {
 
-			temp = disk_get_prev_nmd_partition(cdisk,selected);
+			temp = disk_get_prev_nmd_partition(*cdisk,selected);
 			if (temp) {
 				selected = temp;
 				selnum--;
 			key = 0;
 		}
 		else if (key == DOWN_KEY) {
-			temp = ped_disk_next_partition(cdisk,selected);
+			temp = ped_disk_next_partition(*cdisk,selected);
 			while (temp && temp->type & PED_PARTITION_METADATA)
-				temp = ped_disk_next_partition(cdisk,temp);
+				temp = ped_disk_next_partition(*cdisk,temp);
 			if (temp) { 
 				selnum++;
 				selected = temp;
 	init_disk();
 
 	show_info();
-	do_plist(disk,NULL,0,0);
+	do_plist(&disk,NULL,0,0);
 	endwin();
 }
 
 
 static void
 print_usage() {
+	int i;
+	char buf[SMALLBUF];
+	printf(_(usage_msg));
+	printf("\n%s\n",_("OPTIONs:"));
+	for (i = 0; options_help[i].opt; i++) {
+		if (options_help[i].arg)
+			snprintf(buf,SMALLBUF,"%s=%s",options_help[i].lopt,_(options_help[i].arg));
+		else
+			strncpy(buf,options_help[i].lopt,SMALLBUF);
+		printf ("  -%c, --%-23.23s %s\n", options_help[i].opt, buf, _(options_help[i].help));
+	}
 	exit(0);
 }
 
 
 static void
 print_version() {
-	printf("cfdisk (GNU fdisk) %s\n",VERSION);
-	printf("Copyright (C) 2006 Free Software Foundation, Inc.\n");
-	printf("This is free software.  You may redistribute copies of it under the terms of\n");
-	printf("the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n");
-	printf("There is NO WARRANTY, to the extent permitted by law.");
+	printf(prog_name);
+	printf(_(license_msg));
 	exit(0);
 }
 
         PedFileSystem*          src_fs;
         PedFileSystem*          dst_fs;
         PedFileSystemType*      dst_fs_type;
+	
+	if (!dst_disk)
+                goto error;
+
 	if (opts & UI_WARN_COMMIT)
 		if (!ask_boolean_question 
 		(_("WARNING: This writes all data to disk automatically, continue?"),uiquery)) 
 			return 0;
-
-        if (!dst_disk)
-                goto error;
+	/* It doesn't hurt to commit at that point, right? */
+	if (uiquery->need_commit) if (!do_commit(dst_disk,uiquery)) goto error;
+        
 
         src_disk = dst_disk;
 	if (!get_disk (_("Source device"), &src_disk, uiquery))
         src_fs = ped_file_system_open (&src->geom);
         if (!src_fs)
                 goto error_destroy_disk;
+	/* At this point we do something worth commiting. If we fail, the caller should clean the mess */
+	uiquery->need_commit = 1;
         dst_fs = ped_file_system_copy (src_fs, &dst->geom, uiquery->timer);
         if (!dst_fs)
                 goto error_close_src_fs;
 
 }
 
-/* TODO: get rid of warn */
+/* FIXME: This function seems to be problematic, deal with it appropriately in the ui */
 int
 do_move (PedDisk *disk, PedPartition *part, PedSector start, PedSector end, UIOpts opts, UICalls *uiquery)
 {
 
         if (!disk)
                 goto error;
-
+	
 	if (opts & UI_WARN_COMMIT) {
 		if (!ask_boolean_question 
 		(_("WARNING: This writes all data to disk automatically, continue?"),uiquery)) 
 			return 0;
 	}
-
+	/* So the best we can do here is to commit */
+	if (uiquery->need_commit)
+		if (!do_commit(disk,uiquery))
+			goto error;
 	if (!part)
         	if (!get_partition (_("Partition"), disk, &part, uiquery))
                 	goto error;
         constraint = constraint_intersect_and_destroy (
                         ped_file_system_get_copy_constraint (fs, disk->dev),
                         constraint_from_start_end (disk->dev, range_start, range_end));
+	/* So here we do something worth commiting and dangerous. UI *must* do something if we fail
+	   and need_commit is true... */
+	uiquery->need_commit = 1;
         if (!ped_disk_set_partition_geom (disk, part, constraint,
                                           new_geom.start, new_geom.end))
                 goto error_destroy_constraint;
 
         /* do the move */
         fs_copy = ped_file_system_copy (fs, &part->geom, uiquery->timer);
-        if (!fs_copy)
+        if (!fs_copy) {
+		//ped_constraint_destroy (constraint);
+		//constraint = ped_constraint_any(disk->dev);
+		//ped_disk_set_partition_geom(disk,part,constraint,old_geom.start,old_geom.end);
+		ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+			_("Couldn't run the partition copy"));
                 goto error_close_fs;
+	}
         ped_file_system_close (fs_copy);
         ped_file_system_close (fs);
         if (!ped_disk_commit (disk)) {
 		if (!ask_boolean_question 
 		(_("WARNING: This writes all data to disk automatically, continue?"),uiquery)) 
 			return 0;
-
+	/* Why the hell I use two seperate ifs, instead of one? I wish I knew */
+	if (uiquery->need_commit)
+		if (!do_commit(disk,uiquery))
+			goto error;
         if (!get_sector (_("Start"), disk->dev, &start, NULL, opts & UI_CUSTOM_VALUES, uiquery))
                 goto error;
         if (!get_sector (_("End"), disk->dev, &end, NULL, opts & UI_CUSTOM_VALUES, uiquery))
         ped_geometry_init (&probe_end_region, disk->dev,
                            PED_MAX(end - fuzz, 0),
                            PED_MIN(2 * fuzz, (disk->dev)->length - (end - fuzz)));
-
+	/* I don't believe this is dangerous, but still */
+	uiquery->need_commit = 1;
         if (!_rescue_pass (disk, &probe_start_region, &probe_end_region, uiquery))
                 goto error;
 
 		if (!ask_boolean_question 
 		(_("WARNING: This writes all data to disk automatically, continue?"),uiquery)) 
 			return 0;
+	if (uiquery->need_commit)
+		if (!do_commit(disk,uiquery))
+			goto error;
 	if (!part) 
         	if (!get_partition (_("Partition"), disk, &part, uiquery))
                 	goto error;
                             range_start, range_end);
 	/* If the partition is extended or we have UI_NO_FS_RESIZE, don't try to resize the fs, FIXME */
         if (part->type == PED_PARTITION_EXTENDED || (opts & UI_NO_FS_RESIZE)) {
+		/* I really hope this part isn't dangerous */
                 constraint = constraint_from_start_end (disk->dev,
                                 range_start, range_end);
                 if (!ped_disk_set_partition_geom (disk, part, constraint,
                         goto error_destroy_constraint;
 		if (part->type == PED_PARTITION_EXTENDED)
                 	ped_partition_set_system (part, NULL);
-
+		
 		uiquery->need_commit = 1;
         } else {
 		fs = ped_file_system_open (&part->geom);
 				ped_file_system_get_resize_constraint (fs),
 				constraint_from_start_end (
 					disk->dev, range_start, range_end));
+		/* We will play with the geometry */
+		uiquery->need_commit = 1;
                 if (!ped_disk_set_partition_geom (disk, part, constraint,
                                                   new_geom.start, new_geom.end))
                         goto error_close_fs;
 #define can_create_logical(disk) _can_create_logical(disk)
 
 /* Main functions */
+/* NOTE: resize, move and perhaps copy are dangerous. Please, make your ui check their status.
+   if they FAIL and leave need_commit to true, please reread the partition table from the device.
+   Sorry for the inconvinience. I might change this in the future so that it is not needed */
 extern int do_check (PedDisk*, PedPartition*, UICalls *);
 extern int do_cp (PedDisk*, PedPartition*, UIOpts, UICalls *);
 extern int do_mklabel (PedDevice*, PedDisk**, const PedDiskType*, UICalls *);
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.