summaryrefslogblamecommitdiffstats
path: root/libfdisk/src/ask.c
blob: dd152a1f0cf27f5d6f7dec5b8d2d1065ff572231 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
 

                     

                   







                                                   
                         
 









                                          
 
                                                      




                          





                                                                       





                                             






                                                       












                                                                  






                                                                  





                                                                            











                                                                 
                                                                              
 


                                                             
                                   

                                   



                                                                        


                                    

 


                                                            
                                   


                                 






                                                                      


                                                        
                                   


                                 






                                                                 


                                                         
                                   


                                 









                                                                   
                                   


                                    






                                                                       


                                                         
                                   













                                                                            
                                   












                                                                   
                                   









                                                                      






                                                   


                                                                 
                                               
                                                                  
                                                                 



















                                                                       
                                                                            
                                                                    
                           

                                                                                           
                                                                            
            

                                                                                              
                                                                               






















                                                                              
                                                                        

                                                                              
                               

                                                          

                                       




                           



                                                                            
                                                   



                                                 
 



                               
                                                      

                             

                                               




                                                                 
                              
                                                                 
                                                                                 



                                             



                                                                        
                                                                                 





                                                    
                                                                          





                                                                                      
                                                                                    

                                           
                                                                            







                                                                                   
                                                                                    

                                    
                                                                            



                               
                  
                                                                                                

                                                            


                                                                     
                                            

         



                                                            
         
                                                                                          
                            


                  































                                                           
                                                                 


                  













































                                                                    







































                                                                      


























                                                               
 






























                                                                                                  
                                                                     
                                                                 


                              
                   


                    


                                          
                              

                           
                               
         

                                      
                                        
                                            




                                                       
                   









                                                               
                                                                



                   



























                                                                               






                                                               
                                                                   










                                                                
                                                                 


                   
 








                                                                                      
                                                 
                                                                                     




                                                              
                   










                                                                  
                                                                    
         
                                                       






                                                
                                                                    
         
                                                       















                                                            

#include "strutils.h"

#include "fdiskP.h"

struct fdisk_ask *fdisk_new_ask(void)
{
	return calloc(1, sizeof(struct fdisk_ask));
}

void fdisk_reset_ask(struct fdisk_ask *ask)
{
	assert(ask);
	free(ask->query);

	memset(ask, 0, sizeof(*ask));
}

void fdisk_free_ask(struct fdisk_ask *ask)
{
	if (!ask)
		return;
	fdisk_reset_ask(ask);
	free(ask);
}

const char *fdisk_ask_get_query(struct fdisk_ask *ask)
{
	assert(ask);
	return ask->query;
}

int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str)
{
	assert(ask);
	return !strdup_to_struct_member(ask, query, str) ? -ENOMEM : 0;
}

int fdisk_ask_get_type(struct fdisk_ask *ask)
{
	assert(ask);
	return ask->type;
}

int fdisk_ask_set_type(struct fdisk_ask *ask, int type)
{
	assert(ask);
	ask->type = type;
	return 0;
}

unsigned int fdisk_ask_get_flags(struct fdisk_ask *ask)
{
	assert(ask);
	return ask->flags;
}

int fdisk_ask_set_flags(struct fdisk_ask *ask, unsigned int flags)
{
	assert(ask);
	ask->flags = flags;
	return 0;
}

int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask)
{
	int rc;

	assert(ask);
	assert(cxt);

	DBG(ASK, dbgprint("do_ask for '%s'",
				ask->query ? ask->query :
				ask->type == FDISK_ASKTYPE_INFO  ? "info" :
				ask->type == FDISK_ASKTYPE_WARNX ? "warnx" :
				ask->type == FDISK_ASKTYPE_WARN  ? "warn" :
				"?nothing?"));

	if (!cxt->ask_cb) {
		DBG(ASK, dbgprint("no ask callback specified!"));
		return -EINVAL;
	}

	rc = cxt->ask_cb(cxt, ask, cxt->ask_data);

	DBG(ASK, dbgprint("do_ask done [rc=%d]", rc));
	return rc;
}

#define is_number_ask(a)  (fdisk_is_ask(a, NUMBER) || fdisk_is_ask(a, OFFSET))

const char *fdisk_ask_number_get_range(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_number_ask(ask));
	return ask->data.num.range;
}

int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range)
{
	assert(ask);
	assert(is_number_ask(ask));
	ask->data.num.range = range;
	return 0;
}

uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_number_ask(ask));
	return ask->data.num.dfl;
}

int fdisk_ask_number_set_default(struct fdisk_ask *ask, uint64_t dflt)
{
	assert(ask);
	ask->data.num.dfl = dflt;
	return 0;
}

uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_number_ask(ask));
	return ask->data.num.low;
}

int fdisk_ask_number_set_low(struct fdisk_ask *ask, uint64_t low)
{
	assert(ask);
	ask->data.num.low = low;
	return 0;
}

uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_number_ask(ask));
	return ask->data.num.hig;
}

int fdisk_ask_number_set_high(struct fdisk_ask *ask, uint64_t high)
{
	assert(ask);
	ask->data.num.hig = high;
	return 0;
}

uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_number_ask(ask));
	return ask->data.num.result;
}

int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result)
{
	assert(ask);
	ask->data.num.result = result;
	return 0;
}

uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_number_ask(ask));
	return ask->data.num.base;
}

int fdisk_ask_number_set_base(struct fdisk_ask *ask, uint64_t base)
{
	assert(ask);
	ask->data.num.base = base;
	return 0;
}

/* if numbers are not in bytes, then specify number of bytes per the unit */
uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_number_ask(ask));
	return ask->data.num.unit;
}

int fdisk_ask_number_set_unit(struct fdisk_ask *ask, uint64_t unit)
{
	assert(ask);
	ask->data.num.unit = unit;
	return 0;
}

int fdisk_ask_number_is_relative(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_number_ask(ask));
	return ask->data.num.relative;
}

int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative)
{
	assert(ask);
	ask->data.num.relative = relative ? 1 : 0;
	return 0;
}

int fdisk_ask_number_inchars(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_number_ask(ask));
	return ask->data.num.inchars;
}

/*
 * Generates string with list ranges (e.g. 1,2,5-8) for the 'cur'
 */
#define tochar(num)	((int) ('a' + num - 1))
static char *mk_string_list(char *ptr, size_t *len, size_t *begin,
			    size_t *run, ssize_t cur, int inchar)
{
	int rlen;

	if (cur != -1) {
		if (!*begin) {			/* begin of the list */
			*begin = cur + 1;
			return ptr;
		}

		if (*begin + *run == cur) {	/* no gap, continue */
			(*run)++;
			return ptr;
		}
	} else if (!*begin) {
		*ptr = '\0';
		return ptr;		/* end of empty list */
	}

					/* add to the list */
	if (!*run)
		rlen = inchar ? snprintf(ptr, *len, "%c,", tochar(*begin)) :
				snprintf(ptr, *len, "%zu,", *begin);
	else if (*run == 1)
		rlen = inchar ?
			snprintf(ptr, *len, "%c,%c,", tochar(*begin), tochar(*begin + 1)) :
			snprintf(ptr, *len, "%zu,%zu,", *begin, *begin + 1);
	else
		rlen = inchar ?
			snprintf(ptr, *len, "%c-%c,", tochar(*begin), tochar(*begin + *run)) :
			snprintf(ptr, *len, "%zu-%zu,", *begin, *begin + *run);

	if (rlen < 0 || (size_t) rlen + 1 > *len)
		return NULL;

	ptr += rlen;

	if (rlen > 0 && *len > (size_t) rlen)
		*len -= rlen;
	else
		*len = 0;

	if (cur == -1 && *begin) {
		/* end of the list */
		*(ptr - 1) = '\0';	/* remove tailing ',' from the list */
		return ptr;
	}

	*begin = cur + 1;
	*run = 0;

	return ptr;
}

/* returns: 1=0 on success, < 0 on error, 1 if no free/used partition */
int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew)
{
	int rc = 0, inchar = 0;
	char range[BUFSIZ], *ptr = range;
	size_t i, len = sizeof(range), begin = 0, run = 0;
	struct fdisk_ask *ask = NULL;
	__typeof__(ask->data.num) *num;

	assert(cxt);
	assert(cxt->label);
	assert(partnum);

	if (cxt->label && cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
		inchar = 1;

	DBG(ASK, dbgprint("%s: asking for %s partition number "
			  "(max: %zu, inchar: %s)",
			cxt->label->name,
			wantnew ? "new" : "used",
			cxt->label->nparts_max,
			inchar ? "yes" : "not"));

	ask = fdisk_new_ask();
	if (!ask)
		return -ENOMEM;

	fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
	num = &ask->data.num;

	ask->data.num.inchars = inchar ? 1 : 0;

	for (i = 0; i < cxt->label->nparts_max; i++) {
		int status = 0;

		rc = fdisk_partition_get_status(cxt, i, &status);
		if (rc)
			break;
		if (wantnew && !(status & FDISK_PARTSTAT_USED)) {
			ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar);
			if (!ptr) {
				rc = -EINVAL;
				break;
			}
			if (!num->low)
				num->dfl = num->low = i + 1;
			num->hig = i + 1;
		} else if (!wantnew && (status & FDISK_PARTSTAT_USED)) {
			ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar);
			if (!num->low)
				num->low = i + 1;
			num->dfl = num->hig = i + 1;
		}
	}

	DBG(ASK, dbgprint("ask limits: low: %ju, high: %ju, default: %ju",
				num->low, num->hig, num->dfl));

	if (!rc && !wantnew && num->low == num->hig) {
		if (num->low > 0) {
			/* only one existing partiton, don't ask, return the number */
			fdisk_ask_number_set_result(ask, num->low);
			fdisk_info(cxt, _("Selected partition %d"), (int) num->low);

		} else if (num->low == 0) {
			fdisk_warnx(cxt, _("No partition is defined yet!"));
			rc = 1;
		}
		goto dont_ask;
	}
	if (!rc && wantnew && num->low == num->hig) {
		if (num->low > 0) {
			/* only one free partition, don't ask, return the number */
			fdisk_ask_number_set_result(ask, num->low);
			fdisk_info(cxt, _("Selected partition %d"), (int) num->low);
		}
		if (num->low == 0) {
			fdisk_warnx(cxt, _("No free partition available!"));
			rc = 1;
		}
		goto dont_ask;
	}
	if (!rc) {
		mk_string_list(ptr, &len, &begin, &run, -1, inchar);	/* terminate the list */
		rc = fdisk_ask_number_set_range(ask, range);
	}
	if (!rc)
		rc = fdisk_ask_set_query(ask, _("Partition number"));
	if (!rc)
		rc = fdisk_do_ask(cxt, ask);

dont_ask:
	if (!rc) {
		*partnum = fdisk_ask_number_get_result(ask);
		if (*partnum)
			*partnum -= 1;
	}
	DBG(ASK, dbgprint("result: %ju [rc=%d]\n", fdisk_ask_number_get_result(ask), rc));
	fdisk_free_ask(ask);
	return rc;
}

/* very basic wraper to ask numbers */
int fdisk_ask_number(struct fdisk_context *cxt,
		     uintmax_t low,
		     uintmax_t dflt,
		     uintmax_t high,
		     const char *query,
		     uintmax_t *result)
{
	struct fdisk_ask *ask;
	int rc;

	assert(cxt);

	ask = fdisk_new_ask();
	if (!ask)
		return -ENOMEM;

	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
	if (!rc)
		fdisk_ask_number_set_low(ask, low);
	if (!rc)
		fdisk_ask_number_set_default(ask, dflt);
	if (!rc)
		fdisk_ask_number_set_high(ask, high);
	if (!rc)
		fdisk_ask_set_query(ask, query);
	if (!rc)
		rc = fdisk_do_ask(cxt, ask);
	if (!rc)
		*result = fdisk_ask_number_get_result(ask);

	fdisk_free_ask(ask);
	DBG(ASK, dbgprint("result: %ju [rc=%d]\n", *result, rc));
	return rc;
}

char *fdisk_ask_string_get_result(struct fdisk_ask *ask)
{
	assert(ask);
	assert(fdisk_is_ask(ask, STRING));
	return ask->data.str.result;
}

/*
 * The @result has to be poiter to the allocated buffer.
 */
int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result)
{
	assert(ask);
	ask->data.str.result = result;
	return 0;
}

/*
 * Don't forget to deallocate @result.
 */
int fdisk_ask_string(struct fdisk_context *cxt,
		     const char *query,
		     char **result)
{
	struct fdisk_ask *ask;
	int rc;

	assert(cxt);

	ask = fdisk_new_ask();
	if (!ask)
		return -ENOMEM;

	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_STRING);
	if (!rc)
		fdisk_ask_set_query(ask, query);
	if (!rc)
		rc = fdisk_do_ask(cxt, ask);
	if (!rc)
		*result = fdisk_ask_string_get_result(ask);

	fdisk_free_ask(ask);
	DBG(ASK, dbgprint("result: %s [rc=%d]\n", *result, rc));
	return rc;
}

int fdisk_ask_yesno(struct fdisk_context *cxt,
		     const char *query,
		     int *result)
{
	struct fdisk_ask *ask;
	int rc;

	assert(cxt);

	ask = fdisk_new_ask();
	if (!ask)
		return -ENOMEM;

	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_YESNO);
	if (!rc)
		fdisk_ask_set_query(ask, query);
	if (!rc)
		rc = fdisk_do_ask(cxt, ask);
	if (!rc)
		*result = fdisk_ask_yesno_get_result(ask);

	fdisk_free_ask(ask);
	DBG(ASK, dbgprint("result: %d [rc=%d]\n", *result, rc));
	return rc;
}

uint64_t fdisk_ask_yesno_get_result(struct fdisk_ask *ask)
{
	assert(ask);
	assert(fdisk_is_ask(ask, YESNO));
	return ask->data.yesno.result;
}

int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, uint64_t result)
{
	assert(ask);
	ask->data.yesno.result = result;
	return 0;
}

struct tt *fdisk_ask_get_table(struct fdisk_ask *ask)
{
	assert(ask);
	assert(fdisk_is_ask(ask, TABLE));
	return ask->data.table;
}

int fdisk_print_table(struct fdisk_context *cxt, struct tt *tb)
{
	struct fdisk_ask *ask;
	int rc;

	assert(cxt);
	assert(tb);

	ask = fdisk_new_ask();
	if (!ask)
		return -ENOMEM;

	fdisk_ask_set_type(ask, FDISK_ASKTYPE_TABLE);
	ask->data.table = tb;

	rc = fdisk_do_ask(cxt, ask);

	fdisk_free_ask(ask);
	return rc;
}

#define is_print_ask(a) (fdisk_is_ask(a, WARN) || fdisk_is_ask(a, WARNX) || fdisk_is_ask(a, INFO))

int fdisk_ask_print_get_errno(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_print_ask(ask));
	return ask->data.print.errnum;
}

int fdisk_ask_print_set_errno(struct fdisk_ask *ask, int errnum)
{
	assert(ask);
	ask->data.print.errnum = errnum;
	return 0;
}

const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask)
{
	assert(ask);
	assert(is_print_ask(ask));
	return ask->data.print.mesg;
}

/* does not reallocate the message! */
int fdisk_ask_print_set_mesg(struct fdisk_ask *ask, const char *mesg)
{
	assert(ask);
	ask->data.print.mesg = mesg;
	return 0;
}

static int do_vprint(struct fdisk_context *cxt, int errnum, int type,
		 unsigned int flags, const char *fmt, va_list va)
{
	struct fdisk_ask *ask;
	int rc;
	char *mesg;

	assert(cxt);

	if (vasprintf(&mesg, fmt, va) < 0)
		return -ENOMEM;

	ask = fdisk_new_ask();
	if (!ask) {
		free(mesg);
		return -ENOMEM;
	}

	fdisk_ask_set_type(ask, type);
	fdisk_ask_set_flags(ask, flags);
	fdisk_ask_print_set_mesg(ask, mesg);
	if (errnum >= 0)
		fdisk_ask_print_set_errno(ask, errnum);
	rc = fdisk_do_ask(cxt, ask);

	fdisk_free_ask(ask);
	free(mesg);
	return rc;
}

int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...)
{
	int rc;
	va_list ap;

	assert(cxt);
	va_start(ap, fmt);
	rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, 0, fmt, ap);
	va_end(ap);
	return rc;
}

/* "smart" version, allows to set flags for the message */
int fdisk_sinfo(struct fdisk_context *cxt,
		unsigned int flags, const char *fmt, ...)
{
	int rc;
	va_list ap;

	assert(cxt);
	va_start(ap, fmt);
	rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, flags, fmt, ap);
	va_end(ap);
	return rc;

}

int fdisk_colon(struct fdisk_context *cxt, const char *fmt, ...)
{
	int rc;
	va_list ap;

	assert(cxt);
	va_start(ap, fmt);
	rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, FDISK_INFO_COLON, fmt, ap);
	va_end(ap);
	return rc;

}

int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...)
{
	int rc;
	va_list ap;

	assert(cxt);
	va_start(ap, fmt);
	rc = do_vprint(cxt, errno, FDISK_ASKTYPE_WARN, 0, fmt, ap);
	va_end(ap);
	return rc;
}

int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...)
{
	int rc;
	va_list ap;

	assert(cxt);
	va_start(ap, fmt);
	rc = do_vprint(cxt, -1, FDISK_ASKTYPE_WARNX, 0, fmt, ap);
	va_end(ap);
	return rc;
}

int fdisk_info_new_partition(
			struct fdisk_context *cxt,
			int num, sector_t start, sector_t stop,
			struct fdisk_parttype *t)
{
	int rc;
	char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE,
				     (uint64_t)(stop - start + 1) * cxt->sector_size);

	rc = fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
			_("Created a new partition %d of type '%s' and of size %s."),
			num, t ? t->name : _("Unknown"), str);
	free(str);
	return rc;
}

#ifdef TEST_PROGRAM
int test_ranges(struct fdisk_test *ts, int argc, char *argv[])
{
	/*                1  -  3,       6,    8, 9,   11    13 */
	size_t nums[] = { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1 };
	size_t numx[] = { 0, 0, 0 };
	char range[BUFSIZ], *ptr = range;
	size_t i, len = sizeof(range), begin = 0, run = 0;

	for (i = 0; i < ARRAY_SIZE(nums); i++) {
		if (!nums[i])
			continue;
		ptr = mk_string_list(ptr, &len, &begin, &run, i, 0);
	}
	mk_string_list(ptr, &len, &begin, &run, -1, 0);
	printf("list: '%s'\n", range);

	ptr = range;
	len = sizeof(range), begin = 0, run = 0;
	for (i = 0; i < ARRAY_SIZE(numx); i++) {
		if (!numx[i])
			continue;
		ptr = mk_string_list(ptr, &len, &begin, &run, i, 0);
	}
	mk_string_list(ptr, &len, &begin, &run, -1, 0);
	printf("empty list: '%s'\n", range);

	return 0;
}

int main(int argc, char *argv[])
{
	struct fdisk_test tss[] = {
	{ "--ranges",  test_ranges,    "generates ranges" },
	{ NULL }
	};

	return fdisk_run_test(tss, argc, argv);
}

#endif