summaryrefslogblamecommitdiffstats
path: root/disk-utils/cfdisk.c
blob: 1b87aae799e4dbd137b04aeb9ba5b97b4e5ee08c (plain) (tree)
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116






























                                                                             
                              













                                                                              
                   
                     










                                                   
                                                                   

                                                    
                      

















                                   

                           


























                                                                                                
                                                                               















































                                                                                                 




                                                                      




















                                                                        

                              








                                   

                       









































                                        





                                        
                                        






                                             



                                                                        
                                            









                                   

                                         




























                                                                             

































































                                              
                                              










                              




                                                       
 

                                 

 

                    



                          













                                                              
 



                   
 

                   




                                 

                 



                                            
                                                                            






                                                     
                                                                            




                                                      

















                                                                      








                                                               
                                 










                                      
                   

                                                                       
                                             



















                                                                          
                                                         


                                                  
                               



                                           
                                             
















                                                                          
                                                         



                                                  
     











                                   
                                                  







                                   




                               










































                                                                        
                                                               
                                                              

                                











                                                              

                                                                         






                                           


                                                      





                                                               
                                            


                        


                                                      

                  
                                                                     
 

                                                    
                  
     
 



                                                     




                                          
                             
                               
                                      
                     



                                                               

                          
                                        
















                                                                          
                            
                                                      
                     




                                                           
                        
                                                
                 





                                                
                                                              



                                      
                                                              






                                   


                                           











                                                             
                                                                
























































































































































































































































































































































































                                                                                                                          
                                                       






                                                                    


                                                          








                                                       
                                                            


























                                                                 













                                                    

















                                             

                                                 



                                                   
                                                    

                                                       

                                                    

                                   
                                                      


                                   
                                      
                               

                                                         




                                                                
                                                                      
                          
 
                                          





                                                           

                                                         



                                       
                                                                     

                            

                                                                         





































                                                                            
                                 





























                                                                            

                                                                                   















                                                               

                  

                               






                                      
 































                                                                         

     





                                        




                                                             














                                           













































































                                                                          
                                 





                                        
                                                                  















                                                            
                      














                                                              
                                           



                                                         
                                          



                                                            
                                     





                                                                
                                                
                                





                                                                        
                                               
                                 
                                                 
                                   
                                                                        
        

                                                            
















                                                  
                                                    


























                                                                          


                                                                                                       



















































                                                                        
                                                                         



































                                                                          


                                                                                     






                                                                        
                                                                   





























































                                                                           
                                                        































































































































                                                                               



                                                                 






                                                           
                                                       
 


                                                                

                                                       
                                                           
                    


                                                                    




























































                                                                               
                                 























































                                                                              




                                                                    























































































































                                                                           



















                                                                                           
                       


















                                       
                
 
                             
 












                                                                             











































































































                                                           
             



                    
                                                                       



                           













                                                                           


                
                               

































































                                                                    
                                                         












                                               
             
 
/****************************************************************************
 *
 *     CFDISK
 *
 * cfdisk is a curses based disk drive partitioning program that can
 * create partitions for a wide variety of operating systems including
 * Linux, MS-DOS and OS/2.
 *
 * cfdisk was inspired by the fdisk program, by A. V. Le Blanc
 * (LeBlanc@mcc.ac.uk).
 *
 *     Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
 *
 * cfdisk is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * cfdisk is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with cfdisk; if not, write to the Free Software Foundation,
 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Created:	Fri Jan 28 22:46:58 1994, martin@cs.unc.edu
 * >2GB patches: Sat Feb 11 09:08:10 1995, faith@cs.unc.edu
 * Prettier menus: Sat Feb 11 09:08:25 1995, Janne Kukonlehto
 *                                           <jtklehto@stekt.oulu.fi>
 * Versions 0.8e-h: aeb@cwi.nl
 *
 ****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <curses.h>
#include <signal.h>
#include <math.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/fs.h>		/* for BLKRRPART */

#if defined(__GNUC__) || defined(HAS_LONG_LONG)
typedef long long ext2_loff_t;
#else
typedef long      ext2_loff_t;
#endif

extern ext2_loff_t ext2_llseek(unsigned int fd, ext2_loff_t offset,
			       unsigned int origin);

#define VERSION "0.8i"

#define DEFAULT_DEVICE "/dev/hda"
#define ALTERNATE_DEVICE "/dev/sda"

#define LINE_LENGTH 80
#define MAXIMUM_PARTS 60

#define SECTOR_SIZE 512

#define MAX_CYLINDERS 65535
#define MAX_HEADS 255
#define MAX_SECTORS 63

#define ACTIVE_FLAG 0x80
#define PART_TABLE_FLAG 0xAA55

#define UNUSABLE -1
#define FREE_SPACE 0x00
#define DOS_EXTENDED 0x05
#define LINUX_EXTENDED 0x85
#define LINUX_MINIX 0x81
#define LINUX_SWAP 0x82
#define LINUX 0x83

#define ADD_EXISTS "This partition is already in use"
#define ADD_UNUSABLE "This partition is unusable"
#define DEL_EMPTY "Cannot delete an empty partition"
#define ID_EMPTY "Cannot change FS Type to empty"
#define ID_EXT "Cannot change FS Type to extended"
#define NEED_EXT "No room to create the extended partition"
#define NO_FLAGS "Cannot make this partition bootable"
#define NO_MORE_PARTS "No more partitions"
#define PRINT_OPEN_ERR "Cannot open file '%s'"
#define TWO_EXTENDEDS "Cannot create logical drive here -- would create two extended partitions"
#define TYPE_EMPTY "Cannot change the type of an empty partition"
#define BAD_COMMAND "Illegal command"
#define MAX_UNMAXABLE "Cannot maximize this partition"
#define BAD_OPEN "Cannot open disk drive"
#define BAD_SEEK "Cannot seek on disk drive"
#define BAD_READ "Cannot read disk drive"
#define BAD_WRITE "Cannot write disk drive"
#define BAD_GEOMETRY "Cannot read disk drive geometry"
#define BAD_PRIMARY "Bad primary partition"
#define BAD_LOGICAL "Bad logical partition"
#define BAD_CYLINDERS "Illegal cylinders value"
#define BAD_HEADS "Illegal heads value"
#define BAD_SECTORS "Illegal sectors value"
#define READONLY_WARN "Opened disk read-only - you have no permission to write"
#define WRITE_WARN "Warning!!  This may destroy data on your disk!"
#define YES_NO "Please enter `yes' or `no'"
#define WRITING_PART "Writing partition table to disk..."
#define YES_WRITE "Wrote partition table to disk"
#define NO_WRITE "Did not write partition table to disk"
#define RRPART_FAILED "Wrote partition table, but re-read table failed.  Reboot to update table."

#define PRI_OR_LOG -1
#define PRIMARY -2
#define LOGICAL -3

#define COL_ID_WIDTH 20

#define CR '\015'
#define ESC '\033'
#define DEL '\177'
#define BELL '\007'
/* '\014' == ^L */
#define REDRAWKEY '\014'

/* Display units */
#define MEGABYTES 1
#define SECTORS 2
#define CYLINDERS 3

#define GS_DEFAULT -1
#define GS_ESCAPE -2

#define PRINT_RAW_TABLE 1
#define PRINT_SECTOR_TABLE 2
#define PRINT_PARTITION_TABLE 4

#define IS_PRIMARY(p) ((p) >= 0 && (p) < 4)
#define IS_LOGICAL(p) ((p) > 3)

#define round_int(d) ((double)((int)(d+0.5)))
#define ceiling(d) ((double)(((d) != (int)(d)) ? (int)(d+1.0) : (int)(d)))

#define set_hsc(h,s,c,sector) \
{ \
      s = sector % sectors + 1;	\
      sector /= sectors;	\
      h = sector % heads;	\
      sector /= heads;		\
      c = sector & 0xFF;	\
      s |= (sector >> 2) & 0xC0;\
}

#define is_extended(x)	((x) == DOS_EXTENDED || (x) == LINUX_EXTENDED)

/* we might also want to recognise 0xe and 0xf */
#define is_dos_partition(x) ((x) == 1 || (x) == 4 || (x) == 6)

#define ALIGNMENT 2
typedef union {
    struct {
	unsigned char align[ALIGNMENT];
	unsigned char b[SECTOR_SIZE];
    } c;
    struct {
	unsigned char align[ALIGNMENT];
	unsigned char buffer[0x1BE];
	struct partition part[4];
	unsigned short flag;
    } p;
} partition_table;

typedef struct {
    int first_sector;	/* first sector in partition */
    int last_sector;	/* last sector in partition */
    int offset;		/* offset from first sector to start of data */
    int flags;		/* active == 0x80 */
    int id;		/* filesystem type */
    int num;		/* number of partition -- primary vs. logical */
#define LABELSZ 11
    char dos_label[LABELSZ+1];
} partition_info;

char *disk_device = DEFAULT_DEVICE;
int fd;
int heads = 0;
int sectors = 0;
int cylinders = 0;
int changed = FALSE;
int opened = FALSE;
int opentype;
int curses_started = 0;

partition_info p_info[MAXIMUM_PARTS];
partition_info ext_info;
int num_parts = 0;

int logical = 0;
int logical_sectors[MAXIMUM_PARTS];

__sighandler_t old_SIGINT, old_SIGTERM;

int arrow_cursor = FALSE;
int display_units = MEGABYTES;
int zero_table = FALSE;
int print_only = 0;

/* Curses screen information */
int cur_part = 0;
int warning_last_time = FALSE;
int defined = FALSE;
int COLUMNS = 80;
int NUM_ON_SCREEN = 1;

/* Y coordinates */
int HEADER_START = 0;
int DISK_TABLE_START = 5;
int WARNING_START = 23;
int COMMAND_LINE_Y = 21;

/* X coordinates */
int NAME_START = 4;
int FLAGS_START = 16;
int PTYPE_START = 30;
int FSTYPE_START = 45;
int SIZE_START = 70;
int COMMAND_LINE_X = 5;

#define NUM_PART_TYPES 256
char *partition_type[NUM_PART_TYPES] = {
    [LINUX_MINIX] = "Linux/MINIX",
    [LINUX_SWAP]  = "Linux Swap",
    [LINUX]       = "Linux",
    [FREE_SPACE]  = "Free Space",
    [DOS_EXTENDED]= "Extended",
    [LINUX_EXTENDED] = "Linux extended",
    [0x01]        = "DOS FAT12",
    [0x04]        = "DOS FAT16",
    [0x06]        = "DOS FAT16 (big)",
    [0x07]        = "OS/2 HPFS or NTFS",
    [0x0A]        = "OS/2 Boot Manager",
    [0x0B]        = "Win95 FAT32",
    [0x0C]        = "Win95 FAT32 (LBA)",
    [0x0E]        = "Win95 FAT16 (LBA)",
    [0x0F]        = "Win95 Extended (LBA)",
    [0x11]        = "Hidden DOS FAT12",
    [0x14]        = "Hidden DOS FAT16",
    [0x16]        = "Hidden DOS FAT16 (big)",
    [0xA5]        = "BSD/386",

/* The rest of these are taken from A. V. Le Blanc's (LeBlanc@mcc.ac.uk)
 * fdisk program.  I do not know where they came from, but I include
 * them for completeness.  (With additions.)
 */

    [0x02]        = "XENIX root",
    [0x03]        = "XENIX usr",
    [0x08]        = "AIX",
    [0x09]        = "AIX bootable",
    [0x40]        = "Venix 80286",
    [0x51]        = "Novell?",
    [0x52]        = "Microport",
    [0x63]        = "GNU HURD",
    [0x64]        = "Novell Netware 286",
    [0x65]        = "Novell Netware 386",
    [0x75]        = "PC/IX",
    [0x80]        = "Old MINIX",
    [0x93]        = "Amoeba",
    [0x94]        = "Amoeba BBT",
    [0xB7]        = "BSDI fs",
    [0xB8]        = "BSDI swap",
    [0xC7]        = "Syrinx",
    [0xDB]        = "CP/M",
    [0xE1]        = "DOS access",
    [0xE3]        = "DOS R/O",
    [0xF2]        = "DOS secondary",
    [0xFF]        = "BBT"
};

void fdexit(int ret)
{
    if (opened)
	close(fd);

    if (changed) {
	fprintf(stderr, "Disk has been changed.\n");
	fprintf(stderr, "Reboot the system to ensure the partition "
			"table is correctly updated.\n");
	
	fprintf( stderr, "\nWARNING: If you have created or modified any\n"
		         "DOS 6.x partitions, please see the cfdisk manual\n"
		         "page for additional information.\n" );
    }

    exit(ret);
}

int get_string(char *str, int len, char *def)
{
    char c;
    int i = 0;
    int x, y;
    int use_def = FALSE;

    getyx(stdscr, y, x);
    clrtoeol();

    str[i] = 0;

    if (def != NULL) {
	mvaddstr(y, x, def);
	move(y, x);
	use_def = TRUE;
    }

    refresh();
    while ((c = getch()) != '\n' && c != CR) {
	switch (c) {
	case ESC:
	    move(y, x);
	    clrtoeol();
	    refresh();
	    return GS_ESCAPE;
	case DEL:
	case '\b':
	    if (i > 0) {
		str[--i] = 0;
		mvaddch(y, x+i, ' ');
		move(y, x+i);
	    } else if (use_def) {
		clrtoeol();
		use_def = FALSE;
	    } else
		putchar(BELL);
	    break;
	default:
	    if (i < len && isprint(c)) {
		mvaddch(y, x+i, c);
		if (use_def) {
		    clrtoeol();
		    use_def = FALSE;
		}
		str[i++] = c;
		str[i] = 0;
	    } else
		putchar(BELL);
	}
	refresh();
    }

    if (use_def)
	return GS_DEFAULT;
    else
	return i;
}

void clear_warning(void)
{
    int i;

    if (!curses_started || !warning_last_time)
	return;

    move(WARNING_START,0);
    for (i = 0; i < COLS; i++)
	addch(' ');

    warning_last_time = FALSE;
}

void print_warning(char *s)
{
    if (!curses_started) {
	 fprintf(stderr, "%s\n", s);
    } else {
	mvaddstr(WARNING_START, (COLS-strlen(s))/2, s);
	putchar(BELL); /* CTRL-G */

	warning_last_time = TRUE;
    }
}

void die_x(int ret);

void fatal(char *s)
{
    char str[LINE_LENGTH];

    if (curses_started) {
	 sprintf(str, "FATAL ERROR: %s", s);
	 mvaddstr(WARNING_START, (COLS-strlen(str))/2, str);
	 sprintf(str, "Press any key to exit fdisk");
	 mvaddstr(WARNING_START+1, (COLS-strlen(str))/2, str);
	 putchar(BELL); /* CTRL-G */
	 refresh();
	 (void)getch();
	 die_x(1);
    } else {
	 fprintf(stderr, "FATAL ERROR: %s\n", s);
	 exit(1);
    }
}

void die(int dummy)
{
    die_x(0);
}

void die_x(int ret)
{
    signal(SIGINT, old_SIGINT);
    signal(SIGTERM, old_SIGTERM);
    mvcur(0, COLS-1, LINES-1, 0);
    nl();
    endwin();
    printf("\n");
    fdexit(ret);
}

void read_sector(char *buffer, int sect_num)
{
    if (ext2_llseek(fd, ((ext2_loff_t) sect_num)*SECTOR_SIZE, SEEK_SET) < 0)
	fatal(BAD_SEEK);
    if (read(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
	fatal(BAD_READ);
}

void write_sector(char *buffer, int sect_num)
{
    if (ext2_llseek(fd, ((ext2_loff_t) sect_num)*SECTOR_SIZE, SEEK_SET) < 0)
	fatal(BAD_SEEK);
    if (write(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
	fatal(BAD_WRITE);
}

void get_dos_label(int i)
{
    char label[LABELSZ+1];
    ext2_loff_t offset;
    int j;

    offset = ((ext2_loff_t) p_info[i].first_sector + p_info[i].offset)
	 * SECTOR_SIZE + 43;
    if (ext2_llseek(fd, offset, SEEK_SET) == offset
	&& read(fd, &label, LABELSZ) == LABELSZ) {
	 for(j=0; j<LABELSZ; j++)
	      if(!isascii(label[j]))
		   label[j] = 0;
	 label[LABELSZ] = 0;
	 strcpy(p_info[i].dos_label, label);
    }
}

void check_part_info(void)
{
    int i, pri = 0, log = 0;

    for (i = 0; i < num_parts; i++)
	if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
	    pri++;
	else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
	    log++;
    if (is_extended(ext_info.id))
	if (log > 0)
	    pri++;
	else {
	    ext_info.first_sector = 0;
	    ext_info.last_sector = 0;
	    ext_info.offset = 0;
	    ext_info.flags = 0;
	    ext_info.id = FREE_SPACE;
	    ext_info.num = PRIMARY;
	}

    if (pri >= 4) {
	for (i = 0; i < num_parts; i++)
	    if (p_info[i].id == FREE_SPACE || p_info[i].id == UNUSABLE)
		if (is_extended(ext_info.id))
		    if (p_info[i].first_sector >= ext_info.first_sector &&
			p_info[i].last_sector <= ext_info.last_sector) {
			p_info[i].id = FREE_SPACE;
			p_info[i].num = LOGICAL;
		    } else if (i > 0 &&
			       p_info[i-1].first_sector >=
			       ext_info.first_sector &&
			       p_info[i-1].last_sector <=
			       ext_info.last_sector) {
			p_info[i].id = FREE_SPACE;
			p_info[i].num = LOGICAL;
		    } else if (i < num_parts-1 &&
			       p_info[i+1].first_sector >=
			       ext_info.first_sector &&
			       p_info[i+1].last_sector <=
			       ext_info.last_sector) {
			p_info[i].id = FREE_SPACE;
			p_info[i].num = LOGICAL;
		    } else
			p_info[i].id = UNUSABLE;
		else /* if (!is_extended(ext_info.id)) */
		    p_info[i].id = UNUSABLE;
	    else /* if (p_info[i].id > 0) */
		while (0); /* Leave these alone */
    } else { /* if (pri < 4) */
	for (i = 0; i < num_parts; i++) {
	    if (p_info[i].id == UNUSABLE)
		p_info[i].id = FREE_SPACE;
	    if (p_info[i].id == FREE_SPACE)
		if (is_extended(ext_info.id))
		    if (p_info[i].first_sector >= ext_info.first_sector &&
			p_info[i].last_sector <= ext_info.last_sector)
			p_info[i].num = LOGICAL;
		    else if (i > 0 &&
			     p_info[i-1].first_sector >=
			     ext_info.first_sector &&
			     p_info[i-1].last_sector <=
			     ext_info.last_sector)
			p_info[i].num = PRI_OR_LOG;
		    else if (i < num_parts-1 &&
			     p_info[i+1].first_sector >=
			     ext_info.first_sector &&
			     p_info[i+1].last_sector <=
			     ext_info.last_sector)
			p_info[i].num = PRI_OR_LOG;
		    else
			p_info[i].num = PRIMARY;
		else /* if (!is_extended(ext_info.id)) */
		    p_info[i].num = PRI_OR_LOG;
	    else /* if (p_info[i].id > 0) */
		while (0); /* Leave these alone */
	}
    }
}

void remove_part(int i)
{
    int p;

    for (p = i; p < num_parts; p++)
	p_info[p] = p_info[p+1];

    num_parts--;
}

void insert_empty_part(int i, int first, int last)
{
    int p;

    for (p = num_parts; p > i; p--)
	 p_info[p] = p_info[p-1];

    p_info[i].first_sector = first;
    p_info[i].last_sector = last;
    p_info[i].offset = 0;
    p_info[i].flags = 0;
    p_info[i].id = FREE_SPACE;
    p_info[i].num = PRI_OR_LOG;
    p_info[i].dos_label[0] = 0;

    num_parts++;
}

void del_part(int i)
{
    int num = p_info[i].num;

    if (i > 0 && (p_info[i-1].id == FREE_SPACE ||
		  p_info[i-1].id == UNUSABLE)) {
	/* Merge with previous partition */
	p_info[i-1].last_sector = p_info[i].last_sector;
	remove_part(i--);
    }

    if (i < num_parts - 1 && (p_info[i+1].id == FREE_SPACE ||
			      p_info[i+1].id == UNUSABLE)) {
	/* Merge with next partition */
	p_info[i+1].first_sector = p_info[i].first_sector;
	remove_part(i);
    }

    if (i > 0)
	p_info[i].first_sector = p_info[i-1].last_sector + 1;
    else
	p_info[i].first_sector = 0;

    if (i < num_parts - 1)
	p_info[i].last_sector = p_info[i+1].first_sector - 1;
    else
	p_info[i].last_sector = sectors*heads*cylinders - 1;

    p_info[i].offset = 0;
    p_info[i].flags = 0;
    p_info[i].id = FREE_SPACE;
    p_info[i].num = PRI_OR_LOG;

    if (IS_LOGICAL(num)) {
	/* We have a logical partition --> shrink the extended partition
	 * if (1) this is the first logical drive, or (2) this is the
	 * last logical drive; and if there are any other logical drives
	 * then renumber the ones after "num".
	 */
	if (i == 0 || (i > 0 && IS_PRIMARY(p_info[i-1].num))) {
	    ext_info.first_sector = p_info[i].last_sector + 1;
	    ext_info.offset = 0;
	}
	if (i == num_parts-1 ||
	    (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)))
	    ext_info.last_sector = p_info[i].first_sector - 1;
	for (i = 0; i < num_parts; i++)
	    if (p_info[i].num > num)
		p_info[i].num--;
    }

    /* Clean up the rest of the partitions */
    check_part_info();
}

int add_part(int num, int id, int flags, int first, int last, int offset,
	     int want_label)
{
    int i, pri = 0, log = 0;

    if (num_parts == MAXIMUM_PARTS ||
	first < 0 ||
	first >= cylinders*heads*sectors ||
	last < 0 ||
	last >= cylinders*heads*sectors) {
	return -1;		/* bad start or end */
    }

    for (i = 0; i < num_parts; i++)
	if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
	    pri++;
	else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
	    log++;
    if (is_extended(ext_info.id) && log > 0)
	pri++;

    if (IS_PRIMARY(num))
	if (pri >= 4) {
	    return -1;		/* no room for more */
	} else
	    pri++;

    for (i = 0; i < num_parts && p_info[i].last_sector < first; i++);

    if (i == num_parts || p_info[i].id != FREE_SPACE
	|| last > p_info[i].last_sector) {
	return -1;
    }

    if (is_extended(id)) {
	if (ext_info.id != FREE_SPACE) {
	    return -1;		/* second extended */
	}
	else if (IS_PRIMARY(num)) {
	    ext_info.first_sector = first;
	    ext_info.last_sector = last;
	    ext_info.offset = offset;
	    ext_info.flags = flags;
	    ext_info.id = id;
	    ext_info.num = num;
	    ext_info.dos_label[0] = 0;
	    return 0;
	} else {
	    return -1;		/* explicit extended logical */
	}
    }

    if (IS_LOGICAL(num)) {
	if (!is_extended(ext_info.id)) {
	    print_warning("!!!! Internal error creating logical "
			  "drive with no extended partition !!!!");
	} else {
	    /* We might have a logical partition outside of the extended
	     * partition's range --> we have to extend the extended
	     * partition's range to encompass this new partition, but we
	     * must make sure that there are no primary partitions between
	     * it and the closest logical drive in extended partition.
	     */
	    if (first < ext_info.first_sector) {
		if (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)) {
		    print_warning(TWO_EXTENDEDS);
		    return -1;
		} else {
		    if (first == 0) {
			ext_info.first_sector = 0;
			ext_info.offset = first = offset;
		    } else {
			ext_info.first_sector = first;
		    }
		}
	    } else if (last > ext_info.last_sector) {
		if (i > 0 && IS_PRIMARY(p_info[i-1].num)) {
		    print_warning(TWO_EXTENDEDS);
		    return -1;
		} else {
		    ext_info.last_sector = last;
		}
	    }
	}
    }

    if (first != p_info[i].first_sector &&
	!(IS_LOGICAL(num) && first == offset)) {
	insert_empty_part(i, p_info[i].first_sector, first-1);
	i++;
    }

    if (last != p_info[i].last_sector)
	insert_empty_part(i+1, last+1, p_info[i].last_sector);

    p_info[i].first_sector = first;
    p_info[i].last_sector = last;
    p_info[i].offset = offset;
    p_info[i].flags = flags;
    p_info[i].id = id;
    p_info[i].num = num;
    p_info[i].dos_label[0] = 0;
    if (want_label && is_dos_partition(id))
	 get_dos_label(i);

    check_part_info();

    return 0;
}

int find_primary(void)
{
    int num = 0, cur = 0;

    while (cur < num_parts && IS_PRIMARY(num))
	if ((p_info[cur].id > 0 && p_info[cur].num == num) ||
	    (is_extended(ext_info.id) && ext_info.num == num)) {
	    num++;
	    cur = 0;
	} else
	    cur++;

    if (!IS_PRIMARY(num))
	return -1;
    else
	return num;
}

int find_logical(int i)
{
    int num = -1;
    int j;

    for (j = i; j < num_parts && num == -1; j++)
	if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num))
	    num = p_info[j].num;

    if (num == -1) {
	num = 4;
	for (j = 0; j < num_parts; j++)
	    if (p_info[j].id > 0 && p_info[j].num == num)
		num++;
    }

    return num;
}

void inc_logical(int i)
{
    int j;

    for (j = i; j < num_parts; j++)
	if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num))
	    p_info[j].num++;
}

/* Command menu support by Janne Kukonlehto <jtklehto@phoenix.oulu.fi> September 1994 */

/* Constants for menuType parameter of menuSelect function */
#define MENU_HORIZ 1
#define MENU_VERT 2
#define MENU_ACCEPT_OTHERS 4
#define MENU_BUTTON 8
/* Miscellenous constants */
#define MENU_SPACING 2
#define MENU_MAX_ITEMS 256 /* for simpleMenu function */
#define MENU_UP 1
#define MENU_DOWN 2
#define MENU_RIGHT 3
#define MENU_LEFT 4

struct MenuItem
{
    char key; /* Keyboard shortcut; if zero, then there is no more items in the menu item table */
    char *name; /* Item name, should be eight characters with current implementation */
    char *desc; /* Item description to be printed when item is selected */
};

/* Actual function which prints the button bar and highlights the active button *
 * Should not be called directly. Call function menuSelect instead.             */

int menuUpdate( int y, int x, struct MenuItem *menuItems, int itemLength, char *available, int menuType, int current )
{
    int i, lmargin = x, ymargin = y;
    /* Print available buttons */
    move( y, x ); clrtoeol();
    for( i = 0; menuItems[i].key; i++ )
    {
        char buff[20];
        int lenName;
        /* Search next available button */
        while( menuItems[i].key && !strchr(available, menuItems[i].key) )
        {
            i++;
        }
        if( !menuItems[i].key ) break; /* No more menu items */
        /* If selected item is not available and we have bypassed it, make current item selected */
        if( current < i && menuItems[current].key < 0 ) current = i;
        /* If current item is selected, highlight it */
        if( current == i ) /*attron( A_REVERSE )*/ standout ();
        /* Print item */
        lenName = strlen( menuItems[i].name );
        if(lenName > itemLength)
            print_warning("Menu item too long. Menu may look odd.");
        if( menuType & MENU_BUTTON )
            sprintf( buff, "[%*s%-*s]", (itemLength - lenName) / 2, "",
                (itemLength - lenName + 1) / 2 + lenName, menuItems[i].name );
        else
            sprintf( buff, "%*s%-*s", (itemLength - lenName) / 2, "",
                (itemLength - lenName + 1) / 2 + lenName, menuItems[i].name );
        mvaddstr( y, x, buff );
        /* Lowlight after selected item */
        if( current == i ) /*attroff( A_REVERSE )*/ standend ();
        /* Calculate position for the next item */
        if( menuType & MENU_VERT )
        {
            y += 1;
            if( y >= WARNING_START )
            {
                y = ymargin;
                x += itemLength + MENU_SPACING;
                if( menuType & MENU_BUTTON ) x += 2;
            }
        }
        else
        {
            x += itemLength + MENU_SPACING;
            if( menuType & MENU_BUTTON ) x += 2;
            if( x > COLUMNS - lmargin - 12 )
            {
                x = lmargin;
                y ++ ;
            }
        }
    }
    /* Print the description of selected item */
    mvaddstr( WARNING_START + 1, (COLUMNS - strlen( menuItems[current].desc )) / 2, menuItems[current].desc );
    return y;
}

/* This function takes a list of menu items, lets the user choose one of them *
 * and returns the value keyboard shortcut of the selected menu item          */

int menuSelect( int y, int x, struct MenuItem *menuItems, int itemLength, char *available, int menuType, int menuDefault )
{
    int i, ylast = y, key = 0, current = menuDefault;
    if( !( menuType & ( MENU_HORIZ | MENU_VERT ) ) )	
    {
        print_warning("Menu without direction. Defaulting horizontal.");
        menuType |= MENU_HORIZ;
    }
    /* Make sure that the current is one of the available items */
    while( !strchr(available, menuItems[current].key) )
    {
        current ++ ;
        if( !menuItems[current].key ) current = 0;
    }
    /* Repeat until allowable choice has been made */
    while( !key )
    {
        /* Display the menu */
        ylast = menuUpdate( y, x, menuItems, itemLength, available, menuType, current );
        refresh();
        key = getch();
        /* Clear out all prompts and such */
        clear_warning();
        for( i = y; i < ylast; i ++ )
        {
            move( i, x );
            clrtoeol();
        }
        move( WARNING_START + 1, 0 );
        clrtoeol();
        /* Cursor keys */
        if( key == ESC )
        {
            /* Check whether this is a real ESC or one of extended keys */
            /*nodelay(stdscr, TRUE);*/
            key = getch();
            /*nodelay(stdscr, FALSE);*/
            if( key == /*ERR*/ ESC )
            {
                /* This is a real ESC */
                key = ESC;
            }
            if( key == '[' )
            {
                /* This is one extended keys */
                switch( getch() )
                {
                    case 'A': /* Up arrow */
                        if( menuType & MENU_VERT )
                        {
                            do {
                                current -- ;
                                if( current < 0 ) while( menuItems[current+1].key ) current ++ ;
                            } while( !strchr( available, menuItems[current].key ) );
                            key = 0;
                        }
                        else
                            key = MENU_UP;
                        break;
                    case 'B': /* Down arrow */
                        if( menuType & MENU_VERT )
                        {
                            do {
                                current ++ ;
                                if( !menuItems[current].key ) current = 0 ;
                            } while( !strchr( available, menuItems[current].key ) );
                            key = 0;
                        }
                        else
                            key = MENU_DOWN;
                        break;
                    case 'C': /* Right arrow */
                        if( menuType & MENU_HORIZ )
                        {
                            do {
                                current ++ ;
                                if( !menuItems[current].key ) 
                                {
                                    current = 0 ;
                                }
                            } while( !strchr( available, menuItems[current].key ) );
                            key = 0;
                        }
                        else
                            key = MENU_RIGHT;
                        break;
                    case 'D': /* Left arrow */
                        if( menuType & MENU_HORIZ )
                        {
                            do {
                                current -- ;
                                if( current < 0 )
                                {
                                    while( menuItems[current + 1].key ) current ++ ;
                                }
                            } while( !strchr( available, menuItems[current].key ) );
                            key = 0;
                        }
                        else
                            key = MENU_LEFT;
                        break;
                }
            }
        }
        /* Enter equals to the keyboard shortcut of current menu item */
        if( key == 13 )
        {
            key = menuItems[current].key;
        }
        /* Should all keys to be accepted? */
        if( key && (menuType & MENU_ACCEPT_OTHERS) ) break;
        /* Is pressed key among acceptable ones */
        if( key && (strchr(available, tolower(key)) || strchr(available, key)) ) break;
        /* The key has not been accepted so far -> let's reject it */
        if( key )
        {
            key = 0;
            putchar( BELL );
            print_warning("Illegal key");
        }
    }
    /* Clear out prompts and such */
    clear_warning();
    for( i = y; i <= ylast; i ++ )
    {
        move( i, x );
        clrtoeol();
    }
    move( WARNING_START + 1, 0 );
    clrtoeol();
    return key;
}

/* A function which displays "Press a key to continue" and waits for a keypress *
 * Perhaps calling function menuSelect is a bit overkill but who cares?         */

void menuContinue(void)
{
    static struct MenuItem menuContinueBtn[]=
    {
        { 'c', "", "Press a key to continue" },
        { 0, NULL, NULL }
    };

    menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X,
	menuContinueBtn, 0, "c", MENU_HORIZ | MENU_ACCEPT_OTHERS, 0 );
}

/* Function menuSelect takes way too many parameters  *
 * Luckily, most of time we can do with this function */

int menuSimple(struct MenuItem *menuItems, int menuDefault)
{
    int i, j, itemLength = 0;
    char available[MENU_MAX_ITEMS];
    for(i = 0; menuItems[i].key; i++)
    {
        j = strlen( menuItems[i].name );
        if( j > itemLength ) itemLength = j;
        available[i] = menuItems[i].key;
    }
    available[i] = 0;
    return menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuItems, itemLength,
        available, MENU_HORIZ | MENU_BUTTON, menuDefault);
}

/* End of command menu support code */

void new_part(int i)
{
    char response[LINE_LENGTH], def[LINE_LENGTH];
    char c;
    int first = p_info[i].first_sector;
    int last = p_info[i].last_sector;
    int offset = 0;
    int flags = 0;
    int id = LINUX;
    int num = -1;
    int num_sects = last - first + 1;
    int len, ext, j;

    if (p_info[i].num == PRI_OR_LOG) {
        static struct MenuItem menuPartType[]=
        {
            { 'p', "Primary", "Create a new primary partition" },
            { 'l', "Logical", "Create a new logical partition" },
            { ESC, "Cancel", "Don't create a partition" },
            { 0, NULL, NULL }
        };
        
	c = menuSimple( menuPartType, 0 );
	if (toupper(c) == 'P')
	    num = find_primary();
	else if (toupper(c) == 'L')
	    num = find_logical(i);
	else
	    return;
    } else if (p_info[i].num == PRIMARY)
	num = find_primary();
    else if (p_info[i].num == LOGICAL)
	num = find_logical(i);
    else
	print_warning("!!! Internal error !!!");

    sprintf(def, "%.2f", ceiling(num_sects/20.48)/100);
    mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, "Size (in MB): ");
    if ((len = get_string(response, LINE_LENGTH, def)) <= 0 &&
	len != GS_DEFAULT)
	return;
    else if (len > 0) {
#define num_cyls(bytes) (round_int(bytes/SECTOR_SIZE/(sectors*heads)))
	for (j = 0;
	     j < len-1 && (isdigit(response[j]) || response[j] == '.');
	     j++);
	if (toupper(response[j]) == 'K') {
	    num_sects = num_cyls(atof(response)*1024)*sectors*heads;
	} else if (toupper(response[j]) == 'M') {
	    num_sects = num_cyls(atof(response)*1024*1024)*sectors*heads;
	} else if (toupper(response[j]) == 'C') {
	    num_sects = round_int(atof(response))*sectors*heads;
	} else if (toupper(response[j]) == 'S') {
	    num_sects = round_int(atof(response));
	} else {
	    num_sects = num_cyls(atof(response)*1024*1024)*sectors*heads;
	}
    }

    if (num_sects <= 0 ||
	num_sects > p_info[i].last_sector - p_info[i].first_sector + 1)
	return;

    move( COMMAND_LINE_Y, COMMAND_LINE_X ); clrtoeol();
    if (num_sects < p_info[i].last_sector - p_info[i].first_sector + 1) {
	/* Determine where inside free space to put partition.
	 */
	static struct MenuItem menuPlace[]=
	{
	    { 'b', "Beginning", "Add partition at beginning of free space" },
	    { 'e', "End", "Add partition at end of free space" },
	    { ESC, "Cancel", "Don't create a partition" },
	    { 0, NULL, NULL }
	};
	c = menuSimple( menuPlace, 0 );
	if (toupper(c) == 'B')
	    last = first + num_sects - 1;
	else if (toupper(c) == 'E')
	    first = last - num_sects + 1;
	else
	    return;
    }

    if (IS_LOGICAL(num) && !is_extended(ext_info.id)) {
	/* We want to add a logical partition, but need to create an
	 * extended partition first.
	 */
	if ((ext = find_primary()) < 0) {
	    print_warning(NEED_EXT);
	    return;
	}
	(void) add_part(ext, DOS_EXTENDED, 0, first, last,
		       (first == 0 ? sectors : 0), 0);
	first = ext_info.first_sector + ext_info.offset;
    }

    if (IS_LOGICAL(num))
	inc_logical(i);

    /* Now we have a complete partition to ourselves */
    if (first == 0 || IS_LOGICAL(num))
	offset = sectors;

    (void) add_part(num, id, flags, first, last, offset, 0);
}

void clear_p_info(void)
{
    num_parts = 1;
    p_info[0].first_sector = 0;
    p_info[0].last_sector = sectors*heads*cylinders - 1;
    p_info[0].offset = 0;
    p_info[0].flags = 0;
    p_info[0].id = FREE_SPACE;
    p_info[0].num = PRI_OR_LOG;

    ext_info.first_sector = 0;
    ext_info.last_sector = 0;
    ext_info.offset = 0;
    ext_info.flags = 0;
    ext_info.id = FREE_SPACE;
    ext_info.num = PRIMARY;
}

void fill_p_info(void)
{
    int p, i;
    struct hd_geometry geometry;
    partition_table buffer;
    partition_info tmp_ext = { 0, 0, 0, 0, FREE_SPACE, PRIMARY };

    if ((fd = open(disk_device, O_RDWR)) < 0) {
	 if ((fd = open(disk_device, O_RDONLY)) < 0)
	      fatal(BAD_OPEN);
	 opentype = O_RDONLY;
	 print_warning(READONLY_WARN);
	 if (curses_started) {
	      refresh();
	      getch();
	      clear_warning();
	 }
    } else
	 opentype = O_RDWR;
    opened = TRUE;

    read_sector(buffer.c.b, 0);

    if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
	if (!heads)
	    heads = geometry.heads;
	if (!sectors)
	    sectors = geometry.sectors;
	if (!cylinders)
	    cylinders = geometry.cylinders;
    }

    if (!heads || !sectors || !cylinders)
	fatal(BAD_GEOMETRY);

    clear_p_info();

    if (!zero_table) {
	for (i = 0; i < 4; i++) {
	    int bs = buffer.p.part[i].start_sect;

	    if (buffer.p.part[i].sys_ind > 0 &&
		add_part(i,
			 buffer.p.part[i].sys_ind,
			 buffer.p.part[i].boot_ind,
			 ((bs <= sectors) ? 0 : bs),
			 buffer.p.part[i].start_sect +
			 buffer.p.part[i].nr_sects - 1,
			 ((bs <= sectors) ? bs : 0),
			 1)) {
		fatal(BAD_PRIMARY);
	    }
	    if (is_extended(buffer.p.part[i].sys_ind))
		tmp_ext = ext_info;
	}

	if (is_extended(tmp_ext.id)) {
	    ext_info = tmp_ext;
	    logical_sectors[logical] =
		 ext_info.first_sector + ext_info.offset;
	    read_sector(buffer.c.b, logical_sectors[logical++]);
	    i = 4;
	    do {
		for (p = 0;
		     p < 4 && (!buffer.p.part[p].sys_ind ||
			       is_extended(buffer.p.part[p].sys_ind));
		     p++);

		if (p < 4 && add_part(i++,
			     buffer.p.part[p].sys_ind,
			     buffer.p.part[p].boot_ind,
			     logical_sectors[logical-1],
			     logical_sectors[logical-1] +
			     buffer.p.part[p].start_sect +
			     buffer.p.part[p].nr_sects - 1,
			     buffer.p.part[p].start_sect,
			     1)) {
		    fatal(BAD_LOGICAL);
		}

		for (p = 0;
		     p < 4 && !is_extended(buffer.p.part[p].sys_ind);
		     p++);
		if (p < 4) {
		    logical_sectors[logical] = ext_info.first_sector
			 + ext_info.offset + buffer.p.part[p].start_sect;
		    read_sector(buffer.c.b, logical_sectors[logical++]);
		}
	    } while (p < 4 && logical < MAXIMUM_PARTS-4);
	}
    }
}

void fill_part_table(struct partition *p, partition_info *pi)
{
    int sects;

    p->boot_ind = pi->flags;
    p->sys_ind = pi->id;
    if (IS_LOGICAL(pi->num))
	p->start_sect = pi->offset;
    else
	p->start_sect = pi->first_sector + pi->offset;
    p->nr_sects = pi->last_sector - (pi->first_sector+pi->offset) + 1;
    sects = (((pi->first_sector+pi->offset)/(sectors*heads) > 1023) ?
	     heads*sectors*1024 - 1 : pi->first_sector+pi->offset);
    set_hsc(p->head, p->sector, p->cyl, sects);
    sects = ((pi->last_sector/(sectors*heads) > 1023) ?
	     heads*sectors*1024 - 1 : pi->last_sector);
    set_hsc(p->end_head, p->end_sector, p->end_cyl, sects);
}

void fill_primary_table(partition_table *buffer)
{
    int i;

    /* Zero out existing table */
    for (i = 0x1BE; i < SECTOR_SIZE; i++)
	buffer->c.b[i] = 0;

    for (i = 0; i < num_parts; i++)
	if (IS_PRIMARY(p_info[i].num))
	    fill_part_table(&(buffer->p.part[p_info[i].num]), &(p_info[i]));

    if (is_extended(ext_info.id))
	fill_part_table(&(buffer->p.part[ext_info.num]), &ext_info);

    buffer->p.flag = PART_TABLE_FLAG;
}

void fill_logical_table(partition_table *buffer, partition_info *pi)
{
    struct partition *p;
    int i, sects;

    for (i = 0; i < logical && pi->first_sector != logical_sectors[i]; i++);
    if (i == logical || buffer->p.flag != (unsigned short)PART_TABLE_FLAG)
	for (i = 0; i < SECTOR_SIZE; i++)
	    buffer->c.b[i] = 0;

    /* Zero out existing table */
    for (i = 0x1BE; i < SECTOR_SIZE; i++)
	buffer->c.b[i] = 0;

    fill_part_table(&(buffer->p.part[0]), pi);

    for (i = 0;
	 i < num_parts && pi->num != p_info[i].num - 1;
	 i++);

    if (i < num_parts) {
	p = &(buffer->p.part[1]);
	pi = &(p_info[i]);

	p->boot_ind = 0;
	p->sys_ind = DOS_EXTENDED;
	p->start_sect = pi->first_sector - ext_info.first_sector - ext_info.offset;
	p->nr_sects = pi->last_sector - pi->first_sector + 1;
	sects = ((pi->first_sector/(sectors*heads) > 1023) ?
		 heads*sectors*1024 - 1 : pi->first_sector);
	set_hsc(p->head, p->sector, p->cyl, sects);
	sects = ((pi->last_sector/(sectors*heads) > 1023) ?
		 heads*sectors*1024 - 1 : pi->last_sector);
	set_hsc(p->end_head, p->end_sector, p->end_cyl, sects);
    }

    buffer->p.flag = PART_TABLE_FLAG;
}

void write_part_table(void)
{
    int i, done = FALSE, len;
    partition_table buffer;
    struct stat s;
    int is_bdev;
    char response[LINE_LENGTH];

    if (opentype == O_RDONLY) {
	 print_warning(READONLY_WARN);
	 refresh();
	 getch();
	 clear_warning();
	 return;
    }

    is_bdev = 0;
    if(fstat(fd, &s) == 0 && S_ISBLK(s.st_mode))
	 is_bdev = 1;

    if (is_bdev) {
	 print_warning(WRITE_WARN);

	 while (!done) {
	      mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
		       "Are you sure you want write the partition table "
		       "to disk? (yes or no): ");
	      len = get_string(response, LINE_LENGTH, NULL);
	      clear_warning();
	      if (len == GS_ESCAPE)
		   return;
	      else if (len == 2 &&
		       toupper(response[0]) == 'N' &&
		       toupper(response[1]) == 'O') {
		   print_warning(NO_WRITE);
		   return;
	      } else if (len == 3 &&
			 toupper(response[0]) == 'Y' &&
			 toupper(response[1]) == 'E' &&
			 toupper(response[2]) == 'S')
		   done = TRUE;
	      else
		   print_warning(YES_NO);
	 }

	 clear_warning();
	 print_warning(WRITING_PART);
	 refresh();
    }

    read_sector(buffer.c.b, 0);
    fill_primary_table(&buffer);
    write_sector(buffer.c.b, 0);

    for (i = 0; i < num_parts; i++)
	if (IS_LOGICAL(p_info[i].num)) {
	    read_sector(buffer.c.b, p_info[i].first_sector);
	    fill_logical_table(&buffer, &(p_info[i]));
	    write_sector(buffer.c.b, p_info[i].first_sector);
	}

    if (is_bdev) {
	 sync();
	 sleep(2);
	 if (!ioctl(fd,BLKRRPART))
	      changed = TRUE;
	 sync();
	 sleep(4);

	 clear_warning();
	 if (changed)
	      print_warning(YES_WRITE);
	 else
	      print_warning(RRPART_FAILED);
    } else
	 print_warning(YES_WRITE);
}

void fp_printf(FILE *fp, char *format, ...)
{
    va_list args;
    char buf[1024];
    int y, x;

    va_start(args, format);
    vsprintf(buf, format, args);
    va_end(args);

    if (fp == NULL) {
	/* The following works best if the string to be printed has at
           most only one newline. */
	printw("%s", buf);
	getyx(stdscr, y, x);
	if (y >= COMMAND_LINE_Y-2) {
	    menuContinue();
	    erase();
	    move(0, 0);
	}
    } else
	fprintf(fp, "%s", buf);
}

#define MAX_PER_LINE 16
void print_file_buffer(FILE *fp, char *buffer)
{
    int i,l;

    for (i = 0, l = 0; i < SECTOR_SIZE; i++, l++) {
	if (l == 0)
	    fp_printf(fp, "0x%03X:", i);
	fp_printf(fp, " %02X", (unsigned char) buffer[i]);
	if (l == MAX_PER_LINE - 1) {
	    fp_printf(fp, "\n");
	    l = -1;
	}
    }
    if (l > 0)
	fp_printf(fp, "\n");
    fp_printf(fp, "\n");
}

void print_raw_table(void)
{
    int i, to_file;
    partition_table buffer;
    char fname[LINE_LENGTH];
    FILE *fp;

    if (print_only) {
	fp = stdout;
	to_file = TRUE;
    } else {
	mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
		 "Enter filename or press RETURN to display on screen: ");

	if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
	    return;

	if (to_file) {
	    if ((fp = fopen(fname, "w")) == NULL) {
		char errstr[LINE_LENGTH];
		sprintf(errstr, PRINT_OPEN_ERR, fname);
		print_warning(errstr);
		return;
	    }
	} else {
	    fp = NULL;
	    erase();
	    move(0, 0);
	}
    }

    fp_printf(fp, "Disk Drive: %s\n", disk_device);

    fp_printf(fp, "Sector 0:\n");
    read_sector(buffer.c.b, 0);
    fill_primary_table(&buffer);
    print_file_buffer(fp, buffer.c.b);

    for (i = 0; i < num_parts; i++)
	if (IS_LOGICAL(p_info[i].num)) {
	    fp_printf(fp, "Sector %d:\n", p_info[i].first_sector);
	    read_sector(buffer.c.b, p_info[i].first_sector);
	    fill_logical_table(&buffer, &(p_info[i]));
	    print_file_buffer(fp, buffer.c.b);
	}

    if (to_file) {
	if (!print_only)
	    fclose(fp);
    } else {
        menuContinue();
    }
}

void print_p_info_entry(FILE *fp, partition_info *p)
{
    int size;
    char part_str[40];

    if (p->id == UNUSABLE)
	fp_printf(fp, "   None   ");
    else if (p->id == FREE_SPACE && p->num == PRI_OR_LOG)
	fp_printf(fp, "   Pri/Log");
    else if (p->id == FREE_SPACE && p->num == PRIMARY)
	fp_printf(fp, "   Primary");
    else if (p->id == FREE_SPACE && p->num == LOGICAL)
	fp_printf(fp, "   Logical");
    else
	fp_printf(fp, "%2d %-7.7s", p->num+1,
		  IS_LOGICAL(p->num) ? "Logical" : "Primary");

    fp_printf(fp, " ");

    fp_printf(fp, "%8d%c", p->first_sector,
	      ((p->first_sector/(sectors*heads)) !=
	       ((float)p->first_sector/(sectors*heads)) ?
	       '*' : ' '));

    fp_printf(fp, "%8d%c", p->last_sector,
	      (((p->last_sector+1)/(sectors*heads)) !=
	       ((float)(p->last_sector+1)/(sectors*heads)) ?
	       '*' : ' '));

    fp_printf(fp, "%7d%c", p->offset,
	      ((((p->first_sector == 0 || IS_LOGICAL(p->num)) &&
		 (p->offset != sectors)) ||
		(p->first_sector != 0 && IS_PRIMARY(p->num) &&
		 p->offset != 0)) ?
	       '#' : ' '));

    size = p->last_sector - p->first_sector + 1;
    fp_printf(fp, "%8d%c", size,
	      ((size/(sectors*heads)) != ((float)size/(sectors*heads)) ?
	       '*' : ' '));

    fp_printf(fp, " ");

    if (p->id == UNUSABLE)
	sprintf(part_str, "%.17s", "Unusable");
    else if (p->id == FREE_SPACE)
	sprintf(part_str, "%.17s", "Free Space");
    else if (partition_type[p->id])
	sprintf(part_str, "%.17s (%02X)", partition_type[p->id], p->id);
    else
	sprintf(part_str, "%.17s (%02X)", "Unknown", p->id);
    fp_printf(fp, "%-22.22s", part_str);

    fp_printf(fp, " ");

    if (p->flags == ACTIVE_FLAG)
	fp_printf(fp, "Boot (%02X)", p->flags);
    else if (p->flags != 0)
	fp_printf(fp, "Unknown (%02X)", p->flags);
    else
	fp_printf(fp, "None (%02X)", p->flags);

    fp_printf(fp, "\n");
}

void print_p_info(void)
{
    char fname[LINE_LENGTH];
    FILE *fp;
    int i, to_file, pext = is_extended(ext_info.id);

    if (print_only) {
	fp = stdout;
	to_file = TRUE;
    } else {
	mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
		 "Enter filename or press RETURN to display on screen: ");

	if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
	    return;

	if (to_file) {
	    if ((fp = fopen(fname, "w")) == NULL) {
		char errstr[LINE_LENGTH];
		sprintf(errstr, PRINT_OPEN_ERR, fname);
		print_warning(errstr);
		return;
	    }
	} else {
	    fp = NULL;
	    erase();
	    move(0, 0);
	}
    }

    fp_printf(fp, "Partition Table for %s\n", disk_device);
    fp_printf(fp, "\n");
    fp_printf(fp, "            First    Last\n");
    fp_printf(fp, " # Type     Sector   Sector   Offset  Length   Filesystem Type (ID)   Flags\n");
    fp_printf(fp, "-- ------- -------- --------- ------ --------- ---------------------- ---------\n");

    for (i = 0; i < num_parts; i++) {
	if (pext && (p_info[i].first_sector >= ext_info.first_sector)) {
	    print_p_info_entry(fp,&ext_info);
	    pext = FALSE;
	}
	print_p_info_entry(fp, &(p_info[i]));
    }

    if (to_file) {
	if (!print_only)
	    fclose(fp);
    } else {
        menuContinue();
    }
}

void print_part_entry(FILE *fp, int num, partition_info *pi)
{
    int first = 0, start = 0, end = 0, size = 0;
    int ss = 0, sh = 0, sc = 0;
    int es = 0, eh = 0, ec = 0;
    int flags = 0, id = 0;

    if (pi != NULL) {
	flags = pi->flags;
	id = pi->id;

	if (IS_LOGICAL(num))
	    first = pi->offset;
	else
	    first = pi->first_sector + pi->offset;

	start = pi->first_sector + pi->offset;
	end = pi->last_sector;
	size = end - start + 1;
	if ((start/(sectors*heads)) > 1023)
	    start = heads*sectors*1024 - 1;
	if ((end/(sectors*heads)) > 1023)
	    end = heads*sectors*1024 - 1;

	ss = start % sectors + 1;
	start /= sectors;
	sh = start % heads;
	sc = start / heads;

	es = end % sectors + 1;
	end /= sectors;
	eh = end % heads;
	ec = end / heads;
    }

    fp_printf(fp, "%2d  0x%02X %4d %4d %4d 0x%02X %4d %4d %4d %7d %9d\n",
	      num+1, flags, sh, ss, sc, id, eh, es, ec, first, size);
}


void print_part_table(void)
{
    int i, j, to_file;
    char fname[LINE_LENGTH];
    FILE *fp;

    if (print_only) {
	fp = stdout;
	to_file = TRUE;
    } else {
	mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
		 "Enter filename or press RETURN to display on screen: ");

	if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
	    return;

	if (to_file) {
	    if ((fp = fopen(fname, "w")) == NULL) {
		char errstr[LINE_LENGTH];
		sprintf(errstr, PRINT_OPEN_ERR, fname);
		print_warning(errstr);
		return;
	    }
	} else {
	    fp = NULL;
	    erase();
	    move(0, 0);
	}
    }

    fp_printf(fp, "Partition Table for %s\n", disk_device);
    fp_printf(fp, "\n");
    fp_printf(fp, "         ---Starting---      ----Ending----   Start Number of\n");
    fp_printf(fp, " # Flags Head Sect Cyl   ID  Head Sect Cyl   Sector  Sectors\n");
    fp_printf(fp, "-- ----- ---- ---- ---- ---- ---- ---- ---- ------- ---------\n");

    for (i = 0; i < 4; i++) {
	for (j = 0;
	     j < num_parts && (p_info[j].id <= 0 || p_info[j].num != i);
	     j++);
	if (j < num_parts) {
	    print_part_entry(fp, i, &(p_info[j]));
	} else if (is_extended(ext_info.id) && ext_info.num == i) {
	    print_part_entry(fp, i, &ext_info);
	} else {
	    print_part_entry(fp, i, NULL);
	}
    }

    for (i = 0; i < num_parts; i++)
	if (IS_LOGICAL(p_info[i].num))
	    print_part_entry(fp, p_info[i].num, &(p_info[i]));

    if (to_file) {
	if (!print_only)
	    fclose(fp);
    } else {
        menuContinue();
    }
}

void print_tables(void)
{
    int done = FALSE;

    static struct MenuItem menuFormat[]=
    {
        { 'r', "Raw", "Print the table using raw data format" },
        { 's', "Sectors", "Print the table ordered by sectors" },
        { 't', "Table", "Just print the partition table" },
        { ESC, "Cancel", "Don't print the table" },
        { 0, NULL, NULL }
    };
    
    while (!done)
	switch ( toupper(menuSimple( menuFormat, 2)) ) {
	case 'R':
	    print_raw_table();
	    done = TRUE;
	    break;
	case 'S':
	    print_p_info();
	    done = TRUE;
	    break;
	case 'T':
	    print_part_table();
	    done = TRUE;
	    break;
	case ESC:
	    done = TRUE;
	    break;
	}
}

#define END_OF_HELP "EOHS!"
#define NEW_HELP_SCREEN "SNHS!"
void display_help()
{
    char *help_text[] = {
	"Help Screen for cfdisk " VERSION,
	"",
	"This is cfdisk, a curses based disk partitioning programs, which",
	"allows you to create, delete and modify partitions on your hard",
	"disk drive.",
	"",
	"Copyright (C) 1994-1997 Kevin E. Martin & aeb",
	"",
	"Command      Meaning",
	"-------      -------",
	"  b          Toggle bootable flag of the current partition",
	"  d          Delete the current partition",
	"  g          Change cylinders, heads, sectors-per-track parameters",
	"             WARNING: This option should only be used by people who",
	"             know what they are doing.",
	"  h          Print this screen",
	"  m          Maximize disk usage of the current partition",
	"             Note: This may make the partition incompatible with",
	"             DOS, OS/2, ...",
	"  n          Create new partition from free space",
	"  p          Print partition table to the screen or to a file",
	"             There are several different formats for the partition",
	"             that you can choose from:",
	"                r - Raw data (exactly what would be written to disk)",
	"                s - Table ordered by sectors",
	"                t - Table in raw format",
	"  q          Quit program without writing partition table",
	"  t          Change the filesystem type",
	"  u          Change units of the partition size display",
	"             Rotates through Mb, sectors and cylinders",
	"  W          Write partition table to disk (must enter upper case W)",
        "             Since this might destroy data on the disk, you must",
	"             either confirm or deny the write by entering `yes' or",
	"             `no'",
	"Up Arrow     Move cursor to the previous partition",
	"Down Arrow   Move cursor to the next partition",
	"CTRL-L       Redraws the screen",
	"  ?          Print this screen",
	"",
	"Note: All of the commands can be entered with either upper or lower",
	"case letters (except for Writes).",
	END_OF_HELP
    };

    int cur_line = 0;
    FILE *fp = NULL;

    erase();
    move(0, 0);
    while (strcmp(help_text[cur_line], END_OF_HELP))
	if (!strcmp(help_text[cur_line], NEW_HELP_SCREEN)) {
	    menuContinue();
	    erase();
	    move(0, 0);
	    cur_line++;
	} else
	    fp_printf(fp, "%s\n", help_text[cur_line++]);

    menuContinue();
}

int change_geometry(void)
{
    int ret_val = FALSE;
    int done = FALSE;
    char def[LINE_LENGTH];
    char response[LINE_LENGTH];
    int tmp_val;

    while (!done) {
        static struct MenuItem menuGeometry[]=
        {
            { 'c', "Cylinders", "Change cylinder geometry" },
            { 'h', "Heads", "Change head geometry" },
            { 's', "Sectors", "Change sector geometry" },
            { 'd', "Done", "Done with changing geometry" },
            { 0, NULL, NULL }
        };
	move(COMMAND_LINE_Y, COMMAND_LINE_X);
	clrtoeol();
	refresh();

	clear_warning();

	switch (toupper( menuSimple(menuGeometry, 3) )) {
	case 'C':
	    sprintf(def, "%d", cylinders);
	    mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
		     "Enter the number of cylinders: ");
	    if (get_string(response, LINE_LENGTH, def) > 0) {
		tmp_val = atoi(response);
		if (tmp_val > 0 && tmp_val <= MAX_CYLINDERS) {
		    cylinders = tmp_val;
		    ret_val = TRUE;
		} else
		    print_warning(BAD_CYLINDERS);
	    }
	    break;
	case 'H':
	    sprintf(def, "%d", heads);
	    mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
		     "Enter the number of heads: ");
	    if (get_string(response, LINE_LENGTH, def) > 0) {
		tmp_val = atoi(response);
		if (tmp_val > 0 && tmp_val <= MAX_HEADS) {
		    heads = tmp_val;
		    ret_val = TRUE;
		} else
		    print_warning(BAD_HEADS);
	    }
	    break;
	case 'S':
	    sprintf(def, "%d", sectors);
	    mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
		     "Enter the number of sectors per track: ");
	    if (get_string(response, LINE_LENGTH, def) > 0) {
		tmp_val = atoi(response);
		if (tmp_val > 0 && tmp_val <= MAX_SECTORS) {
		    sectors = tmp_val;
		    ret_val = TRUE;
		} else
		    print_warning(BAD_SECTORS);
	    }
	    break;
	case ESC:
	case 'D':
	    done = TRUE;
	    break;
	default:
	    putchar(BELL);
	    break;
	}
    }

    if (ret_val) {
	int disk_end = heads*sectors*cylinders-1;

	if (p_info[num_parts-1].last_sector > disk_end) {
	    while (p_info[num_parts-1].first_sector > disk_end) {
		if (p_info[num_parts-1].id == FREE_SPACE ||
		    p_info[num_parts-1].id == UNUSABLE)
		    remove_part(num_parts-1);
		else
		    del_part(num_parts-1);
	    }

	    p_info[num_parts-1].last_sector = disk_end;

	    if (ext_info.last_sector > disk_end)
		ext_info.last_sector = disk_end;
	} else if (p_info[num_parts-1].last_sector < disk_end) {
	    if (p_info[num_parts-1].id == FREE_SPACE ||
		p_info[num_parts-1].id == UNUSABLE) {
		p_info[num_parts-1].last_sector = disk_end;
	    } else {
		insert_empty_part(num_parts,
				  p_info[num_parts-1].last_sector+1,
				  disk_end);
	    }
	}

	/* Make sure the partitions are correct */
	check_part_info();
    }

    return ret_val;
}

void change_id(int i)
{
    char id[LINE_LENGTH], def[LINE_LENGTH];
    int num_types = 0;
    int num_across, num_down;
    int len, new_id = LINUX;
    int y_start, y_end;
    int j, pos;

    for (num_types = 0, j = 1; j < NUM_PART_TYPES; j++)
	if (partition_type[j])
	    num_types++;

    num_across = COLS/COL_ID_WIDTH;
    num_down = (((float)num_types)/num_across + 1);
    y_start = COMMAND_LINE_Y - 1 - num_down;
    if (y_start > DISK_TABLE_START+cur_part+4)
	y_start = DISK_TABLE_START+cur_part+4;
    y_end = y_start + num_down - 1;

    for (j = y_start - 1; j <= y_end + 1; j++) {
	move(j, 0);
	clrtoeol();
    }

    for (pos = 0, j = 1; j < NUM_PART_TYPES; j++)
	if (partition_type[j]) {
	    move(y_start + pos % num_down, (pos/num_down)*COL_ID_WIDTH + 1);
	    printw("%02X %-16.16s", j, partition_type[j]);
	    pos++;
	}

    sprintf(def, "%02X", new_id);
    mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, "Enter filesystem type: ");
    if ((len = get_string(id, 2, def)) <= 0 && len != GS_DEFAULT)
	return;

    if (len != GS_DEFAULT) {
	if (!isxdigit(id[0]))
	    return;
	new_id = (isdigit(id[0]) ? id[0] - '0' : tolower(id[0]) - 'a' + 10);
	if (len == 2)
	    if (isxdigit(id[1]))
		new_id = new_id*16 +
		    (isdigit(id[1]) ? id[1] - '0' : tolower(id[1]) - 'a' + 10);
	    else
		return;
    }

    if (new_id == 0)
	print_warning(ID_EMPTY);
    else if (is_extended(new_id))
	print_warning(ID_EXT);
    else
	p_info[i].id = new_id;
}

void draw_partition(int i)
{
    int size, j;
    int y = i + DISK_TABLE_START + 2 - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN;

    if (!arrow_cursor) {
	move(y, 0);
	for (j = 0; j < COLS; j++)
	    addch(' ');
    }

    if (p_info[i].id > 0) {
	mvprintw(y, NAME_START,
		 "%s%d", disk_device, p_info[i].num+1);
	if (p_info[i].flags) {
	    if (p_info[i].flags == ACTIVE_FLAG)
		mvaddstr(y, FLAGS_START, "Boot");
	    else
		mvprintw(y, FLAGS_START, "Unk(%02X)", p_info[i].flags);
	    if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) {
		if (p_info[i].offset != sectors)
		    addstr(", NC");
	    } else {
		if (p_info[i].offset != 0)
		    addstr(", NC");
	    }
	} else {
	    if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) {
		if (p_info[i].offset != sectors)
		    mvaddstr(y, FLAGS_START, "NC");
	    } else {
		if (p_info[i].offset != 0)
		    mvaddstr(y, FLAGS_START, "NC");
	    }
	}
    }
    mvaddstr(y, PTYPE_START,
	     (p_info[i].id == UNUSABLE ? "" :
	      (IS_LOGICAL(p_info[i].num) ? "Logical" :
	       (p_info[i].num >= 0 ? "Primary" :
		(p_info[i].num == PRI_OR_LOG ? "Pri/Log" :
		 (p_info[i].num == PRIMARY ? "Primary" : "Logical"))))));
    if (p_info[i].id == UNUSABLE)
	mvaddstr(y, FSTYPE_START, "Unusable");
    else if (p_info[i].id == FREE_SPACE)
	mvaddstr(y, FSTYPE_START, "Free Space");
    else if (partition_type[p_info[i].id])
	mvaddstr(y, FSTYPE_START, partition_type[p_info[i].id]);
    else
	mvprintw(y, FSTYPE_START, "Unknown (%02X)", p_info[i].id);

    if (p_info[i].dos_label[0]) {
	int l = strlen(p_info[i].dos_label);
	mvprintw(y, SIZE_START-5-l, " [%s]  ", p_info[i].dos_label);
    }

    size = p_info[i].last_sector - p_info[i].first_sector + 1;
    if (display_units == SECTORS)
	mvprintw(y, SIZE_START, "%9d", size);
    else if (display_units == CYLINDERS)
	mvprintw(y, SIZE_START, "%9d", size/(sectors*heads));
    else
	mvprintw(y, SIZE_START, "%9.2f", ceiling(size/20.48)/100);
    if (((size/(sectors*heads)) != ceiling(size/(sectors*(float)heads))) ||
	((p_info[i].first_sector/(sectors*heads)) !=
	 ceiling(p_info[i].first_sector/(sectors*heads))))
	mvprintw(y, COLUMNS-1, "*");
}

void init_const(void)
{
    if (!defined) {
	NAME_START = (((float)NAME_START)/COLUMNS)*COLS;
	FLAGS_START = (((float)FLAGS_START)/COLUMNS)*COLS;
	PTYPE_START = (((float)PTYPE_START)/COLUMNS)*COLS;
	FSTYPE_START = (((float)FSTYPE_START)/COLUMNS)*COLS;
	SIZE_START = (((float)SIZE_START)/COLUMNS)*COLS;
	COMMAND_LINE_X = (((float)COMMAND_LINE_X)/COLUMNS)*COLS;

	COMMAND_LINE_Y = LINES - 4;
	WARNING_START = LINES - 2;

	if ((NUM_ON_SCREEN = COMMAND_LINE_Y - DISK_TABLE_START - 3) <= 0)
	    NUM_ON_SCREEN = 1;

	COLUMNS = COLS;
	defined = TRUE;
    }
}

void draw_screen(void)
{
    int i;
    char *line;

    line = (char *)malloc((COLS+1)*sizeof(char));

    if (warning_last_time) {
	for (i = 0; i < COLS; i++) {
	    move(WARNING_START, i);
	    line[i] = inch();
	}
	line[COLS] = 0;
    }

    erase();

    if (warning_last_time)
	mvaddstr(WARNING_START, 0, line);


    sprintf(line, "cfdisk %s", VERSION);
    mvaddstr(HEADER_START, (COLS-strlen(line))/2, line);
    sprintf(line, "Disk Drive: %s", disk_device);
    mvaddstr(HEADER_START+2, (COLS-strlen(line))/2, line);
    sprintf(line, "Heads: %d   Sectors per Track: %d   Cylinders: %d",
	    heads, sectors, cylinders);
    mvaddstr(HEADER_START+3, (COLS-strlen(line))/2, line);

    mvaddstr(DISK_TABLE_START, NAME_START, "Name");
    mvaddstr(DISK_TABLE_START, FLAGS_START, "Flags");
    mvaddstr(DISK_TABLE_START, PTYPE_START, "Part Type");
    mvaddstr(DISK_TABLE_START, FSTYPE_START, "FS Type");
    if (display_units == SECTORS)
	mvaddstr(DISK_TABLE_START, SIZE_START, "  Sectors");
    else if (display_units == CYLINDERS)
	mvaddstr(DISK_TABLE_START, SIZE_START, "Cylinders");
    else
	mvaddstr(DISK_TABLE_START, SIZE_START, "Size (MB)");

    move(DISK_TABLE_START+1, 1);
    for (i = 1; i < COLS-1; i++)
	addch('-');

    if (NUM_ON_SCREEN >= num_parts)
	for (i = 0; i < num_parts; i++)
	    draw_partition(i);
    else
	for (i = (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN;
	     i < NUM_ON_SCREEN + (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN &&
	     i < num_parts;
	     i++)
	    draw_partition(i);

    free(line);
}

int draw_cursor(int move)
{
    if (move != 0 && (cur_part + move < 0 || cur_part + move >= num_parts))
	return -1;

    if (arrow_cursor)
	mvaddstr(DISK_TABLE_START + cur_part + 2
		 - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, "   ");
    else
	draw_partition(cur_part);

    cur_part += move;

    if (((cur_part - move)/NUM_ON_SCREEN)*NUM_ON_SCREEN !=
	(cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN)
	draw_screen();

    if (arrow_cursor)
	mvaddstr(DISK_TABLE_START + cur_part + 2
		 - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, "-->");
    else {
	standout();
	draw_partition(cur_part);
	standend();
    }

    return 0;
}

void do_curses_fdisk(void)
{
    int done = FALSE;
    char command;

    static struct MenuItem menuMain[]=
    {
        { 'b', "Bootable", "Toggle bootable flag of the current partition" },
        { 'd', "Delete", "Delete the current partition" },
        { 'g', "Geometry", "Change disk geometry (experts only)" },
        { 'h', "Help", "Print help screen" },
        { 'm', "Maximize", "Maximize disk usage of the current partition (experts only)" },
        { 'n', "New", "Create new partition from free space" },
        { 'p', "Print", "Print partition table to the screen or to a file" },
        { 'q', "Quit", "Quit program without writing partition table" },
        { 't', "Type", "Change the filesystem type (DOS, Linux, OS/2 and so on)" },
        { 'u', "Units", "Change units of the partition size display (MB, sect, cyl)" },
        { 'W', "Write", "Write partition table to disk (this might destroy data)" },
        { 0, NULL, NULL }
    };
    curses_started = 1;
    initscr();
    old_SIGINT = signal(SIGINT, die);
    old_SIGTERM = signal(SIGTERM, die);
#ifdef DEBUG
    signal(SIGINT, old_SIGINT);
    signal(SIGTERM, old_SIGTERM);
#endif

    cbreak();
    noecho();
    nonl();

    init_const();

    fill_p_info();

    draw_screen();

    while (!done) {
	char *s;

	(void)draw_cursor(0);

	if (p_info[cur_part].id == FREE_SPACE) {
	    s = ((opentype == O_RDWR) ? "hnpquW" : "hnpqu");
	    command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8,
	        s, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
	} else if (p_info[cur_part].id > 0) {
	    s = ((opentype == O_RDWR) ? "bdhmpqtuW" : "bdhmpqtu");
	    command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8,
	        s, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
	} else {
	    s = ((opentype == O_RDWR) ? "hpquW" : "hpqu");
	    command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8,
	        s, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
	}
	switch ( command ) {
	case 'B':
	case 'b':
	    if (p_info[cur_part].id > 0)
		p_info[cur_part].flags ^= 0x80;
	    else
		print_warning(NO_FLAGS);
	    break;
	case 'D':
	case 'd':
	    if (p_info[cur_part].id > 0) {
		del_part(cur_part);
		if (cur_part >= num_parts)
		    cur_part = num_parts - 1;
		draw_screen();
	    } else
		print_warning(DEL_EMPTY);
	    break;
	case 'G':
	case 'g':
	    if (change_geometry())
		draw_screen();
	    break;
	case 'M':
	case 'm':
	    if (p_info[cur_part].id > 0) {
		if (p_info[cur_part].first_sector == 0 ||
		    IS_LOGICAL(p_info[cur_part].num)) {
		    if (p_info[cur_part].offset == sectors)
			p_info[cur_part].offset = 1;
		    else
			p_info[cur_part].offset = sectors;
		    draw_screen();
		} else if (p_info[cur_part].offset != 0)
		    p_info[cur_part].offset = 0;
		else
		    print_warning(MAX_UNMAXABLE);
	    } else
		print_warning(MAX_UNMAXABLE);
	    break;
	case 'N':
	case 'n':
	    if (p_info[cur_part].id == FREE_SPACE) {
		new_part(cur_part);
		draw_screen();
	    } else if (p_info[cur_part].id == UNUSABLE)
		print_warning(ADD_UNUSABLE);
	    else
		print_warning(ADD_EXISTS);
	    break;
	case 'P':
	case 'p':
	    print_tables();
	    draw_screen();
	    break;
	case 'Q':
	case 'q':
	    done = TRUE;
	    break;
	case 'T':
	case 't':
	    if (p_info[cur_part].id > 0) {
		change_id(cur_part);
		draw_screen();
	    } else
		print_warning(TYPE_EMPTY);
	    break;
	case 'U':
	case 'u':
	    if (display_units == MEGABYTES)
		display_units = SECTORS;
	    else if (display_units == SECTORS)
		display_units = CYLINDERS;
	    else if (display_units == CYLINDERS)
		display_units = MEGABYTES;
	    draw_screen();
	    break;
	case 'W':
	    write_part_table();
	    break;
	case 'H':
	case 'h':
	case '?':
	    display_help();
	    draw_screen();
	    break;
	case MENU_UP : /* Up arrow */
	    if (!draw_cursor(-1))
		command = 0;
	    else
		print_warning(NO_MORE_PARTS);
	    break;
	case MENU_DOWN : /* Down arrow */
	    if (!draw_cursor(1))
		command = 0;
	    else
		print_warning(NO_MORE_PARTS);
	    break;
	case REDRAWKEY:
	    clear();
	    draw_screen();
	    break;
	default:
	    print_warning(BAD_COMMAND);
	    putchar(BELL); /* CTRL-G */
	}
    }

    die_x(0);
}

void copyright(void)
{
    fprintf(stderr, "Copyright (C) 1994-1997 Kevin E. Martin & aeb\n");
}

void usage(char *prog_name)
{
    fprintf(stderr, "\nUsage:\n");
    fprintf(stderr, "Print version:\n");
    fprintf(stderr, "\t%s -v\n", prog_name);
    fprintf(stderr, "Print partition table:\n");
    fprintf(stderr, "\t%s -P {r|s|t} [options] device\n", prog_name);
    fprintf(stderr, "Interactive use:\n");
    fprintf(stderr, "\t%s [options] device\n", prog_name);
    fprintf(stderr, "
Options:
-a: Use arrow instead of highlighting;
-z: Start with a zero partition table, instead of reading the pt from disk;
-c C -h H -s S: Override the kernel's idea of the number of cylinders,
                the number of heads and the number of sectors/track.\n\n");

    copyright();
}

int main(int argc, char **argv)
{
    char c;
    int i, len;

    while ((c = getopt(argc, argv, "ac:h:s:vzP:")) != EOF)
	switch (c) {
	case 'a':
	    arrow_cursor = TRUE;
	    break;
	case 'c':
	    cylinders = atoi(optarg);
	    if (cylinders <= 0 || cylinders > MAX_CYLINDERS) {
		fprintf(stderr, "%s: %s\n", argv[0], BAD_CYLINDERS);
		exit(1);
	    }
	    break;
	case 'h':
	    heads = atoi(optarg);
	    if (heads <= 0 || heads > MAX_HEADS) {
		fprintf(stderr, "%s: %s\n", argv[0], BAD_HEADS);
		exit(1);
	    }
	    break;
	case 's':
	    sectors = atoi(optarg);
	    if (sectors <= 0 || sectors > MAX_SECTORS) {
		fprintf(stderr, "%s: %s\n", argv[0], BAD_SECTORS);
		exit(1);
	    }
	    break;
	case 'v':
	    fprintf(stderr, "cfdisk %s\n", VERSION);
	    copyright();
	    exit(0);
	case 'z':
	    zero_table = TRUE;
	    break;
	case 'P':
	    len = strlen(optarg);
	    for (i = 0; i < len; i++) {
		switch (optarg[i]) {
		case 'r':
		    print_only |= PRINT_RAW_TABLE;
		    break;
		case 's':
		    print_only |= PRINT_SECTOR_TABLE;
		    break;
		case 't':
		    print_only |= PRINT_PARTITION_TABLE;
		    break;
		default:
		    usage(argv[0]);
		    break;
		}
	    }
	    break;
	default:
	    usage(argv[0]);
	    exit(1);
	}

    if (argc-optind == 1)
	disk_device = argv[optind];
    else if (argc-optind != 0) {
	usage(argv[0]);
	exit(1);
    } else if ((fd = open(DEFAULT_DEVICE, O_RDONLY)) < 0)
	disk_device = ALTERNATE_DEVICE;
    else close(fd);

    if (print_only) {
	fill_p_info();
	if (print_only & PRINT_RAW_TABLE)
	    print_raw_table();
	if (print_only & PRINT_SECTOR_TABLE)
	    print_p_info();
	if (print_only & PRINT_PARTITION_TABLE)
	    print_part_table();
    } else
	do_curses_fdisk();
    return 0;
}