I have path name like this str[20]="/dev/sda1" and needs to get partion number i.e '1' and device name "sda1" seperatly stored in variables.
I tried something like this to get string but I want both partion number and string seperatly.
sscanf(str,"%[^0123456789]", temp);
but temp will give me string only not number?
CodePudding user response:
In this case you can reuse the str
buffer, but I will not use sscanf
, try with strcspn
, it works even if the path doesn't have a number:
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] = "/dev/sda1";
char num[sizeof str] = "";
size_t pos = strcspn(str, "0123456789");
strcpy(num, str pos);
str[pos] = '\0';
puts(str);
puts(num);
return 0;
}
Output:
/dev/sda
1
A better version scanning from the last slash:
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] = "/dev1/sda1";
char num[sizeof str] = "";
// Searches for the last occurrence of '/'
char *ptr = strrchr(str, '/');
if (ptr != NULL)
{
size_t pos = strcspn(ptr, "0123456789");
strcpy(num, ptr pos);
ptr[pos] = '\0';
}
puts(str);
puts(num);
return 0;
}
Output:
/dev1/sda
1
EDIT:
any specific reason ,why can't we use sscanf here
You can, but notice that an extra buffer is needed:
#include <stdio.h>
int main(void)
{
char str[] = "/dev/sda1";
char dev[sizeof str] = "";
char num[sizeof str] = "";
sscanf(str, "%[^0-9]%s", dev, num);
puts(dev);
puts(num);
return 0;
}
CodePudding user response:
I would recommend:
Use
strrchr(path, '/')
to find the last slash in the path, and if non-NULL, use the rest of the string (starting at the character following the last slash) as the device name. If there is no slash in the path, then it consists of the device name only.Use
sscanf()
to extract the components of the device name, with a terminating%c
(taking a pointer to achar
) to detect if the name or path contains extra characters. The return value is the number of successful conversions, and we require that terminating conversion to fail. (If you use%n
, it is not counted as a conversion.)Support at least
/dev/hdXP
(ATA/ATAPI devices),/dev/sdXP
(SCSI and SATA devices),nvmeCnDpP
(NVMe devices), and/dev/mmcblkDpP
(eMMC devices) partition types.
Here is a working example program. Supply one or more partition names or paths, and it describes each name.
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
enum {
BLOCKDEV_NONE = -1,
BLOCKDEV_HD, /* device hd<letter>, partition hd<letter><number> */
BLOCKDEV_SD, /* device sd<letter>, partition sd<letter><number> */
BLOCKDEV_NVME, /* device nvme<number>n<number>, partition nvme<number>n<number>p<number> */
BLOCKDEV_MMC, /* device mmcblk<number>, partition mmcblk<number>p<number> */
};
struct partition_info {
char *blockdev_path; /* Full path to the block device */
char *blockdev_name; /* Block device name, basename(blockdev_path) */
char *partition_path; /* Full path to the partition device */
char *partition_name; /* Partition device name, basename(partition_path) */
int type; /* One of the BLOCKDEV_ constants */
int controller; /* Controller number for NVMe, 0 for others */
int device; /* Device number; 0..25 for letters a..z */
int partition; /* Partition number */
};
const char *blockdev_type_desc(const int type)
{
switch (type) {
case BLOCKDEV_NONE: return "None";
case BLOCKDEV_HD: return "ATA block device";
case BLOCKDEV_SD: return "SCSI or SATA block device";
case BLOCKDEV_NVME: return "NVMe block device";
case BLOCKDEV_MMC: return "eMMC block device";
default: return "(Unknown block device)";
}
}
void free_partition_info(struct partition_info *info)
{
/* Since free(NULL) is safe, make free_partition_info(NULL) safe also. */
if (!info)
return;
info->type = BLOCKDEV_NONE;
info->controller = -1;
info->device = -1;
info->partition = -1;
free(info->blockdev_path);
info->blockdev_path = NULL;
info->blockdev_name = NULL;
free(info->partition_path);
info->partition_path = NULL;
info->partition_name = NULL;
}
static inline char *name_part(char *path)
{
if (!path)
return NULL;
char *last_slash = strrchr(path, '/');
if (last_slash)
return last_slash 1;
else
return path;
}
int set_partition_info(struct partition_info *info, const char *path)
{
int ctrlnum, devnum, partnum, n;
char letter[2], dummy;
/* A pointer to an uninitialized partition_info structure is required. */
if (!info) {
errno = EINVAL;
return -1;
}
info->type = BLOCKDEV_NONE;
info->controller = -1;
info->device = -1;
info->partition = -1;
info->blockdev_path = NULL;
info->blockdev_name = NULL;
info->partition_path = NULL;
info->partition_name = NULL;
/* Partition path cannot be NULL or empty. */
if (!path || !*path) {
errno = ENOENT;
return -1;
}
/* Partition name part follows the last slash, or is the full string. */
const char *last_slash = strrchr(path, '/');
const char *name = (last_slash) ? last_slash 1 : path;
/* Prefixlen is the length of the string up to and including the last slash. */
const size_t prefixlen = (last_slash) ? (size_t)(last_slash - path 1) : 0;
/* NVMe partition? */
if (sscanf(name, "nvme%dn%dp%d%c", &ctrlnum, &devnum, &partnum, &dummy) == 3) {
info->type = BLOCKDEV_NVME;
info->controller = ctrlnum;
info->device = devnum;
info->partition = partnum;
info->blockdev_path = strdup(path);
info->partition_path = strdup(path);
if (!info->blockdev_path || !info->partition_path) {
free(info->partition_path);
free(info->blockdev_path);
info->blockdev_path = NULL;
info->partition_path = NULL;
errno = ENOMEM;
return -1;
}
*(strrchr(info->blockdev_path, 'p')) = '\0';
info->blockdev_name = name_part(info->blockdev_path);
info->partition_name = name_part(info->partition_path);
return 0;
}
/* MMC partition? */
if (sscanf(name, "mmcblk%dp%d%c", &devnum, &partnum, &dummy) == 2) {
info->type = BLOCKDEV_MMC;
info->controller = 0;
info->device = devnum;
info->partition = partnum;
info->blockdev_path = strdup(path);
info->partition_path = strdup(path);
if (!info->blockdev_path || !info->partition_path) {
free(info->partition_path);
free(info->blockdev_path);
info->blockdev_path = NULL;
info->partition_path = NULL;
errno = ENOMEM;
return -1;
}
*(strrchr(info->blockdev_path, 'p')) = '\0';
info->blockdev_name = name_part(info->blockdev_path);
info->partition_name = name_part(info->partition_path);
return 0;
}
/* HD partition? */
if (sscanf(name, "hd%1[a-z]%n%d%c", letter, &n, &partnum, &dummy) == 2 && n == 3) {
info->type = BLOCKDEV_HD;
info->controller = 0;
info->device = letter[0] - 'a';
info->partition = partnum;
info->blockdev_path = strndup(path, prefixlen 3);
info->partition_path = strdup(path);
if (!info->blockdev_path || !info->partition_path) {
free(info->partition_path);
free(info->blockdev_path);
info->blockdev_path = NULL;
info->partition_path = NULL;
errno = ENOMEM;
return -1;
}
info->blockdev_name = name_part(info->blockdev_path);
info->partition_name = name_part(info->partition_path);
return 0;
}
/* SD partition? */
if (sscanf(name, "sd%1[a-z]%n%d%c", letter, &n, &partnum, &dummy) == 2 && n == 3) {
info->type = BLOCKDEV_SD;
info->controller = 0;
info->device = letter[0] - 'a';
info->partition = partnum;
info->blockdev_path = strndup(path, prefixlen 3);
info->partition_path = strdup(path);
if (!info->blockdev_path || !info->partition_path) {
free(info->partition_path);
free(info->blockdev_path);
info->blockdev_path = NULL;
info->partition_path = NULL;
errno = ENOMEM;
return -1;
}
info->blockdev_name = name_part(info->blockdev_path);
info->partition_name = name_part(info->partition_path);
return 0;
}
/* Unknown device; return "no such device" error. */
errno = ENODEV;
return -1;
}
int main(int argc, char *argv[])
{
for (int arg = 1; arg < argc; arg ) {
struct partition_info p;
if (set_partition_info(&p, argv[arg])) {
fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
}
printf("Specified path: \"%s\" (%p)\n", argv[arg], argv[arg]);
printf(" Type: %s\n", blockdev_type_desc(p.type));
if (p.type == BLOCKDEV_NVME)
printf(" Controller: %d\n", p.controller);
if (p.type == BLOCKDEV_HD || p.type == BLOCKDEV_SD)
printf(" Device: '%c' (%d)\n", p.device 'a', p.device);
else
printf(" Device: %d\n", p.device);
printf(" Partition: %d\n", p.partition);
printf(" Block device path: \"%s\" (%p)\n", p.blockdev_path, p.blockdev_path);
printf(" Block device name: \"%s\" (%p)\n", p.blockdev_name, p.blockdev_name);
printf("Partition device path: \"%s\" (%p)\n", p.partition_path, p.partition_path);
printf("Partition device name: \"%s\" (%p)\n", p.partition_name, p.partition_name);
printf("\n");
fflush(stdout);
free_partition_info(&p);
}
return EXIT_SUCCESS;
}
Note that the struct partition_info
contains dynamically allocated copies. The name parts are pointers to the corresponding path member, just skipping any directory components in the path.
For illustration, the example program shows the string pointer values (target address in hexadecimal) in parentheses after each string output in double quotes. This is to show how the various strings relate (or not) to each other.