mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
include ../common.mak
|
||||
|
||||
lib = libutil.a
|
||||
|
||||
examples = util_base_example \
|
||||
util_panic_example \
|
||||
util_path_example \
|
||||
util_scandir_example \
|
||||
util_file_example \
|
||||
util_libc_example \
|
||||
util_opt_example \
|
||||
util_opt_command_example \
|
||||
util_prg_example \
|
||||
util_rec_example
|
||||
|
||||
all: $(lib) $(examples)
|
||||
|
||||
objects = util_base.o \
|
||||
util_path.o \
|
||||
util_scandir.o \
|
||||
util_file.o \
|
||||
util_libc.o \
|
||||
util_list.o \
|
||||
util_opt.o \
|
||||
util_panic.o \
|
||||
util_part.o \
|
||||
util_prg.o \
|
||||
util_proc.o \
|
||||
util_rec.o
|
||||
|
||||
util_base_example: util_base_example.o $(rootdir)/libutil/libutil.a
|
||||
util_panic_example: util_panic_example.o $(rootdir)/libutil/libutil.a
|
||||
util_path_example: util_path_example.o $(rootdir)/libutil/libutil.a
|
||||
util_scandir_example: util_scandir_example.o $(rootdir)/libutil/libutil.a
|
||||
util_file_example: util_file_example.o $(rootdir)/libutil/libutil.a
|
||||
util_libc_example: util_libc_example.o $(rootdir)/libutil/libutil.a
|
||||
util_opt_example: util_opt_example.o $(rootdir)/libutil/libutil.a
|
||||
util_opt_command_example: util_opt_command_example.o $(rootdir)/libutil/libutil.a
|
||||
util_panic_example: util_panic_example.o $(rootdir)/libutil/libutil.a
|
||||
util_prg_example: util_prg_example.o $(rootdir)/libutil/libutil.a
|
||||
util_rec_example: util_rec_example.o $(rootdir)/libutil/libutil.a
|
||||
|
||||
$(lib): $(objects)
|
||||
|
||||
install: all
|
||||
|
||||
clean:
|
||||
rm -f *.o $(lib) $(examples)
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* General helper functions
|
||||
*
|
||||
* Copyright IBM Corp. 2013, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_libc.h"
|
||||
|
||||
/*
|
||||
* Print hexdump for buffer with variable group parameter
|
||||
*/
|
||||
void util_hexdump_grp(FILE *fh, const char *tag, const void *data, int grp,
|
||||
int count, int indent)
|
||||
{
|
||||
const char *buf = data;
|
||||
int i, first = 1;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (first) {
|
||||
fprintf(fh, "%*s", indent, " ");
|
||||
if (tag)
|
||||
fprintf(fh, "%s: ", tag);
|
||||
fprintf(fh, "%08x: ", i);
|
||||
first = 0;
|
||||
}
|
||||
fprintf(fh, "%02x", buf[i]);
|
||||
if (i % 16 == 15 || i + 1 == count) {
|
||||
fprintf(fh, "\n");
|
||||
first = 1;
|
||||
} else if (i % grp == grp - 1) {
|
||||
fprintf(fh, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print hexdump for buffer with fix grp parameter
|
||||
*/
|
||||
void util_hexdump(FILE *fh, const char *tag, const void *data, int count)
|
||||
{
|
||||
util_hexdump_grp(fh, tag, data, sizeof(long), count, 0);
|
||||
}
|
||||
|
||||
#define MAX_CHARS_PER_LINE 80
|
||||
|
||||
/*
|
||||
* Print string with indentation
|
||||
*
|
||||
* Print a string while accounting for a given indent value, characters per line
|
||||
* limit, and line breaks ('\n') within the string. The first line has to be
|
||||
* indented manually.
|
||||
*
|
||||
* @param[in] str String that should be printed
|
||||
* @param[in] indent Indentation for printing
|
||||
*/
|
||||
void util_print_indented(const char *str, int indent)
|
||||
{
|
||||
char *word, *line, *desc, *desc_ptr;
|
||||
int word_len, pos = indent;
|
||||
|
||||
desc = desc_ptr = util_strdup(str);
|
||||
line = strsep(&desc, "\n");
|
||||
while (line) {
|
||||
word = strsep(&line, " ");
|
||||
pos = indent;
|
||||
while (word) {
|
||||
word_len = strlen(word);
|
||||
if (pos + word_len + 1 > MAX_CHARS_PER_LINE) {
|
||||
printf("\n%*s", indent, "");
|
||||
pos = indent;
|
||||
}
|
||||
if (pos == indent)
|
||||
printf("%s", word);
|
||||
else
|
||||
printf(" %s", word);
|
||||
pos += word_len + 1;
|
||||
word = strsep(&line, " ");
|
||||
}
|
||||
if (desc)
|
||||
printf("\n%*s", indent, "");
|
||||
line = strsep(&desc, "\n");
|
||||
}
|
||||
printf("\n");
|
||||
free(desc_ptr);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* util_base_example - Example program for util_base
|
||||
*
|
||||
* Copyright 2017 IBM Corp.
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
//! [code]
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
|
||||
#define INDENTATION 24
|
||||
|
||||
const char *msg =
|
||||
"There is a theory which states that if ever anybody discovers exactly "
|
||||
"what the Universe is for and why it is here, it will instantly disappear "
|
||||
"and be replaced by something even more ...\n"
|
||||
"\n"
|
||||
" ... bizarre\n"
|
||||
" ... and inexplicable.\n"
|
||||
"\n"
|
||||
"There is another theory which states that this has already happened.";
|
||||
|
||||
/*
|
||||
* Demonstrate util_base functions
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
printf("TEST: util_print_indented(msg, %d)\n", INDENTATION);
|
||||
printf("----------------------------------\n");
|
||||
printf("%-*s", INDENTATION, "Douglas Adams:");
|
||||
util_print_indented(msg, INDENTATION);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
//! [code]
|
||||
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Read and write files
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <linux/limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_prg.h"
|
||||
|
||||
/*
|
||||
* Read the first line of a file into given buffer
|
||||
*/
|
||||
static int file_gets(char *str, size_t size, const char *path)
|
||||
{
|
||||
char *p, *end;
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
/* In case of error we always return empty string */
|
||||
str[0] = 0;
|
||||
/* Read the string */
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
p = fgets(str, size, fp);
|
||||
if (p) {
|
||||
/* Check for end of line */
|
||||
end = memchr(str, '\n', size);
|
||||
if (end)
|
||||
*end = 0;
|
||||
else
|
||||
str[size - 1] = 0;
|
||||
}
|
||||
if (strlen(str) == 0) {
|
||||
rc = -1;
|
||||
goto out_fclose;
|
||||
}
|
||||
rc = 0;
|
||||
out_fclose:
|
||||
fclose(fp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the first line of a file
|
||||
*
|
||||
* If read is successful 'str' contains first line of a file without the
|
||||
* trailing newline. If read fails, an empty string is returned for 'str'
|
||||
* The resulting string will always be null-terminated.
|
||||
*
|
||||
* @param[out] str Result buffer
|
||||
* @param[in] size Size of the result buffer
|
||||
* @param[in] fmt Format string for generation of the path name
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @retval 0 File was read
|
||||
* @retval -1 Error while reading file
|
||||
*/
|
||||
int util_file_read_line(char *const str, size_t size, const char *fmt, ...)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
va_list ap;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
return file_gets(str, size, path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write string to a file
|
||||
*/
|
||||
static int file_puts(const char *str, const char *path)
|
||||
{
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
/* write the string */
|
||||
fp = fopen(path, "w");
|
||||
if (!fp)
|
||||
return -1;
|
||||
if (fputs(str, fp) == EOF) {
|
||||
rc = -1;
|
||||
goto out_fclose;
|
||||
}
|
||||
rc = 0;
|
||||
out_fclose:
|
||||
fclose(fp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write string to a file without the terminating null byte
|
||||
*
|
||||
* @param[in] str Content is to be written
|
||||
* @param[in] fmt Format string for generation of the path name
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @retval 0 Write was successful
|
||||
* @retval -1 Error while writing file
|
||||
*/
|
||||
int util_file_write_s(const char *str, const char *fmt, ...)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
va_list ap;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
return file_puts(str, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write signed long value to a file according to given base
|
||||
*
|
||||
* @param[in] val Value is to be written
|
||||
* @param[in] base Base for conversion, either 8, 10, or 16
|
||||
* @param[in] fmt Format string for generation of the path name
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @retval 0 Write was successful
|
||||
* @retval -1 Error while writing file
|
||||
*/
|
||||
int util_file_write_l(long val, int base, const char *fmt, ...)
|
||||
{
|
||||
char *str, path[PATH_MAX];
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
switch (base) {
|
||||
case 8:
|
||||
util_asprintf(&str, "%lo", val);
|
||||
break;
|
||||
case 10:
|
||||
util_asprintf(&str, "%ld", val);
|
||||
break;
|
||||
case 16:
|
||||
util_asprintf(&str, "%lx", val);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid base: %d\n", base);
|
||||
}
|
||||
rc = file_puts(str, path);
|
||||
free(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write signed long long value to a file according to given base
|
||||
*
|
||||
* @param[in] val Value is to be written
|
||||
* @param[in] base Base for conversion, either 8, 10, or 16
|
||||
* @param[in] fmt Format string for generation of the path name
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @retval 0 Write was successful
|
||||
* @retval -1 Error while writing file
|
||||
*/
|
||||
int util_file_write_ll(long long val, int base, const char *fmt, ...)
|
||||
{
|
||||
char *str, path[PATH_MAX];
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
switch (base) {
|
||||
case 8:
|
||||
util_asprintf(&str, "%llo", val);
|
||||
break;
|
||||
case 10:
|
||||
util_asprintf(&str, "%lld", val);
|
||||
break;
|
||||
case 16:
|
||||
util_asprintf(&str, "%llx", val);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid base: %d\n", base);
|
||||
}
|
||||
rc = file_puts(str, path);
|
||||
free(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write unsigned long value to a file according to given base
|
||||
*
|
||||
* @param[in] val Value is to be written
|
||||
* @param[in] base Base for conversion, either 8, 10, or 16
|
||||
* @param[in] fmt Format string for generation of the path name
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @retval 0 Write was successful
|
||||
* @retval -1 Error while writing file
|
||||
*/
|
||||
int util_file_write_ul(unsigned long val, int base, const char *fmt, ...)
|
||||
{
|
||||
char *str, path[PATH_MAX];
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
switch (base) {
|
||||
case 8:
|
||||
util_asprintf(&str, "%lo", val);
|
||||
break;
|
||||
case 10:
|
||||
util_asprintf(&str, "%lu", val);
|
||||
break;
|
||||
case 16:
|
||||
util_asprintf(&str, "%lx", val);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid base: %d\n", base);
|
||||
}
|
||||
rc = file_puts(str, path);
|
||||
free(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write unsigned long long value to a file according to given base
|
||||
*
|
||||
* @param[in] val Value is to be written
|
||||
* @param[in] base Base for conversion, either 8, 10, or 16
|
||||
* @param[in] fmt Format string for generation of the path name
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @retval 0 Write was successful
|
||||
* @retval -1 Error while writing file
|
||||
*/
|
||||
int util_file_write_ull(unsigned long long val, int base, const char *fmt, ...)
|
||||
{
|
||||
char *str, path[PATH_MAX];
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
switch (base) {
|
||||
case 8:
|
||||
util_asprintf(&str, "%llo", val);
|
||||
break;
|
||||
case 10:
|
||||
util_asprintf(&str, "%llu", val);
|
||||
break;
|
||||
case 16:
|
||||
util_asprintf(&str, "%llx", val);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid base: %d\n", base);
|
||||
}
|
||||
rc = file_puts(str, path);
|
||||
free(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file and convert it to signed long according to given base
|
||||
*
|
||||
* @param[out] val Buffer for value
|
||||
* @param[in] base Base for conversion, either 8, 10, or 16
|
||||
* @param[in] fmt Format string for generation of the path name
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @retval 0 Long integer has been read correctly
|
||||
* @retval -1 Error while reading file
|
||||
*/
|
||||
int util_file_read_l(long *val, int base, const char *fmt, ...)
|
||||
{
|
||||
char path[PATH_MAX], buf[512];
|
||||
va_list ap;
|
||||
int count;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
if (file_gets(buf, sizeof(buf), path))
|
||||
return -1;
|
||||
switch (base) {
|
||||
case 8:
|
||||
count = sscanf(buf, "%lo", val);
|
||||
break;
|
||||
case 10:
|
||||
count = sscanf(buf, "%ld", val);
|
||||
break;
|
||||
case 16:
|
||||
count = sscanf(buf, "%lx", val);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid base: %d\n", base);
|
||||
}
|
||||
return (count == 1) ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file and convert it to signed long long according to given base
|
||||
*
|
||||
* @param[out] val Buffer for value
|
||||
* @param[in] base Base for conversion, either 8, 10, or 16
|
||||
* @param[in] fmt Format string for generation of the path name
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @retval 0 Long integer has been read correctly
|
||||
* @retval -1 Error while reading file
|
||||
*/
|
||||
int util_file_read_ll(long long *val, int base, const char *fmt, ...)
|
||||
{
|
||||
char path[PATH_MAX], buf[512];
|
||||
va_list ap;
|
||||
int count;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
if (file_gets(buf, sizeof(buf), path))
|
||||
return -1;
|
||||
switch (base) {
|
||||
case 8:
|
||||
count = sscanf(buf, "%llo", val);
|
||||
break;
|
||||
case 10:
|
||||
count = sscanf(buf, "%lld", val);
|
||||
break;
|
||||
case 16:
|
||||
count = sscanf(buf, "%llx", val);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid base: %d\n", base);
|
||||
}
|
||||
return (count == 1) ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file and convert it to unsigned long according to given base
|
||||
*
|
||||
* @param[out] val Buffer for value
|
||||
* @param[in] base Base for conversion, either 8, 10, or 16
|
||||
* @param[in] fmt Format string for generation of the path name
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @retval 0 Long integer has been read correctly
|
||||
* @retval -1 Error while reading file
|
||||
*/
|
||||
int util_file_read_ul(unsigned long *val, int base, const char *fmt, ...)
|
||||
{
|
||||
char path[PATH_MAX], buf[512];
|
||||
va_list ap;
|
||||
int count;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
if (file_gets(buf, sizeof(buf), path))
|
||||
return -1;
|
||||
switch (base) {
|
||||
case 8:
|
||||
count = sscanf(buf, "%lo", val);
|
||||
break;
|
||||
case 10:
|
||||
count = sscanf(buf, "%lu", val);
|
||||
break;
|
||||
case 16:
|
||||
count = sscanf(buf, "%lx", val);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid base: %d\n", base);
|
||||
}
|
||||
return (count == 1) ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file and convert it to unsigned long long according to given base
|
||||
*
|
||||
* @param[out] val Buffer for value
|
||||
* @param[in] base Base for conversion, either 8, 10, or 16
|
||||
* @param[in] fmt Format string for generation of the path name
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @retval 0 Long integer has been read correctly
|
||||
* @retval -1 Error while reading file
|
||||
*/
|
||||
int util_file_read_ull(unsigned long long *val, int base, const char *fmt, ...)
|
||||
{
|
||||
char path[PATH_MAX], buf[512];
|
||||
va_list ap;
|
||||
int count;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
if (file_gets(buf, sizeof(buf), path))
|
||||
return -1;
|
||||
switch (base) {
|
||||
case 8:
|
||||
count = sscanf(buf, "%llo", val);
|
||||
break;
|
||||
case 10:
|
||||
count = sscanf(buf, "%llu", val);
|
||||
break;
|
||||
case 16:
|
||||
count = sscanf(buf, "%llx", val);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid base: %d\n", base);
|
||||
}
|
||||
return (count == 1) ? 0 : -1;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* util_file_example - Example program for util_file
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
//! [code]
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_file.h"
|
||||
|
||||
/*
|
||||
* Write buffer to file and read it back again
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
char buf_wr[4096], buf_rd[4096];
|
||||
unsigned long long value_ull;
|
||||
long value_l;
|
||||
|
||||
/* Generate input */
|
||||
sprintf(buf_wr, "Say something interesting!\nSecond line\n");
|
||||
printf("Write.....:\n%s", buf_wr);
|
||||
|
||||
/* Write string to file */
|
||||
if (util_file_write_s(buf_wr, "/tmp/%s", "testfile")) {
|
||||
perror("util_file_write_s failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Read back first line of file */
|
||||
if (util_file_read_line(buf_rd, sizeof(buf_rd), "/tmp/%s", "testfile")) {
|
||||
perror("util_file_read_line failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("Read......: %s\n", buf_rd);
|
||||
|
||||
/* Write long to file */
|
||||
printf("Write.....: %ld\n", 4711L);
|
||||
if (util_file_write_l(4711L, 10, "/tmp/%s", "testfile")) {
|
||||
perror("util_file_write failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* Read back long from file */
|
||||
if (util_file_read_l(&value_l, 10, "/tmp/%s", "testfile")) {
|
||||
perror("util_file_read_l failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("Read......: %ld\n", value_l);
|
||||
|
||||
/* Write long long hexadecimal to file */
|
||||
printf("Write.....: 0x%llx\n", 0x4712ULL);
|
||||
if (util_file_write_ull(0x4712ULL, 16, "/tmp/%s", "testfile")) {
|
||||
perror("util_file_write failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* Read back long long hexadecimal from file */
|
||||
if (util_file_read_ull(&value_ull, 16, "/tmp/%s", "testfile")) {
|
||||
perror("util_file_read_ull failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("Read......: 0x%llx\n", value_ull);
|
||||
/* Remove file */
|
||||
unlink("/tmp/testfile");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
//! [code]
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Handle standard errors for libc functions
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_panic.h"
|
||||
|
||||
/*
|
||||
* Return size as string of largest unit, e.g. 1025 = "1 KiB"
|
||||
*/
|
||||
static void format_size(char *str, size_t size)
|
||||
{
|
||||
static const char * const unit_vec[] =
|
||||
{"byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < UTIL_ARRAY_SIZE(unit_vec); i++) {
|
||||
if (size / 1024 == 0) {
|
||||
sprintf(str, "%zu %s", size, unit_vec[i]);
|
||||
return;
|
||||
}
|
||||
size /= 1024;
|
||||
}
|
||||
sprintf(str, "huge");
|
||||
}
|
||||
|
||||
static void __util_oom(const char *func, const char *file, int line,
|
||||
size_t size)
|
||||
{
|
||||
char size_str[256];
|
||||
|
||||
fprintf(stderr, "%s: Failed to allocate memory",
|
||||
program_invocation_short_name);
|
||||
if (size > 0) {
|
||||
format_size(size_str, size);
|
||||
fprintf(stderr, " (%s)", size_str);
|
||||
}
|
||||
fprintf(stderr, " at %s:%d %s()\n", file, line, func);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate memory or exit in case of failure
|
||||
*/
|
||||
void *__util_malloc(const char *func, const char *file, int line, size_t size)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
buf = malloc(size);
|
||||
|
||||
if (buf == NULL)
|
||||
__util_oom(func, file, line, size);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate zero-initialized memory or exit in case of failure
|
||||
*/
|
||||
void *__util_zalloc(const char *func, const char *file, int line, size_t size)
|
||||
{
|
||||
void *buf = __util_malloc(func, file, line, size);
|
||||
|
||||
memset(buf, 0, size);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-allocate memory or exit in case of failure
|
||||
*/
|
||||
void *__util_realloc(const char *func, const char *file, int line,
|
||||
void *ptr, size_t size)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
buf = realloc(ptr, size);
|
||||
|
||||
if (buf == NULL)
|
||||
__util_oom(func, file, line, size);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Duplicate a string buffer or exit in case of failure
|
||||
*/
|
||||
void *__util_strdup(const char *func, const char *file, int line,
|
||||
const char *str)
|
||||
{
|
||||
void *buf = strdup(str);
|
||||
|
||||
if (buf == NULL)
|
||||
__util_oom(func, file, line, strlen(str) + 1);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate two strings or exit in case of failure
|
||||
*
|
||||
* The first string \a str1 is resized and a copy of the second
|
||||
* string \a str2 is appended to it.
|
||||
*
|
||||
* Therefore the first string \a str1 must either have been allocated
|
||||
* using malloc(), calloc(), or realloc() or must be NULL.
|
||||
*
|
||||
* @param[in] str1 Pointer to first string to concatenate, which
|
||||
* becomes invalid
|
||||
* @param[in] str2 Constant pointer to second string to concatenate
|
||||
*
|
||||
* @returns Pointer to concatenated string
|
||||
*/
|
||||
char *util_strcat_realloc(char *str1, const char *str2)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if (str1) {
|
||||
buf = util_realloc(str1, strlen(str1) + strlen(str2) + 1);
|
||||
strcat(buf, str2);
|
||||
} else {
|
||||
buf = util_strdup(str2);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to uppercase
|
||||
*
|
||||
* String \a str is converted to uppercase
|
||||
*
|
||||
* @param[in,out] str String to convert
|
||||
*/
|
||||
void util_str_toupper(char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; str[i] != '\0'; i++)
|
||||
str[i] = toupper(str[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print to newly allocated string or exit in case of failure
|
||||
*/
|
||||
int __util_vasprintf(const char *func, const char *file, int line,
|
||||
char **strp, const char *fmt, va_list ap)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = vasprintf(strp, fmt, ap);
|
||||
if (rc == -1)
|
||||
__util_oom(func, file, line, 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print to newly allocated string or exit in case of failure
|
||||
*/
|
||||
int __util_asprintf(const char *func, const char *file, int line,
|
||||
char **strp, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, fmt);
|
||||
rc = __util_vasprintf(func, file, line, strp, fmt, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print to string buffer or exit in case of failure
|
||||
*/
|
||||
int __util_vsprintf(const char *func, const char *file, int line,
|
||||
char *str, const char *fmt, va_list ap)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = vsprintf(str, fmt, ap);
|
||||
if (rc == -1)
|
||||
__util_assert("rc != -1", func, file, line,
|
||||
rc != -1, "Could not format string\n");
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* util_libc_example - Example program for util_libc
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
//! [code]
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_panic.h"
|
||||
|
||||
/*
|
||||
* Demonstrate that out of memory is automatically handled via panic()
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
unsigned long ulong_max = (unsigned long)-1;
|
||||
void *ptr;
|
||||
char *zeroes, *str;
|
||||
|
||||
/* Use util_strcat_realloc() for string concatenation */
|
||||
fprintf(stderr, "Try to concatenate \"Hello\", \", \" and \"world!\": ");
|
||||
str = util_strdup("Hello");
|
||||
str = util_strcat_realloc(str, ", ");
|
||||
str = util_strcat_realloc(str, "world!");
|
||||
fprintf(stderr, "result = \"%s\"\n", str);
|
||||
free(str);
|
||||
|
||||
/* One byte allocation should work */
|
||||
fprintf(stderr, "Try to allocate 1 byte: ");
|
||||
ptr = util_malloc(1);
|
||||
fprintf(stderr, "done\n");
|
||||
|
||||
/* One byte zeroed-allocation should work */
|
||||
fprintf(stderr, "Try to allocate 1 byte initialized with zeroes: ");
|
||||
zeroes = util_zalloc(1);
|
||||
fprintf(stderr, "done\n");
|
||||
util_assert(*zeroes == 0, "Garbage found in zero initialized memory\n");
|
||||
|
||||
/* The next allocation will probably fail */
|
||||
fprintf(stderr, "Try to allocate %lu bytes:\n", ulong_max);
|
||||
ptr = util_malloc(ulong_max);
|
||||
|
||||
fprintf(stderr, "You should not see me (ptr=%p)!\n", ptr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//! [code]
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Linked list functions
|
||||
*
|
||||
* Copyright IBM Corp. 2013, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_list.h"
|
||||
|
||||
/*
|
||||
* Node to entry
|
||||
*/
|
||||
static inline void *n2e(struct util_list *list, struct util_list_node *node)
|
||||
{
|
||||
return ((void *) node) - list->offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Entry to node
|
||||
*/
|
||||
static inline struct util_list_node *e2n(struct util_list *list, void *entry)
|
||||
{
|
||||
return entry + list->offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize linked list
|
||||
*/
|
||||
void util_list_init_offset(struct util_list *list, unsigned long offset)
|
||||
{
|
||||
memset(list, 0, sizeof(*list));
|
||||
list->offset = offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new linked list
|
||||
*/
|
||||
struct util_list *util_list_new_offset(unsigned long offset)
|
||||
{
|
||||
struct util_list *list = malloc(sizeof(*list));
|
||||
|
||||
if (!list)
|
||||
return NULL;
|
||||
util_list_init_offset(list, offset);
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free linked list
|
||||
*/
|
||||
void util_list_free(struct util_list *list)
|
||||
{
|
||||
free(list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add new element to end of list
|
||||
*/
|
||||
void util_list_add_tail(struct util_list *list, void *entry)
|
||||
{
|
||||
struct util_list_node *node = e2n(list, entry);
|
||||
|
||||
node->next = NULL;
|
||||
if (!list->start) {
|
||||
list->start = node;
|
||||
node->prev = NULL;
|
||||
} else {
|
||||
list->end->next = node;
|
||||
node->prev = list->end;
|
||||
}
|
||||
list->end = node;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add new element to front of list
|
||||
*/
|
||||
void util_list_add_head(struct util_list *list, void *entry)
|
||||
{
|
||||
struct util_list_node *node = e2n(list, entry);
|
||||
|
||||
node->prev = NULL;
|
||||
node->next = NULL;
|
||||
if (!list->start) {
|
||||
list->end = node;
|
||||
} else {
|
||||
list->start->prev = node;
|
||||
node->next = list->start;
|
||||
}
|
||||
list->start = node;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add new element (entry) after an existing element (list_entry)
|
||||
*/
|
||||
void util_list_add_next(struct util_list *list, void *entry, void *list_entry)
|
||||
{
|
||||
struct util_list_node *node = e2n(list, entry);
|
||||
struct util_list_node *list_node = e2n(list, list_entry);
|
||||
|
||||
node->next = list_node->next;
|
||||
node->prev = list_node;
|
||||
if (list_node->next)
|
||||
list_node->next->prev = node;
|
||||
else
|
||||
list->end = node;
|
||||
list_node->next = node;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add new element (entry) before an existing element (list_entry)
|
||||
*/
|
||||
void util_list_add_prev(struct util_list *list, void *entry, void *list_entry)
|
||||
{
|
||||
struct util_list_node *node = e2n(list, entry);
|
||||
struct util_list_node *list_node = e2n(list, list_entry);
|
||||
|
||||
node->prev = list_node->prev;
|
||||
node->next = list_node;
|
||||
if (list_node->prev)
|
||||
list_node->prev->next = node;
|
||||
else
|
||||
list->start = node;
|
||||
list_node->prev = node;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove element from list
|
||||
*/
|
||||
void util_list_remove(struct util_list *list, void *entry)
|
||||
{
|
||||
struct util_list_node *node = e2n(list, entry);
|
||||
|
||||
if (list->start == node)
|
||||
list->start = node->next;
|
||||
if (list->end == node)
|
||||
list->end = node->prev;
|
||||
if (node->prev)
|
||||
node->prev->next = node->next;
|
||||
if (node->next)
|
||||
node->next->prev = node->prev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get first element of list
|
||||
*/
|
||||
void *util_list_start(struct util_list *list)
|
||||
{
|
||||
if (!list->start)
|
||||
return NULL;
|
||||
return ((void *) list->start) - list->offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get last element of list
|
||||
*/
|
||||
void *util_list_end(struct util_list *list)
|
||||
{
|
||||
if (!list->end)
|
||||
return NULL;
|
||||
return n2e(list, list->end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get next element after entry
|
||||
*/
|
||||
void *util_list_next(struct util_list *list, void *entry)
|
||||
{
|
||||
struct util_list_node *node;
|
||||
|
||||
if (!entry)
|
||||
return NULL;
|
||||
node = e2n(list, entry);
|
||||
node = node->next;
|
||||
if (!node)
|
||||
return NULL;
|
||||
return n2e(list, node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get previous element before entry
|
||||
*/
|
||||
void *util_list_prev(struct util_list *list, void *entry)
|
||||
{
|
||||
struct util_list_node *node;
|
||||
|
||||
if (!entry)
|
||||
return NULL;
|
||||
node = e2n(list, entry);
|
||||
node = node->prev;
|
||||
if (!node)
|
||||
return NULL;
|
||||
return n2e(list, node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get number of list entries
|
||||
*/
|
||||
unsigned long util_list_len(struct util_list *list)
|
||||
{
|
||||
unsigned long cnt = 0;
|
||||
void *entry;
|
||||
|
||||
util_list_iterate(list, entry)
|
||||
cnt++;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort table (bubble sort)
|
||||
*/
|
||||
void util_list_sort(struct util_list *list, util_list_cmp_fn cmp_fn,
|
||||
void *data)
|
||||
{
|
||||
struct util_list_node *node1, *node2;
|
||||
unsigned long list_cnt, i, j;
|
||||
void *entry1, *entry2;
|
||||
|
||||
list_cnt = util_list_len(list);
|
||||
|
||||
for (i = 1; i < list_cnt; i++) {
|
||||
node1 = list->start;
|
||||
for (j = 0; j < list_cnt - i; j++) {
|
||||
node2 = node1->next;
|
||||
entry1 = n2e(list, node1);
|
||||
entry2 = n2e(list, node2);
|
||||
if (cmp_fn(entry1, entry2, data) > 0) {
|
||||
node1->next = node2->next;
|
||||
if (node1->next)
|
||||
node1->next->prev = node1;
|
||||
else
|
||||
list->end = node1;
|
||||
node2->next = node1;
|
||||
node2->prev = node1->prev;
|
||||
if (node2->prev)
|
||||
node2->prev->next = node2;
|
||||
else
|
||||
list->start = node2;
|
||||
node1->prev = node2;
|
||||
} else {
|
||||
node1 = node2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if list is empty
|
||||
*/
|
||||
int util_list_is_empty(struct util_list *list)
|
||||
{
|
||||
return list->start == NULL;
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Parse the command line options
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <argz.h>
|
||||
#include <libgen.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_prg.h"
|
||||
|
||||
/*
|
||||
* Private data
|
||||
*/
|
||||
/// @cond
|
||||
static struct util_opt_l {
|
||||
/* Option character string for getopt_long() */
|
||||
char *opt_str;
|
||||
/* Option array for getopt_long() */
|
||||
struct option *option_vec;
|
||||
/* Original util_opt array */
|
||||
struct util_opt *opt_vec;
|
||||
/* Length of longest option string */
|
||||
int opt_max;
|
||||
/* Command used for parsing */
|
||||
const char *command;
|
||||
} l;
|
||||
|
||||
struct util_opt_l *util_opt_l = &l;
|
||||
|
||||
/// @endcond
|
||||
|
||||
#define util_opt_iterate(opt) \
|
||||
for (opt = &l.opt_vec[0]; opt->desc != NULL; opt++)
|
||||
|
||||
#define MAX_OPTLEN 256
|
||||
|
||||
static int opt_max_len(void);
|
||||
|
||||
/**
|
||||
* Initialize the command line options
|
||||
*
|
||||
* Build short option string and long option array to be used for getopt_long().
|
||||
* The ":" prefix is added to the short option string for handling of "missing
|
||||
* required arguments".
|
||||
*
|
||||
* @param[in] opt_vec Option array
|
||||
* @param[in] opt_prefix Optional option string prefix
|
||||
*/
|
||||
void util_opt_init(struct util_opt *opt_vec, const char *opt_prefix)
|
||||
{
|
||||
int i, j, count;
|
||||
char *str;
|
||||
size_t prefix_len = opt_prefix ? strlen(opt_prefix) : 0;
|
||||
|
||||
opterr = 0;
|
||||
/* Get number of options */
|
||||
for (count = 0; opt_vec[count].desc != NULL; count++);
|
||||
/*
|
||||
* Allocate short option string for worst case when all options have
|
||||
* optional parameters e.g "x::" and long option string.
|
||||
*/
|
||||
l.opt_str = util_malloc(sizeof(char) * count * 3 + 2 + prefix_len);
|
||||
l.option_vec = util_malloc(sizeof(struct option) * (count + 1));
|
||||
l.opt_vec = opt_vec;
|
||||
|
||||
str = l.opt_str;
|
||||
if (opt_prefix) {
|
||||
strcpy(str, opt_prefix);
|
||||
str += prefix_len;
|
||||
}
|
||||
/* Force getopt_long() to return ':' for missing required arguments */
|
||||
*str++ = ':';
|
||||
/* Construction of input structures for getopt_long() function. */
|
||||
for (i = 0, j = 0; i < count; i++) {
|
||||
if (opt_vec[i].flags & UTIL_OPT_FLAG_SECTION)
|
||||
continue;
|
||||
if (!(opt_vec[i].flags & UTIL_OPT_FLAG_NOLONG)) {
|
||||
memcpy(&l.option_vec[j++], &opt_vec[i].option,
|
||||
sizeof(struct option));
|
||||
}
|
||||
if (opt_vec[i].flags & UTIL_OPT_FLAG_NOSHORT)
|
||||
continue;
|
||||
*str++ = opt_vec[i].option.val;
|
||||
switch (opt_vec[i].option.has_arg) {
|
||||
case no_argument:
|
||||
break;
|
||||
case required_argument:
|
||||
*str++ = ':';
|
||||
break;
|
||||
case optional_argument:
|
||||
*str++ = ':';
|
||||
*str++ = ':';
|
||||
break;
|
||||
default:
|
||||
util_panic("Unexpected \"has_arg\" parameter: %d\n",
|
||||
opt_vec[i].option.has_arg);
|
||||
}
|
||||
}
|
||||
/* Add end marker to option array and short option string */
|
||||
memset(&l.option_vec[j], 0, sizeof(struct option));
|
||||
*str = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current command for command line option processing
|
||||
*
|
||||
* @param[in] command The current command or NULL for no command
|
||||
*/
|
||||
void util_opt_set_command(const char *command)
|
||||
{
|
||||
l.command = command;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true, if option belongs to current command setting
|
||||
*/
|
||||
static bool opt_is_active(struct util_opt *opt)
|
||||
{
|
||||
if (!opt->command || !l.command)
|
||||
return true;
|
||||
return (strcmp(opt->command, l.command) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for getopt_long
|
||||
*
|
||||
* @param[in] argc Count of command line parameters
|
||||
* @param[in] argv Array of command line parameters
|
||||
*/
|
||||
int util_opt_getopt_long(int argc, char *argv[])
|
||||
{
|
||||
struct util_opt *opt;
|
||||
int val;
|
||||
|
||||
val = getopt_long(argc, argv, l.opt_str, l.option_vec, NULL);
|
||||
|
||||
switch (val) {
|
||||
case ':':
|
||||
case '?':
|
||||
case -1:
|
||||
break;
|
||||
default:
|
||||
if (!l.command)
|
||||
break;
|
||||
util_opt_iterate(opt) {
|
||||
if (!opt_is_active(opt))
|
||||
continue;
|
||||
if (opt->option.val == val)
|
||||
goto out;
|
||||
}
|
||||
/* No valid option found for command */
|
||||
val = '?';
|
||||
if (optarg)
|
||||
optind--;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Format option name: Add short, long option and argument (as applicable)
|
||||
*/
|
||||
static void format_opt(char *buf, size_t maxlen, const struct util_opt *opt)
|
||||
{
|
||||
int has_arg, flags, rc;
|
||||
char val, *arg_str;
|
||||
const char *name;
|
||||
|
||||
has_arg = opt->option.has_arg;
|
||||
name = opt->option.name;
|
||||
val = opt->option.val;
|
||||
flags = opt->flags;
|
||||
|
||||
/* Prepare potential option argument string */
|
||||
if (has_arg == optional_argument) {
|
||||
if (flags & UTIL_OPT_FLAG_NOLONG)
|
||||
util_asprintf(&arg_str, "[%s]", opt->argument);
|
||||
else
|
||||
util_asprintf(&arg_str, "[=%s]", opt->argument);
|
||||
} else if (has_arg == required_argument) {
|
||||
util_asprintf(&arg_str, " %s", opt->argument);
|
||||
} else {
|
||||
util_asprintf(&arg_str, "");
|
||||
}
|
||||
|
||||
/* Format the option */
|
||||
if (flags & UTIL_OPT_FLAG_NOLONG)
|
||||
rc = snprintf(buf, maxlen, "-%c%s", val, arg_str);
|
||||
else if (flags & UTIL_OPT_FLAG_NOSHORT)
|
||||
rc = snprintf(buf, maxlen, " --%s%s", name, arg_str);
|
||||
else
|
||||
rc = snprintf(buf, maxlen, "-%c, --%s%s", val, name, arg_str);
|
||||
|
||||
util_assert(rc < (int)maxlen, "Option too long: %s\n", name);
|
||||
free(arg_str);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true, if option is to be printed for the current command setting
|
||||
*/
|
||||
static bool should_print_opt(const struct util_opt *opt)
|
||||
{
|
||||
if (l.command) {
|
||||
/* Print only options that belong to command */
|
||||
return opt->command ? !strcmp(opt->command, l.command) : false;
|
||||
} else {
|
||||
/* Print only common options (standard for non-command tools) */
|
||||
return opt->command ? false : true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return size of the longest formatted option
|
||||
*/
|
||||
static int opt_max_len(void)
|
||||
{
|
||||
const struct util_opt *opt;
|
||||
unsigned int max = 0;
|
||||
char opt_str[MAX_OPTLEN];
|
||||
|
||||
util_opt_iterate(opt) {
|
||||
if (opt->flags & UTIL_OPT_FLAG_SECTION)
|
||||
continue;
|
||||
if (!should_print_opt(opt))
|
||||
continue;
|
||||
format_opt(opt_str, MAX_OPTLEN, opt);
|
||||
max = MAX(max, strlen(opt_str));
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an option name, followed by a description indented to fit the
|
||||
* longest option name
|
||||
*/
|
||||
void util_opt_print_indented(const char *opt, const char *desc)
|
||||
{
|
||||
printf(" %-*s ", l.opt_max + 1, opt);
|
||||
util_print_indented(desc, 3 + l.opt_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the usage of the command line options to the console
|
||||
*/
|
||||
void util_opt_print_help(void)
|
||||
{
|
||||
char opt_str[MAX_OPTLEN];
|
||||
struct util_opt *opt;
|
||||
int first = 1;
|
||||
|
||||
/*
|
||||
* Create format string: " -%c, --%-<long opt size>s %s"
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* -p, --print STRING Print STRING to console
|
||||
*/
|
||||
l.opt_max = opt_max_len();
|
||||
|
||||
util_opt_iterate(opt) {
|
||||
if (!should_print_opt(opt))
|
||||
continue;
|
||||
if (opt->flags & UTIL_OPT_FLAG_SECTION) {
|
||||
printf("%s%s\n", first ? "" : "\n", opt->desc);
|
||||
first = 0;
|
||||
continue;
|
||||
}
|
||||
format_opt(opt_str, MAX_OPTLEN, opt);
|
||||
util_opt_print_indented(opt_str, opt->desc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print option parsing error message
|
||||
*
|
||||
* This function should be used when the return code of the
|
||||
* util_opt_getopt_long() function returns a character that does
|
||||
* not match any of the expected options.
|
||||
*
|
||||
* @param[in] opt Short option returned by getopt_long()
|
||||
* @param[in] argv Option array
|
||||
*/
|
||||
void util_opt_print_parse_error(char opt, char *argv[])
|
||||
{
|
||||
char optopt_str[3];
|
||||
|
||||
switch (opt) {
|
||||
case ':':
|
||||
/* A required option argument has not been specified */
|
||||
util_prg_print_required_arg(argv[optind - 1]);
|
||||
break;
|
||||
case '?':
|
||||
/* An invalid option has been specified */
|
||||
if (optopt) {
|
||||
/* Short option */
|
||||
sprintf(optopt_str, "-%c", optopt);
|
||||
util_prg_print_invalid_option(optopt_str);
|
||||
} else {
|
||||
/* Long option */
|
||||
util_prg_print_invalid_option(argv[optind - 1]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
util_panic("Option '%c' should not be handled here\n", opt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* util_opt_command_example - Example program for util_opt with commands
|
||||
*
|
||||
* Copyright 2017 IBM Corp.
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
|
||||
//! [code]
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
|
||||
#define OPT_NOSHORT 256
|
||||
|
||||
#define COMMAND_PULL "pull"
|
||||
#define COMMAND_PUSH "push"
|
||||
|
||||
/*
|
||||
* Define the command line options
|
||||
*/
|
||||
static struct util_opt opt_vec[] = {
|
||||
{
|
||||
.desc = "OPTIONS",
|
||||
.flags = UTIL_OPT_FLAG_SECTION,
|
||||
.command = COMMAND_PULL,
|
||||
},
|
||||
{
|
||||
.option = { "single", no_argument, NULL, 's'},
|
||||
.desc = "A single option without an argument",
|
||||
.command = COMMAND_PULL,
|
||||
},
|
||||
{
|
||||
.option = { "req_arg", required_argument, NULL, 'r'},
|
||||
.argument = "REQ_ARG",
|
||||
.desc = "Option with a required argument REQ_ARG",
|
||||
.command = COMMAND_PULL,
|
||||
},
|
||||
{
|
||||
.desc = "OPTIONS",
|
||||
.flags = UTIL_OPT_FLAG_SECTION,
|
||||
.command = COMMAND_PUSH,
|
||||
},
|
||||
{
|
||||
.option = { "noshort", no_argument, NULL, OPT_NOSHORT},
|
||||
.desc = "Option with only a long name",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.command = COMMAND_PUSH,
|
||||
},
|
||||
{
|
||||
.option = { NULL, no_argument, NULL, 'l'},
|
||||
.desc = "Option with only a short name",
|
||||
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||
.command = "push",
|
||||
},
|
||||
UTIL_OPT_SECTION("COMMON OPTIONS"),
|
||||
/* Standard option: -h,--help */
|
||||
UTIL_OPT_HELP,
|
||||
/* Standard option: -v,--version */
|
||||
UTIL_OPT_VERSION,
|
||||
/* End-marker for option vector */
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
static char usage_global_start[] =
|
||||
"Usage: util_opt_command_example [COMMAND] [OPTIONS]\n"
|
||||
"\n"
|
||||
"Demonstrate how programs with commands can use the \"util_opt\" library.\n"
|
||||
"\n"
|
||||
"COMMANDS\n"
|
||||
" push Push some content to somewhere\n"
|
||||
" pull Pull some content from somewhere\n";
|
||||
|
||||
static char usage_global_end[] =
|
||||
"For more information use 'util_opt_command_example COMMAND --help'.\n";
|
||||
|
||||
static char usage_push_start[] =
|
||||
"Usage: util_opt_command_example push [OPTIONS]\n"
|
||||
"\n"
|
||||
"Push some content to somewhere.\n";
|
||||
|
||||
static char usage_pull_start[] =
|
||||
"Usage: util_opt_command_example pull [OPTIONS]\n"
|
||||
"\n"
|
||||
"Pull some content from somewhere.\n";
|
||||
|
||||
/*
|
||||
* Print help header for command or program
|
||||
*/
|
||||
static void command_print_help_start(const char *command)
|
||||
{
|
||||
if (command == NULL)
|
||||
printf("%s", usage_global_start);
|
||||
else if (strcmp(command, COMMAND_PUSH) == 0)
|
||||
printf("%s", usage_push_start);
|
||||
else if (strcmp(command, COMMAND_PULL) == 0)
|
||||
printf("%s", usage_pull_start);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Print help footer
|
||||
*/
|
||||
static void command_print_help_end(const char *command)
|
||||
{
|
||||
if (command == NULL)
|
||||
printf("\n%s", usage_global_end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the command line options with util_opt functions
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c, my_argc = argc;
|
||||
char **my_argv = argv;
|
||||
char *command = NULL;
|
||||
|
||||
/* Install option vector */
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
/* The command name is the very first argument */
|
||||
if (argc >= 2 && strncmp(argv[1], "-", 1) != 0) {
|
||||
command = argv[1];
|
||||
my_argc--;
|
||||
my_argv = &argv[1];
|
||||
|
||||
if (strcasecmp(command, COMMAND_PULL) != 0 &&
|
||||
strcasecmp(command, COMMAND_PUSH) != 0) {
|
||||
fprintf(stderr, "%s: Invalid command '%s'\n",
|
||||
program_invocation_short_name, argv[1]);
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the current command (if any) */
|
||||
util_opt_set_command(command);
|
||||
util_prg_set_command(command);
|
||||
|
||||
/* Parse all options specified in my_argv[] */
|
||||
while (1) {
|
||||
/* Get the next option 'c' from my_argv[] */
|
||||
c = util_opt_getopt_long(my_argc, my_argv);
|
||||
/* No more options on command line? */
|
||||
if (c == -1)
|
||||
break;
|
||||
/* Find the right action for option 'c' */
|
||||
switch (c) {
|
||||
case 'h':
|
||||
command_print_help_start(command);
|
||||
util_opt_print_help();
|
||||
command_print_help_end(command);
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
printf("Specified: --version\n");
|
||||
return EXIT_SUCCESS;
|
||||
case 's':
|
||||
printf("Specified: --single\n");
|
||||
break;
|
||||
case 'r':
|
||||
printf("Specified: --req_arg %s\n", optarg);
|
||||
break;
|
||||
case OPT_NOSHORT:
|
||||
printf("Specified: --noshort\n");
|
||||
break;
|
||||
case 'l':
|
||||
printf("Specified: -l\n");
|
||||
break;
|
||||
default:
|
||||
util_opt_print_parse_error(c, my_argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < my_argc) {
|
||||
util_prg_print_arg_error(my_argv[optind]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (command == NULL) {
|
||||
fprintf(stderr, "%s: Command is required\n",
|
||||
program_invocation_short_name);
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (strcasecmp(command, COMMAND_PULL) == 0) {
|
||||
printf("Run the pull command\n");
|
||||
return EXIT_SUCCESS;
|
||||
} else if (!strcasecmp(command, COMMAND_PUSH)) {
|
||||
printf("Run the push command\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: Invalid command '%s'\n",
|
||||
program_invocation_short_name, argv[1]);
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//! [code]
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* util_opt_example - Example program for util_opt
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
|
||||
//! [code]
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
|
||||
#define OPT_NOSHORT 256
|
||||
|
||||
/*
|
||||
* Define the command line options
|
||||
*/
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTION WITHOUT ARGUMENTS"),
|
||||
/* Our own options */
|
||||
{
|
||||
.option = { "single", no_argument, NULL, 's'},
|
||||
.desc = "A single option without an argument",
|
||||
},
|
||||
UTIL_OPT_SECTION("OPTIONS WITH ARGUMENTS"),
|
||||
{
|
||||
.option = { "req_arg", required_argument, NULL, 'r'},
|
||||
.argument = "REQ_ARG",
|
||||
.desc = "Option with a required argument REQ_ARG",
|
||||
},
|
||||
{
|
||||
/*
|
||||
* NOTE: For specifying an optional parameter OPT_ARG use
|
||||
* either "-oOPT_ARG" or "--opt_arg=OPT_ARG" on the commandline.
|
||||
* Specifying "-o OPT_ARG" or "--opt_arg OPT_ARG" will not work.
|
||||
*/
|
||||
.option = { "opt_arg", optional_argument, NULL, 'o'},
|
||||
.argument = "OPT_ARG",
|
||||
.desc = "Option with an optional argument OPT_ARG. " \
|
||||
"We don't recommend using this feature.",
|
||||
},
|
||||
UTIL_OPT_SECTION("OPTION WITHOUT SHORT OPTION"),
|
||||
{
|
||||
.option = { "noshort", no_argument, NULL, OPT_NOSHORT},
|
||||
.desc = "Option with only a long name",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
UTIL_OPT_SECTION("OPTION WITHOUT LONG OPTION"),
|
||||
{
|
||||
.option = { NULL, no_argument, NULL, 'l'},
|
||||
.desc = "Option with only a short name",
|
||||
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||
},
|
||||
UTIL_OPT_SECTION("OPTION WITH MANUALLY FORMATTED DESCRIPTION"),
|
||||
{
|
||||
.option = { "manual", no_argument, NULL, 'm' },
|
||||
.desc = "Option descriptions can be formatted\n" \
|
||||
"using the new line character '\\n' to:\n" \
|
||||
" - Display descriptions more meaningful\n" \
|
||||
" - Create lists within the description",
|
||||
},
|
||||
UTIL_OPT_SECTION("STANDARD OPTIONS"),
|
||||
/* Standard option: -h,--help */
|
||||
UTIL_OPT_HELP,
|
||||
/* Standard option: -v,--version */
|
||||
UTIL_OPT_VERSION,
|
||||
/* End-marker for option vector */
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
/*
|
||||
* Parse the command line options with util_opt functions
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
|
||||
/* Install option vector */
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
/* Parse all options specified in argv[] */
|
||||
while (1) {
|
||||
/* Get the next option 'c' from argv[] */
|
||||
c = util_opt_getopt_long(argc, argv);
|
||||
/* No more options on command line? */
|
||||
if (c == -1)
|
||||
break;
|
||||
/* Find the right action for option 'c' */
|
||||
switch (c) {
|
||||
case 'h':
|
||||
util_opt_print_help();
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
printf("Specified: --version\n");
|
||||
return EXIT_SUCCESS;
|
||||
case 's':
|
||||
printf("Specified: --single\n");
|
||||
break;
|
||||
case 'o':
|
||||
if (optarg != NULL)
|
||||
printf("Specified: --opt_arg %s\n", optarg);
|
||||
else
|
||||
printf("Specified: --opt_arg [without arg]\n");
|
||||
break;
|
||||
case 'r':
|
||||
printf("Specified: --req_arg %s\n", optarg);
|
||||
break;
|
||||
case OPT_NOSHORT:
|
||||
printf("Specified: --noshort\n");
|
||||
break;
|
||||
default:
|
||||
util_opt_print_parse_error(c, argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
//! [code]
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Collect FFDC data for unexpected errors
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <execinfo.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_panic.h"
|
||||
|
||||
/*
|
||||
* Obtain a backtrace and print it to stderr
|
||||
*
|
||||
* To get symbols, compile the code with "-rdynamic".
|
||||
*/
|
||||
static void print_backtrace(void)
|
||||
{
|
||||
void *array[256];
|
||||
size_t i, size;
|
||||
char **strings;
|
||||
|
||||
fprintf(stderr, "Backtrace:\n\n");
|
||||
size = backtrace(array, UTIL_ARRAY_SIZE(array));
|
||||
strings = backtrace_symbols(array, size);
|
||||
if (strings == NULL) {
|
||||
fprintf(stderr, " Could not obtain backtrace (ENOMEM)\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < size; i++)
|
||||
fprintf(stderr, " %s\n", strings[i]);
|
||||
|
||||
free(strings);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for core ulimit
|
||||
*/
|
||||
static void ulimit_core_check(void)
|
||||
{
|
||||
struct rlimit limit;
|
||||
|
||||
if (getrlimit(RLIMIT_CORE, &limit) != 0)
|
||||
return;
|
||||
if (limit.rlim_cur != 0)
|
||||
return;
|
||||
fprintf(stderr, "Core dump size is zero. To get a full core dump use 'ulimit -c unlimited'.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Print FFDC data and then abort
|
||||
*/
|
||||
static void panic_finish(const char *func, const char *file, int line,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
/* Write panic error string */
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Error string:\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
/* Write file, line number, and function name */
|
||||
fprintf(stderr, "Location:\n\n");
|
||||
fprintf(stderr, " %s:%d: %s()\n", file, line, func);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
/* Print the function backtrace */
|
||||
print_backtrace();
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
ulimit_core_check();
|
||||
fprintf(stderr, "----------------------------------------------------------------------->8-----\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Do panic processing if the assumption is not true
|
||||
*/
|
||||
void __util_assert(const char *assertion_str,
|
||||
const char *func, const char *file, int line,
|
||||
int assumption, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (assumption)
|
||||
return;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "---8<-------------------------------------------------------------------------\n");
|
||||
fprintf(stderr, "ASSERTION FAILED: The application terminated due to an internal or OS error\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "The following assumption was *not* true:\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " %s\n", assertion_str);
|
||||
panic_finish(func, file, line, fmt, ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do panic processing
|
||||
*/
|
||||
void __noreturn __util_panic(const char *func, const char *file, int line,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "---8<-------------------------------------------------------------------------\n");
|
||||
fprintf(stderr, "PANIC: The application terminated due to an unrecoverable error\n");
|
||||
panic_finish(func, file, line, fmt, ap);
|
||||
while(1);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* util_panic_example - Example program for util_panic
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
//! [code]
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "lib/util_panic.h"
|
||||
|
||||
/* Make functions noinline to have a nice backtrace */
|
||||
#define __noinline __attribute__((noinline))
|
||||
|
||||
/*
|
||||
* Test util_panic()
|
||||
*/
|
||||
__noinline void panic_test_func(void)
|
||||
{
|
||||
fprintf(stderr, "Testing util_panic() now\n");
|
||||
|
||||
util_panic("Adieu beautiful world ...\n");
|
||||
|
||||
fprintf(stderr, "You should not see this\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Test util_panic() with errno handling
|
||||
*/
|
||||
__noinline void panic_errno_test_func(void)
|
||||
{
|
||||
const char *file = "i_do_not_exist";
|
||||
|
||||
if (fopen("i_do_not_exist", "r") == NULL) {
|
||||
util_panic("Open file \"%s\" failed: %s\n",
|
||||
file, strerror(errno));
|
||||
}
|
||||
fprintf(stderr, "You should not see this\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Test util_assert()
|
||||
*/
|
||||
__noinline void assert_test_func(void)
|
||||
{
|
||||
char *drink_actual = "beer";
|
||||
|
||||
fprintf(stderr, "Testing util_assert() now\n");
|
||||
|
||||
util_assert(strcmp(drink_actual, "water") == 0,
|
||||
"We expected \"%s\" but got \"%s\"\n",
|
||||
"water", drink_actual);
|
||||
|
||||
fprintf(stderr, "You should not see this\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Demonstrate util_panic() and util_assert()
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct rlimit rlim_unlimited = {-1, -1};
|
||||
struct rlimit rlim_zero = {0, 0};
|
||||
|
||||
if (argc != 2)
|
||||
goto fail;
|
||||
|
||||
if (strcmp(argv[1], "util_panic") == 0) {
|
||||
fprintf(stderr, "Disable core files: ulimit -c 0\n");
|
||||
setrlimit(RLIMIT_CORE, &rlim_zero);
|
||||
/* Do the panic */
|
||||
panic_test_func();
|
||||
} else if (strcmp(argv[1], "util_panic_errno") == 0) {
|
||||
setrlimit(RLIMIT_CORE, &rlim_unlimited);
|
||||
/* Do the panic */
|
||||
panic_errno_test_func();
|
||||
} else if (strcmp(argv[1], "util_assert") == 0) {
|
||||
fprintf(stderr, "Enable core files: ulimit -c unlimited\n");
|
||||
setrlimit(RLIMIT_CORE, &rlim_unlimited);
|
||||
/* Do the assertion */
|
||||
assert_test_func();
|
||||
}
|
||||
fail:
|
||||
fprintf(stderr, "Usage: %s util_panic|util_panic_errno|util_assert\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//! [code]
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Partition detection functions
|
||||
*
|
||||
* Copyright IBM Corp. 2013, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_part.h"
|
||||
|
||||
#define GPT_SIGNATURE 0x4546492050415254ULL /* EFI PART */
|
||||
#define MBR_SIGNATURE 0x55aa
|
||||
#define MBR_PART_TYPE_DOS_EXT 0x05 /* DOS extended partition */
|
||||
#define MBR_PART_TYPE_WIN98_EXT 0x0f /* Windows 98 extended partition */
|
||||
#define MBR_PART_TYPE_LINUX_EXT 0x85 /* Linux extended partition */
|
||||
#define MBR_PART_TYPE_GPT 0xee /* GPT partition */
|
||||
#define MBR_EXT_PART_NUM_FIRST 5 /* Partition number for first logical vol */
|
||||
|
||||
/*
|
||||
* MBR/MSDOS partition entry
|
||||
*/
|
||||
struct mbr_part_entry {
|
||||
uint8_t status;
|
||||
uint8_t chs_start[3];
|
||||
uint8_t type;
|
||||
uint8_t chs_end[3];
|
||||
uint32_t blk_start;
|
||||
uint32_t blk_cnt;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* Master Boot Record (MBR)
|
||||
*/
|
||||
struct mbr {
|
||||
uint8_t reserved[0x1be];
|
||||
struct mbr_part_entry part_entry_vec[4];
|
||||
uint16_t signature;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* GUID Partition Table (GPT) header
|
||||
*/
|
||||
struct gpt {
|
||||
uint64_t signature;
|
||||
uint32_t version;
|
||||
uint32_t hdr_size;
|
||||
uint32_t hdr_crc;
|
||||
uint32_t reserved1;
|
||||
uint64_t blk_cur;
|
||||
uint64_t blk_back;
|
||||
uint64_t blk_first;
|
||||
uint64_t blk_last;
|
||||
uint8_t guid[16];
|
||||
uint64_t part_tab_blk_start;
|
||||
uint32_t part_tab_cnt;
|
||||
uint32_t part_tab_entry_size;
|
||||
uint32_t part_tab_crc;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* GPT partition entry
|
||||
*/
|
||||
struct gpt_part_entry {
|
||||
uint8_t type[16];
|
||||
uint8_t guid[16];
|
||||
uint64_t blk_start;
|
||||
uint64_t blk_end;
|
||||
uint64_t attr;
|
||||
char name[72];
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* Check for extended partition
|
||||
*/
|
||||
static int mbr_part_is_ext(uint8_t type)
|
||||
{
|
||||
if ((type == MBR_PART_TYPE_DOS_EXT) ||
|
||||
(type == MBR_PART_TYPE_WIN98_EXT) ||
|
||||
(type == MBR_PART_TYPE_LINUX_EXT))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if disk has a classic MBR partion table
|
||||
*/
|
||||
static int mbr_table_valid(struct mbr *mbr)
|
||||
{
|
||||
return mbr->signature == MBR_SIGNATURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search partition in logical volumes of an extended partition
|
||||
*/
|
||||
static int mbr_table_ext_search(int fh, size_t blk_start_mbr,
|
||||
size_t blk_start, size_t blk_cnt,
|
||||
size_t blk_size, int part_num)
|
||||
{
|
||||
size_t start, cnt, start_next;
|
||||
struct mbr mbr;
|
||||
|
||||
/* Read MBR for logical volume */
|
||||
if (lseek(fh, blk_start_mbr * blk_size, SEEK_SET) == (off_t)-1)
|
||||
return -1;
|
||||
if (read(fh, &mbr, sizeof(mbr)) == -1)
|
||||
return -1;
|
||||
|
||||
/* Check for invalid MBR or last entry */
|
||||
if (mbr.signature != MBR_SIGNATURE)
|
||||
return -1;
|
||||
if (mbr.part_entry_vec[0].blk_start == 0)
|
||||
return -1;
|
||||
|
||||
/* First entry contains a relative offset for current logical volume */
|
||||
start = blk_start_mbr + le32toh(mbr.part_entry_vec[0].blk_start);
|
||||
cnt = le32toh(mbr.part_entry_vec[0].blk_cnt);
|
||||
|
||||
if ((start == blk_start) && (cnt == blk_cnt))
|
||||
return part_num;
|
||||
|
||||
/* Second entry contains relative offset for next logical volume */
|
||||
start_next = le32toh(mbr.part_entry_vec[1].blk_start);
|
||||
if (start_next == 0)
|
||||
return 0;
|
||||
start_next += blk_start_mbr;
|
||||
|
||||
/* Recursively search for next logical volume in chain */
|
||||
return mbr_table_ext_search(fh, start_next, blk_start, blk_cnt,
|
||||
blk_size, part_num + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Search partition in MBR partition table
|
||||
*/
|
||||
static int mbr_table_search(int fh, struct mbr *mbr, size_t blk_start,
|
||||
size_t blk_cnt, size_t blk_size, int *part_ext)
|
||||
{
|
||||
int part_num_ext, part_num;
|
||||
size_t start, cnt;
|
||||
uint8_t type;
|
||||
|
||||
for (part_num = 1; part_num <= 4; part_num++) {
|
||||
type = mbr->part_entry_vec[part_num - 1].type;
|
||||
start = le32toh(mbr->part_entry_vec[part_num - 1].blk_start);
|
||||
cnt = le32toh(mbr->part_entry_vec[part_num - 1].blk_cnt);
|
||||
if (start == 0) /* Empty slot */
|
||||
continue;
|
||||
/*
|
||||
* The kernel sets count for extended partitions explicitly.
|
||||
* Therefore we do not check count here.
|
||||
*/
|
||||
if (mbr_part_is_ext(type) && (start == blk_start)) {
|
||||
*part_ext = 1;
|
||||
return part_num;
|
||||
}
|
||||
if ((start == blk_start) && (cnt == blk_cnt))
|
||||
return part_num;
|
||||
if (!mbr_part_is_ext(type))
|
||||
continue;
|
||||
part_num_ext = mbr_table_ext_search(fh, start, blk_start,
|
||||
blk_cnt, blk_size,
|
||||
MBR_EXT_PART_NUM_FIRST);
|
||||
if (part_num_ext != 0)
|
||||
return part_num_ext;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search partition in GPT partition table
|
||||
*/
|
||||
static int gpt_table_search(int fh, struct gpt *gpt, size_t blk_start,
|
||||
size_t blk_cnt, size_t blk_size)
|
||||
{
|
||||
size_t start, end, part_tab_blk_start, blk_end;
|
||||
uint32_t part_tab_cnt, part_tab_entry_size;
|
||||
struct gpt_part_entry *part_entry;
|
||||
unsigned int part_num;
|
||||
|
||||
blk_end = blk_start + blk_cnt - 1;
|
||||
part_tab_entry_size = le32toh(gpt->part_tab_entry_size);
|
||||
part_tab_cnt = le32toh(gpt->part_tab_cnt);
|
||||
part_tab_blk_start = le64toh(gpt->part_tab_blk_start);
|
||||
|
||||
if (lseek(fh, part_tab_blk_start * blk_size, SEEK_SET) == (off_t)-1)
|
||||
return -1;
|
||||
for (part_num = 1; part_num <= part_tab_cnt; part_num++) {
|
||||
char buf[part_tab_entry_size];
|
||||
|
||||
part_entry = (struct gpt_part_entry *) buf;
|
||||
if (read(fh, buf, sizeof(buf)) == -1)
|
||||
return -1;
|
||||
start = le64toh(part_entry->blk_start);
|
||||
end = le64toh(part_entry->blk_end);
|
||||
if (start == 0) /* Empty slot */
|
||||
continue;
|
||||
if ((start == blk_start) && (end == blk_end))
|
||||
return part_num;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if disk has a GPT partition table
|
||||
*/
|
||||
static int gpt_table_valid(struct gpt *gpt, struct mbr *mbr)
|
||||
{
|
||||
int cnt, part_num;
|
||||
uint32_t start;
|
||||
uint8_t type;
|
||||
|
||||
if (gpt->signature != GPT_SIGNATURE)
|
||||
return 0;
|
||||
|
||||
/* Check for protective MBR (one reserved GPT partition) */
|
||||
for (part_num = 1, cnt = 0; part_num <= 4; part_num++) {
|
||||
start = le32toh(mbr->part_entry_vec[part_num - 1].blk_start);
|
||||
type = mbr->part_entry_vec[part_num - 1].type;
|
||||
if (!start)
|
||||
continue;
|
||||
if (type != MBR_PART_TYPE_GPT)
|
||||
return 0;
|
||||
if (++cnt > 1)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for partition with given start block and count
|
||||
*
|
||||
* Return partition number when found, 0 when not found, and on error -1
|
||||
* Set "part_ext" to 1 for extended partitions otherwise to 0.
|
||||
*/
|
||||
int util_part_search_fh(int fh, size_t blk_start, size_t blk_cnt,
|
||||
size_t blk_size, int *part_ext)
|
||||
{
|
||||
struct gpt gpt;
|
||||
struct mbr mbr;
|
||||
|
||||
if (lseek(fh, 0, SEEK_SET) == (off_t)-1)
|
||||
return -1;
|
||||
if (read(fh, &mbr, sizeof(mbr)) == -1)
|
||||
return -1;
|
||||
if (lseek(fh, blk_size, SEEK_SET) == (off_t)-1)
|
||||
return -1;
|
||||
if (read(fh, &gpt, sizeof(gpt)) == -1)
|
||||
return -1;
|
||||
*part_ext = 0;
|
||||
if (gpt_table_valid(&gpt, &mbr))
|
||||
return gpt_table_search(fh, &gpt, blk_start, blk_cnt, blk_size);
|
||||
if (mbr_table_valid(&mbr))
|
||||
return mbr_table_search(fh, &mbr, blk_start, blk_cnt, blk_size,
|
||||
part_ext);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for partition with given start block and count
|
||||
*
|
||||
* Return partition number when found, 0 when not found, and on error -1
|
||||
* Set "part_ext" to 1 for extended partitions otherwise to 0.
|
||||
*/
|
||||
int util_part_search(const char *device, size_t blk_start, size_t blk_cnt,
|
||||
size_t blk_size, int *part_ext)
|
||||
{
|
||||
int rc, fh;
|
||||
|
||||
fh = open(device, O_RDONLY);
|
||||
if (fh == -1)
|
||||
return -1;
|
||||
rc = util_part_search_fh(fh, blk_start, blk_cnt, blk_size, part_ext);
|
||||
close(fh);
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Work with paths
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_proc.h"
|
||||
|
||||
/*
|
||||
* Verify that directory exists
|
||||
*/
|
||||
static void verify_dir(const char *dir)
|
||||
{
|
||||
struct stat sb;
|
||||
int rc;
|
||||
|
||||
rc = stat(dir, &sb);
|
||||
if (rc < 0)
|
||||
err(EXIT_FAILURE, "Could not access directory: %s", dir);
|
||||
if (!S_ISDIR(sb.st_mode))
|
||||
errx(EXIT_FAILURE, "Is not a directory: %s", dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return sysfs mount point
|
||||
*/
|
||||
static char *sys_mount_point(void)
|
||||
{
|
||||
struct util_proc_mnt_entry mnt_entry;
|
||||
static char *mount_point;
|
||||
char *dir;
|
||||
|
||||
if (mount_point)
|
||||
return mount_point;
|
||||
/* Check the environment variable */
|
||||
dir = getenv("SYSFS_ROOT");
|
||||
if (dir) {
|
||||
mount_point = util_strdup(dir);
|
||||
} else {
|
||||
if (util_proc_mnt_get_entry("/proc/mounts", "sysfs",
|
||||
&mnt_entry))
|
||||
errx(EXIT_FAILURE, "No mount point found for sysfs");
|
||||
mount_point = util_strdup(mnt_entry.file);
|
||||
util_proc_mnt_free_entry(&mnt_entry);
|
||||
}
|
||||
verify_dir(mount_point);
|
||||
return mount_point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a sysfs path
|
||||
*
|
||||
* The arguments of the function are used to specify a subdirectory under
|
||||
* sysfs root.
|
||||
*
|
||||
* @param[in] fmt Format string for path
|
||||
* @param[in] ... Variable arguments for format string
|
||||
*
|
||||
* @returns Allocated path
|
||||
*/
|
||||
char *util_path_sysfs(const char *fmt, ...)
|
||||
{
|
||||
char *path, *fmt_tot;
|
||||
va_list ap;
|
||||
|
||||
util_asprintf(&fmt_tot, "%s/%s", sys_mount_point(), fmt);
|
||||
/* Format and return full sysfs path */
|
||||
va_start(ap, fmt);
|
||||
util_vasprintf(&path, fmt_tot, ap);
|
||||
va_end(ap);
|
||||
free(fmt_tot);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if path exists and is readable
|
||||
*
|
||||
* This function has the same semantics as "-r path" in bash.
|
||||
*
|
||||
* @param[in] fmt Format string for path to test
|
||||
* @param[in] ... Variable arguments for format string
|
||||
*
|
||||
* @returns true Path exists and is readable
|
||||
* false Otherwise
|
||||
*/
|
||||
bool util_path_is_readable(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *path;
|
||||
bool rc;
|
||||
|
||||
UTIL_VASPRINTF(&path, fmt, ap);
|
||||
rc = access(path, R_OK) == 0 ? true : false;
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if path exists and is writable
|
||||
*
|
||||
* This function has the same semantics as "-w path" in bash.
|
||||
*
|
||||
* @param[in] fmt Format string for path to test
|
||||
* @param[in] ... Variable arguments for format string
|
||||
*
|
||||
* @returns true Path exists and is writable
|
||||
* false Otherwise
|
||||
*/
|
||||
bool util_path_is_writable(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *path;
|
||||
bool rc;
|
||||
|
||||
UTIL_VASPRINTF(&path, fmt, ap);
|
||||
rc = access(path, W_OK) == 0 ? true : false;
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if path exists and is a regular file
|
||||
*
|
||||
* This function has the same semantics as "-f path" in bash.
|
||||
*
|
||||
* @param[in] fmt Format string for path to test
|
||||
* @param[in] ... Variable arguments for format string
|
||||
*
|
||||
* @returns true Path exists and is a regular file
|
||||
* false Otherwise
|
||||
*/
|
||||
bool util_path_is_reg_file(const char *fmt, ...)
|
||||
{
|
||||
struct stat sb;
|
||||
va_list ap;
|
||||
char *path;
|
||||
bool rc;
|
||||
|
||||
UTIL_VASPRINTF(&path, fmt, ap);
|
||||
if (stat(path, &sb)) {
|
||||
rc = false;
|
||||
goto free_str;
|
||||
}
|
||||
rc = (sb.st_mode & S_IFREG) ? true : false;
|
||||
free_str:
|
||||
free(path);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if path exists and is a directory
|
||||
*
|
||||
* This function has the same semantics as "-d path" in bash.
|
||||
*
|
||||
* @param[in] fmt Format string for path to test
|
||||
* @param[in] ... Variable arguments for format string
|
||||
*
|
||||
* @returns true Path exists and is a directory
|
||||
* false Otherwise
|
||||
*/
|
||||
bool util_path_is_dir(const char *fmt, ...)
|
||||
{
|
||||
struct stat sb;
|
||||
va_list ap;
|
||||
char *path;
|
||||
bool rc;
|
||||
|
||||
UTIL_VASPRINTF(&path, fmt, ap);
|
||||
if (stat(path, &sb)) {
|
||||
rc = false;
|
||||
goto free_str;
|
||||
}
|
||||
rc = (sb.st_mode & S_IFDIR) ? true : false;
|
||||
free_str:
|
||||
free(path);
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* util_path_example - Example program for util_path
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
//! [code]
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_prg.h"
|
||||
|
||||
/*
|
||||
* Define program description
|
||||
*/
|
||||
const struct util_prg prg = {
|
||||
.desc = "Sample for util_dirlibrary",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2001,
|
||||
.pub_last = 2016,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Test util_path_sysfs()
|
||||
*/
|
||||
static void test_util_path_sysfs(void)
|
||||
{
|
||||
const char * const subsys_vec[] = {"cpu", "memory"};
|
||||
char *path;
|
||||
int i;
|
||||
|
||||
/* Construct sysfs paths for the subsystems */
|
||||
for (i = 0; i < 2; i++) {
|
||||
path = util_path_sysfs("devices/system/%s", subsys_vec[i]);
|
||||
printf("Path for %6s: \"%s\"\n", subsys_vec[i], path);
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test path
|
||||
*/
|
||||
static void test_path(const char *path)
|
||||
{
|
||||
printf("%-20s: ", path);
|
||||
if (util_path_is_readable(path))
|
||||
printf("read=yes, ");
|
||||
else
|
||||
printf("read=no , ");
|
||||
if (util_path_is_writable(path))
|
||||
printf("write=yes, ");
|
||||
else
|
||||
printf("write=no , ");
|
||||
if (util_path_is_reg_file(path))
|
||||
printf("reg_file=yes");
|
||||
else
|
||||
printf("reg_file=no ");
|
||||
if (util_path_is_dir(path))
|
||||
printf("dir=yes");
|
||||
else
|
||||
printf("dir=no ");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test util_path_is_xxx()
|
||||
*/
|
||||
static void test_util_path_is_xxx(void)
|
||||
{
|
||||
test_path("util_path_example.c");
|
||||
test_path("/tmp");
|
||||
test_path("i_do_not_exist");
|
||||
}
|
||||
|
||||
/*
|
||||
* Usage: util_path_example [sysfs mount point] | "is_xxx"
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
util_prg_init(&prg);
|
||||
|
||||
if (argc < 2)
|
||||
goto out_fail;
|
||||
if (strcmp(argv[1], "sysfs") == 0) {
|
||||
if (argc == 3) {
|
||||
/* Change sysfs path via SYSFS_ROOT env variable */
|
||||
setenv("SYSFS_ROOT", argv[2], 1);
|
||||
}
|
||||
test_util_path_sysfs();
|
||||
} else if (strcmp(argv[1], "is_xxx") == 0) {
|
||||
test_util_path_is_xxx();
|
||||
} else {
|
||||
goto out_fail;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
out_fail:
|
||||
errx(EXIT_FAILURE, "Usage: %s sysfs <path> | is_xxx", argv[0]);
|
||||
}
|
||||
//! [code]
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Print standard program messages
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
/*
|
||||
* Private data
|
||||
*/
|
||||
static struct util_prg_l {
|
||||
const struct util_prg *prg;
|
||||
/* Command used for parsing */
|
||||
const char *command;
|
||||
} l;
|
||||
|
||||
struct util_prg_l *util_prg_l = &l;
|
||||
|
||||
/**
|
||||
* Set the current command for command line option processing
|
||||
*
|
||||
* @param[in] command The current command or NULL for no command
|
||||
*/
|
||||
void util_prg_set_command(const char *command)
|
||||
{
|
||||
l.command = command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print program usage information for the --help option
|
||||
*/
|
||||
void util_prg_print_help(void)
|
||||
{
|
||||
/* Print usage */
|
||||
printf("Usage: %s", program_invocation_short_name);
|
||||
if (l.prg->command_args)
|
||||
printf(" %s", l.prg->command_args);
|
||||
printf(" [OPTIONS]");
|
||||
if (l.prg->args)
|
||||
printf(" %s", l.prg->args);
|
||||
/* Print usage description */
|
||||
printf("\n\n");
|
||||
util_print_indented(l.prg->desc, 0);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print program version information for the --version option
|
||||
*/
|
||||
void util_prg_print_version(void)
|
||||
{
|
||||
const struct util_prg_copyright *copyright;
|
||||
|
||||
printf("%s version %s\n", program_invocation_short_name,
|
||||
RELEASE_STRING);
|
||||
copyright = l.prg->copyright_vec;
|
||||
while (copyright->owner) {
|
||||
if (copyright->pub_first == copyright->pub_last)
|
||||
printf("Copyright %s %d\n", copyright->owner,
|
||||
copyright->pub_first);
|
||||
else
|
||||
printf("Copyright %s %d, %d\n", copyright->owner,
|
||||
copyright->pub_first, copyright->pub_last);
|
||||
copyright++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ask user to use the --help option
|
||||
*/
|
||||
void util_prg_print_parse_error(void)
|
||||
{
|
||||
if (l.command)
|
||||
fprintf(stderr, "Try '%s %s --help' for more information.\n",
|
||||
program_invocation_short_name, l.command);
|
||||
else
|
||||
fprintf(stderr, "Try '%s --help' for more information.\n",
|
||||
program_invocation_short_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* An option has been specified that is not supported
|
||||
*
|
||||
* @param[in] option Option string (short or long)
|
||||
*/
|
||||
void util_prg_print_invalid_option(const char *opt_name)
|
||||
{
|
||||
fprintf(stderr, "%s: Invalid option '%s'\n",
|
||||
program_invocation_short_name, opt_name);
|
||||
util_prg_print_parse_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* A required argument for an option is missing
|
||||
*
|
||||
* @param[in] option Option string
|
||||
*/
|
||||
void util_prg_print_required_arg(const char *opt_name)
|
||||
{
|
||||
fprintf(stderr, "%s: Option '%s' requires an argument\n",
|
||||
program_invocation_short_name, opt_name);
|
||||
util_prg_print_parse_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* A superfluous invalid positional argument has been specified
|
||||
*
|
||||
* @param[in] arg_name Name of the invalid argument
|
||||
*/
|
||||
void util_prg_print_arg_error(const char *arg_name)
|
||||
{
|
||||
fprintf(stderr, "%s: Invalid argument '%s'\n",
|
||||
program_invocation_short_name, arg_name);
|
||||
util_prg_print_parse_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the program module
|
||||
*
|
||||
* @param[in] prg Program description
|
||||
*/
|
||||
void util_prg_init(const struct util_prg *prg)
|
||||
{
|
||||
l.prg = prg;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* util_prg_example - Example program for util_prg
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
//! [code]
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/util_prg.h"
|
||||
|
||||
/*
|
||||
* Program description
|
||||
*/
|
||||
const struct util_prg prg = {
|
||||
.desc = "Sample for util_prg library.",
|
||||
.args = "[POS_ARGS]",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2001,
|
||||
.pub_last = 2016,
|
||||
},
|
||||
{
|
||||
.owner = "Another Corp.",
|
||||
.pub_first = 2016,
|
||||
.pub_last = 2016,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Demonstrate the util_prg_print() functions
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *file_name = "i_do_not_exist";
|
||||
char optopt_str[3];
|
||||
FILE *fp;
|
||||
int opt;
|
||||
|
||||
/* Disable the getopt_long() error messages */
|
||||
opterr = 0;
|
||||
|
||||
util_prg_init(&prg);
|
||||
|
||||
while ((opt = getopt(argc, argv, "vhe")) != -1) {
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
return EXIT_SUCCESS;
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
printf(" -e Try to open non-exisiting file\n");
|
||||
printf(" -h Print this help, then exit\n");
|
||||
printf(" -v Print version information, then exit\n");
|
||||
return EXIT_SUCCESS;
|
||||
case 'e':
|
||||
fp = fopen(file_name, "r");
|
||||
if (!fp) {
|
||||
err(EXIT_FAILURE, "Open of '%s' failed",
|
||||
file_name);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
case ':':
|
||||
/* Option requires an argument */
|
||||
util_prg_print_required_arg(argv[optind - 1]);
|
||||
return EXIT_FAILURE;
|
||||
case '?':
|
||||
sprintf(optopt_str, "-%c", optopt);
|
||||
util_prg_print_invalid_option(optopt_str);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (argc > 2) {
|
||||
util_prg_print_arg_error(argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (argc == 2) {
|
||||
printf("Positional parameter specified: %s\n", argv[1]);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
errx(EXIT_FAILURE, "Specify either -h, -v, -e, or one positional "
|
||||
"parameter");
|
||||
}
|
||||
//! [code]
|
||||
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* s390-tools/zipl/src/proc.c
|
||||
* Scanner for the /proc/ files
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_proc.h"
|
||||
|
||||
static const char util_proc_part_filename[] = "/proc/partitions";
|
||||
static const char util_proc_dev_filename[] = "/proc/devices";
|
||||
|
||||
struct file_buffer {
|
||||
char *buffer;
|
||||
off_t pos;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
#define INITIAL_FILE_BUFFER_SIZE 1024
|
||||
|
||||
/* Read file into buffer without querying its size (necessary for reading files
|
||||
* from /proc). Upon success, return zero and set BUFFER to point to
|
||||
* the file buffer and SIZE (if non-null) to contain the file size. Return
|
||||
* non-zero otherwise. Add a null-byte at the end of the buffer if
|
||||
* NIL_TERMINATE is non-zero. */
|
||||
static int
|
||||
util_proc_read_special_file(const char *filename, char **buffer, size_t *size,
|
||||
int nil_terminate)
|
||||
{
|
||||
FILE *file;
|
||||
char *data;
|
||||
char *new_data;
|
||||
size_t count;
|
||||
size_t current_size;
|
||||
int current;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (file == NULL) {
|
||||
printf("Could not open %s\n",
|
||||
filename);
|
||||
return -1;
|
||||
}
|
||||
current_size = INITIAL_FILE_BUFFER_SIZE;
|
||||
count = 0;
|
||||
data = (char *) malloc(current_size);
|
||||
if (data == NULL) {
|
||||
printf("Could not allocate %zu bytes of memory", current_size);
|
||||
fclose(file);
|
||||
return -1;
|
||||
}
|
||||
current = fgetc(file);
|
||||
while (current != EOF || nil_terminate) {
|
||||
if (current == EOF) {
|
||||
current = 0;
|
||||
nil_terminate = 0;
|
||||
}
|
||||
data[count++] = (char) current;
|
||||
if (count >= current_size) {
|
||||
new_data = (char *) malloc(current_size * 2);
|
||||
if (new_data == NULL) {
|
||||
printf("Could not allocate %zu bytes of memory",
|
||||
current_size * 2);
|
||||
free(data);
|
||||
fclose(file);
|
||||
return -1;
|
||||
}
|
||||
memcpy(new_data, data, current_size);
|
||||
free(data);
|
||||
data = new_data;
|
||||
current_size *= 2;
|
||||
}
|
||||
current = fgetc(file);
|
||||
}
|
||||
fclose(file);
|
||||
*buffer = data;
|
||||
if (size)
|
||||
*size = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the contents of a file and fill in the respective fields of
|
||||
* FILE. Return 0 on success, non-zero otherwise. */
|
||||
static int
|
||||
get_file_buffer(struct file_buffer *file, const char *filename)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = util_proc_read_special_file(filename, &file->buffer,
|
||||
&file->length, 0);
|
||||
file->pos = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Free resources allocated for file buffer identified by
|
||||
* FILE. */
|
||||
static void
|
||||
free_file_buffer(struct file_buffer *file)
|
||||
{
|
||||
if (file->buffer != NULL) {
|
||||
free(file->buffer);
|
||||
file->buffer = NULL;
|
||||
file->pos = 0;
|
||||
file->length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return character at current FILE buffer position or EOF if at end of
|
||||
* file. */
|
||||
static int
|
||||
current_char(struct file_buffer *file)
|
||||
{
|
||||
if (file->buffer != NULL)
|
||||
if (file->pos < (off_t) file->length)
|
||||
return file->buffer[file->pos];
|
||||
return EOF;
|
||||
}
|
||||
|
||||
|
||||
/* Advance the current file pointer of file buffer FILE until the current
|
||||
* character is no longer a whitespace or until the end of line or file is
|
||||
* reached. Return 0 if at least one whitespace character was encountered,
|
||||
* non-zero otherwise. */
|
||||
static int
|
||||
skip_whitespaces(struct file_buffer *file)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = -1;
|
||||
while ((current_char(file) != '\n') && isspace(current_char(file))) {
|
||||
rc = 0;
|
||||
file->pos++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Scan a positive integer number at the current position of file buffer FILE
|
||||
* and advance the position respectively. Upon success, return zero and set
|
||||
* NUMBER to contain the scanned number. Return non-zero otherwise. */
|
||||
static int
|
||||
scan_number(struct file_buffer *file, size_t *number)
|
||||
{
|
||||
int rc;
|
||||
size_t old_number;
|
||||
|
||||
*number = 0;
|
||||
rc = -1;
|
||||
while (isdigit(current_char(file))) {
|
||||
rc = 0;
|
||||
old_number = *number;
|
||||
*number = *number * 10 + current_char(file) - '0';
|
||||
/* Check for overflow */
|
||||
if (old_number > *number) {
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
file->pos++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Scan a device node name at the current position of file buffer FILE and
|
||||
* advance the position respectively. Upon success, return zero and set
|
||||
* NAME to contain a copy of the scanned name. Return non-zero otherwise. */
|
||||
static int
|
||||
scan_name(struct file_buffer *file, char **name)
|
||||
{
|
||||
off_t start_pos;
|
||||
|
||||
start_pos = file->pos;
|
||||
while (!isspace(current_char(file)) &&
|
||||
(current_char(file) != EOF))
|
||||
file->pos++;
|
||||
if (file->pos > start_pos) {
|
||||
*name = (char *) malloc(file->pos - start_pos + 1);
|
||||
if (*name == NULL)
|
||||
return -1;
|
||||
memcpy((void *) *name, (void *) &file->buffer[start_pos],
|
||||
file->pos - start_pos);
|
||||
(*name)[file->pos - start_pos] = 0;
|
||||
return 0;
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Scan for the specified STRING at the current position of file buffer FILE
|
||||
* and advance the position respectively. Upon success, return zero. Return
|
||||
* non-zero otherwise. */
|
||||
static int
|
||||
scan_string(struct file_buffer *file, const char *string)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
for (i = 0; string[i] && (current_char(file) == string[i]);
|
||||
i++, file->pos++)
|
||||
;
|
||||
if (string[i] == '\0')
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Advance the current file position to beginning of next line in file buffer
|
||||
* FILE or to end of file. */
|
||||
static void
|
||||
skip_line(struct file_buffer *file)
|
||||
{
|
||||
while ((current_char(file) != '\n') && (current_char(file) != EOF))
|
||||
file->pos++;
|
||||
if (current_char(file) == '\n')
|
||||
file->pos++;
|
||||
}
|
||||
|
||||
|
||||
/* Return non-zero if the current file position of file buffer FILE is at the
|
||||
* end of file. Return zero otherwise. */
|
||||
static int
|
||||
eof(struct file_buffer *file)
|
||||
{
|
||||
return file->pos >= (off_t) file->length;
|
||||
}
|
||||
|
||||
|
||||
/* Scan a line of the specified /proc/partitions FILE buffer and advance the
|
||||
* current file position pointer respectively. If the current line matches
|
||||
* the correct pattern, fill in the corresponding data into ENTRY and return 0.
|
||||
* Return non-zero otherwise. */
|
||||
static int
|
||||
scan_part_entry(struct file_buffer *file, struct util_proc_part_entry *entry)
|
||||
{
|
||||
int rc;
|
||||
size_t dev_major;
|
||||
size_t dev_minor;
|
||||
size_t blockcount;
|
||||
char *name;
|
||||
|
||||
/* Scan for: (\s*)(\d+)(\s+)(\d+)(\s+)(\d+)(\s+)(\S+)(\.*)$ */
|
||||
skip_whitespaces(file);
|
||||
rc = scan_number(file, &dev_major);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = skip_whitespaces(file);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = scan_number(file, &dev_minor);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = skip_whitespaces(file);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = scan_number(file, &blockcount);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = skip_whitespaces(file);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = scan_name(file, &name);
|
||||
if (rc)
|
||||
return rc;
|
||||
skip_line(file);
|
||||
entry->device = makedev(dev_major, dev_minor);
|
||||
entry->blockcount = blockcount;
|
||||
entry->name = name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Release resources associated with ENTRY. */
|
||||
void
|
||||
util_proc_part_free_entry(struct util_proc_part_entry *entry)
|
||||
{
|
||||
if (entry->name != NULL) {
|
||||
free(entry->name);
|
||||
entry->name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Scan a line of the specified /proc/devices FILE buffer and advance the
|
||||
* current file position pointer respectively. If the current line matches
|
||||
* the correct pattern, fill in the corresponding data into ENTRY and return 0.
|
||||
* Return non-zero otherwise. */
|
||||
static int
|
||||
scan_dev_entry(struct file_buffer *file, struct util_proc_dev_entry *entry,
|
||||
int blockdev)
|
||||
{
|
||||
int rc;
|
||||
size_t dev_major;
|
||||
char *name;
|
||||
|
||||
/* Scan for: (\s*)(\d+)(\s+)(\S+)(\.*)$ */
|
||||
skip_whitespaces(file);
|
||||
rc = scan_number(file, &dev_major);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = skip_whitespaces(file);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = scan_name(file, &name);
|
||||
if (rc)
|
||||
return rc;
|
||||
skip_line(file);
|
||||
entry->device = makedev(dev_major, 0);
|
||||
entry->name = name;
|
||||
entry->blockdev = blockdev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Release resources associated with ENTRY. */
|
||||
void
|
||||
util_proc_dev_free_entry(struct util_proc_dev_entry *entry)
|
||||
{
|
||||
if (entry->name != NULL) {
|
||||
free(entry->name);
|
||||
entry->name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse one record. */
|
||||
static int
|
||||
scan_mnt_entry(struct file_buffer *file, struct util_proc_mnt_entry *entry)
|
||||
{
|
||||
int rc;
|
||||
|
||||
skip_whitespaces(file);
|
||||
rc = scan_name(file, &entry->spec);
|
||||
if (rc)
|
||||
return rc;
|
||||
skip_whitespaces(file);
|
||||
rc = scan_name(file, &entry->file);
|
||||
if (rc)
|
||||
return rc;
|
||||
skip_whitespaces(file);
|
||||
rc = scan_name(file, &entry->vfstype);
|
||||
if (rc)
|
||||
return rc;
|
||||
skip_whitespaces(file);
|
||||
rc = scan_name(file, &entry->mntOpts);
|
||||
if (rc)
|
||||
return rc;
|
||||
skip_whitespaces(file);
|
||||
rc = scan_name(file, &entry->dump);
|
||||
if (rc)
|
||||
return rc;
|
||||
skip_whitespaces(file);
|
||||
rc = scan_name(file, &entry->passno);
|
||||
if (rc)
|
||||
return rc;
|
||||
skip_line(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free the memory allocated for one record. */
|
||||
void
|
||||
util_proc_mnt_free_entry(struct util_proc_mnt_entry *entry)
|
||||
{
|
||||
free(entry->spec);
|
||||
free(entry->file);
|
||||
free(entry->vfstype);
|
||||
free(entry->mntOpts);
|
||||
free(entry->dump);
|
||||
free(entry->passno);
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
}
|
||||
|
||||
/* Scan /proc/partitions for an entry matching DEVICE. When there is a match,
|
||||
* store entry data in ENTRY and return 0. Return non-zero otherwise. */
|
||||
int
|
||||
util_proc_part_get_entry(dev_t device, struct util_proc_part_entry *entry)
|
||||
{
|
||||
struct file_buffer file;
|
||||
int rc;
|
||||
|
||||
rc = get_file_buffer(&file, util_proc_part_filename);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = -1;
|
||||
while (!eof(&file)) {
|
||||
if (scan_part_entry(&file, entry) == 0) {
|
||||
if (entry->device == device) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
util_proc_part_free_entry(entry);
|
||||
} else
|
||||
skip_line(&file);
|
||||
}
|
||||
free_file_buffer(&file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Scan /proc/devices for a blockdevice (BLOCKDEV is 1) or a character
|
||||
* device (BLOCKDEV is 0) with a major number matching the major number of DEV.
|
||||
* When there is a match, store entry data in ENTRY and return 0. Return
|
||||
* non-zero otherwise. */
|
||||
int
|
||||
util_proc_dev_get_entry(dev_t device, int blockdev,
|
||||
struct util_proc_dev_entry *entry)
|
||||
{
|
||||
struct file_buffer file;
|
||||
int rc;
|
||||
int scan_blockdev = 0;
|
||||
|
||||
rc = get_file_buffer(&file, util_proc_dev_filename);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = -1;
|
||||
while (!eof(&file)) {
|
||||
if (scan_string(&file, "Block") == 0) {
|
||||
skip_line(&file);
|
||||
scan_blockdev = 1;
|
||||
continue;
|
||||
} else if (scan_dev_entry(&file, entry, scan_blockdev) == 0) {
|
||||
if ((major(entry->device) == major(device)) &&
|
||||
blockdev == scan_blockdev) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
util_proc_dev_free_entry(entry);
|
||||
} else
|
||||
skip_line(&file);
|
||||
}
|
||||
free_file_buffer(&file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Provide one record form a /proc/mounts like file
|
||||
*
|
||||
* The parameter file_name distinguishes the file form procfs which
|
||||
* is read, the parameter spec is the selector for the record.
|
||||
*/
|
||||
int util_proc_mnt_get_entry(const char *file_name, const char *spec,
|
||||
struct util_proc_mnt_entry *entry)
|
||||
{
|
||||
struct file_buffer file;
|
||||
int rc;
|
||||
|
||||
rc = get_file_buffer(&file, file_name);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = -1;
|
||||
while (!eof(&file)) {
|
||||
rc = scan_mnt_entry(&file, entry);
|
||||
if (rc)
|
||||
goto out_free;
|
||||
if (!strcmp(entry->spec, spec)) {
|
||||
rc = 0;
|
||||
goto out_free;
|
||||
}
|
||||
util_proc_mnt_free_entry(entry);
|
||||
}
|
||||
out_free:
|
||||
free_file_buffer(&file);
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,539 @@
|
||||
/**
|
||||
* util - Utility function library
|
||||
*
|
||||
* Print records in different output formats
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <argz.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_list.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_rec.h"
|
||||
|
||||
/*
|
||||
* Field structure containing the string value and secondary information
|
||||
*/
|
||||
struct util_rec_fld {
|
||||
char *key; /* Name of the filed */
|
||||
char *hdr; /* Content of the header */
|
||||
size_t len; /* Length of string argz array */
|
||||
char *val; /* The value of the field */
|
||||
enum util_rec_align align; /* Alignment of the field */
|
||||
int width; /* Field width */
|
||||
struct util_list_node node; /* Pointers to previous and next field */
|
||||
};
|
||||
|
||||
/*
|
||||
* Print formats
|
||||
*/
|
||||
struct rec_fmt {
|
||||
enum {
|
||||
REC_FMT_WIDE,
|
||||
REC_FMT_LONG,
|
||||
REC_FMT_CSV,
|
||||
} type;
|
||||
union {
|
||||
struct wide_p {
|
||||
char *hdr_sep;
|
||||
char *col_sep;
|
||||
int argz_sep;
|
||||
} wide_p;
|
||||
struct long_p {
|
||||
char *hdr_sep;
|
||||
char *col_sep;
|
||||
int argz_sep;
|
||||
char *key;
|
||||
int key_size;
|
||||
int val_size;
|
||||
} long_p;
|
||||
struct csv_p {
|
||||
char *col_sep;
|
||||
int argz_sep;
|
||||
} csv_p;
|
||||
} d;
|
||||
};
|
||||
|
||||
/*
|
||||
* Record structure (internal representation)
|
||||
*/
|
||||
/// @cond
|
||||
struct util_rec {
|
||||
struct util_list *list; /* List of the fields */
|
||||
struct rec_fmt fmt; /* Output format */
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
struct util_list *__util_rec_get_list(struct util_rec *rec)
|
||||
{
|
||||
return rec->list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the field according to a distinct key
|
||||
*/
|
||||
static struct util_rec_fld *rec_get_fld(struct util_rec *rec, const char *key)
|
||||
{
|
||||
struct util_rec_fld *fld;
|
||||
|
||||
util_list_iterate(rec->list, fld) {
|
||||
if (!strcmp(fld->key, key))
|
||||
return fld;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key name of a field
|
||||
*
|
||||
* @param[in] fld Field for query
|
||||
*
|
||||
* @returns Pointer to key string
|
||||
*/
|
||||
const char *util_rec_fld_get_key(struct util_rec_fld *fld)
|
||||
{
|
||||
return fld->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record with "wide" output format
|
||||
*
|
||||
* @param[in] hdr_sep Header separator
|
||||
*
|
||||
* @returns Pointer to the created record
|
||||
*/
|
||||
struct util_rec *util_rec_new_wide(const char *hdr_sep)
|
||||
{
|
||||
struct util_rec *rec = util_malloc(sizeof(struct util_rec));
|
||||
|
||||
rec->list = util_list_new(struct util_rec_fld, node);
|
||||
rec->fmt.type = REC_FMT_WIDE;
|
||||
rec->fmt.d.wide_p.hdr_sep = util_strdup(hdr_sep);
|
||||
rec->fmt.d.wide_p.argz_sep = ',';
|
||||
return rec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print record header in "wide" output format
|
||||
*/
|
||||
static void rec_print_wide_hdr(struct util_rec *rec)
|
||||
{
|
||||
const char *hdr_sep = rec->fmt.d.wide_p.hdr_sep;
|
||||
int col_nr = 0, size = 0, field_count = 0;
|
||||
struct util_rec_fld *fld;
|
||||
char *buf;
|
||||
|
||||
util_list_iterate(rec->list, fld) {
|
||||
if (col_nr)
|
||||
printf(" ");
|
||||
if (fld->hdr) {
|
||||
if (fld->align == UTIL_REC_ALIGN_LEFT)
|
||||
printf("%-*s", fld->width, fld->hdr);
|
||||
else
|
||||
printf("%*s", fld->width, fld->hdr);
|
||||
size += fld->width;
|
||||
field_count++;
|
||||
}
|
||||
col_nr++;
|
||||
}
|
||||
printf("\n");
|
||||
if (!hdr_sep)
|
||||
return;
|
||||
size += field_count - 1;
|
||||
if (hdr_sep) {
|
||||
buf = util_malloc(size + 1);
|
||||
memset(buf, (int)hdr_sep[0], size);
|
||||
buf[size] = 0;
|
||||
printf("%s\n", buf);
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print record field values in "wide" output format
|
||||
*/
|
||||
void rec_print_wide(struct util_rec *rec)
|
||||
{
|
||||
const char argz_sep = rec->fmt.d.wide_p.argz_sep;
|
||||
struct util_rec_fld *fld;
|
||||
char argz_str[PAGE_SIZE];
|
||||
int fld_count = 0;
|
||||
char *entry;
|
||||
|
||||
util_list_iterate(rec->list, fld) {
|
||||
if (!fld->hdr)
|
||||
continue;
|
||||
if (fld_count)
|
||||
printf(" ");
|
||||
entry = fld->val;
|
||||
if (argz_count(fld->val, fld->len) > 1) {
|
||||
strcpy(argz_str, entry);
|
||||
while ((entry = argz_next(fld->val, fld->len, entry)))
|
||||
strcat(strncat(argz_str, &argz_sep, 1), entry);
|
||||
entry = argz_str;
|
||||
}
|
||||
if (fld->align == UTIL_REC_ALIGN_LEFT)
|
||||
printf("%-*s", fld->width, entry);
|
||||
else
|
||||
printf("%*s", fld->width, entry);
|
||||
fld_count++;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Free private memory of record
|
||||
*/
|
||||
static void rec_free_wide(struct util_rec *rec)
|
||||
{
|
||||
free(rec->fmt.d.wide_p.hdr_sep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record with "long" output format
|
||||
*
|
||||
* @param[in] hdr_sep Header separator
|
||||
* @param[in] col_sep Column separator
|
||||
* @param[in] key Primary key of record
|
||||
* @param[in] key_size Width of left column i.e. keys
|
||||
* @param[in] val_size Width of right column i.e. values
|
||||
*
|
||||
* @returns Pointer to the created record
|
||||
*/
|
||||
struct util_rec *util_rec_new_long(const char *hdr_sep, const char *col_sep,
|
||||
const char *key, int key_size, int val_size)
|
||||
{
|
||||
struct util_rec *rec = util_malloc(sizeof(struct util_rec));
|
||||
|
||||
rec->list = util_list_new(struct util_rec_fld, node);
|
||||
rec->fmt.type = REC_FMT_LONG;
|
||||
rec->fmt.d.long_p.hdr_sep = util_strdup(hdr_sep);
|
||||
rec->fmt.d.long_p.col_sep = util_strdup(col_sep);
|
||||
rec->fmt.d.long_p.key = util_strdup(key);
|
||||
rec->fmt.d.long_p.key_size = key_size;
|
||||
rec->fmt.d.long_p.val_size = val_size;
|
||||
rec->fmt.d.long_p.argz_sep = ' ';
|
||||
return rec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print field header in "long" output format
|
||||
*/
|
||||
static void rec_print_long_hdr(struct util_rec *rec)
|
||||
{
|
||||
struct long_p *p = &rec->fmt.d.long_p;
|
||||
struct util_rec_fld *fld;
|
||||
int len = 0;
|
||||
char *buf;
|
||||
|
||||
fld = rec_get_fld(rec, p->key);
|
||||
util_assert(fld != NULL, "Record not found\n");
|
||||
util_assert(fld->hdr != NULL, "Header for field not found\n");
|
||||
if (p->col_sep) {
|
||||
printf("%-*s %s %-*s\n", p->key_size, fld->hdr,
|
||||
p->col_sep, fld->width, fld->val);
|
||||
len = p->key_size + p->val_size + 3;
|
||||
} else {
|
||||
printf("%-*s %-*s\n", p->key_size, fld->hdr, fld->width,
|
||||
fld->val);
|
||||
len = p->key_size + p->val_size + 1;
|
||||
}
|
||||
if (!p->hdr_sep)
|
||||
return;
|
||||
buf = util_malloc(len + 1);
|
||||
memset(buf, p->hdr_sep[0], len);
|
||||
buf[len] = 0;
|
||||
printf("%s\n", buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print record field values in "long" output format
|
||||
*/
|
||||
static void rec_print_long(struct util_rec *rec)
|
||||
{
|
||||
struct long_p *p = &rec->fmt.d.long_p;
|
||||
struct util_rec_fld *fld;
|
||||
char *item = NULL;
|
||||
|
||||
rec_print_long_hdr(rec);
|
||||
|
||||
util_list_iterate(rec->list, fld) {
|
||||
if (!fld->hdr)
|
||||
continue;
|
||||
if (!strcmp(p->key, fld->key))
|
||||
continue;
|
||||
if (!fld->val)
|
||||
continue;
|
||||
item = argz_next(fld->val, fld->len, item);
|
||||
if (p->col_sep) {
|
||||
printf(" %-*s %s %s\n",
|
||||
p->key_size - 8, fld->hdr, p->col_sep, item);
|
||||
while ((item = argz_next(fld->val, fld->len, item)))
|
||||
printf(" %-*s %c %s\n",
|
||||
p->key_size - 8, "", p->argz_sep, item);
|
||||
} else {
|
||||
printf(" %-*s %s\n",
|
||||
p->key_size - 8, fld->hdr, fld->val);
|
||||
while ((item = argz_next(fld->val, fld->len, item)))
|
||||
printf(" %-*s %s\n",
|
||||
p->key_size - 8, "", item);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Free private memory of record
|
||||
*/
|
||||
static void rec_free_long(struct util_rec *rec)
|
||||
{
|
||||
free(rec->fmt.d.long_p.hdr_sep);
|
||||
free(rec->fmt.d.long_p.col_sep);
|
||||
free(rec->fmt.d.long_p.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record with "csv" output format
|
||||
*
|
||||
* @param[in] col_sep Column separator
|
||||
*
|
||||
* @returns Pointer to the created record
|
||||
*/
|
||||
struct util_rec *util_rec_new_csv(const char *col_sep)
|
||||
{
|
||||
struct util_rec *rec = util_malloc(sizeof(struct util_rec));
|
||||
|
||||
rec->list = util_list_new(struct util_rec_fld, node);
|
||||
rec->fmt.type = REC_FMT_CSV;
|
||||
rec->fmt.d.csv_p.col_sep = util_strdup(col_sep);
|
||||
rec->fmt.d.csv_p.argz_sep = ' ';
|
||||
return rec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print record header in "csv" output format
|
||||
*/
|
||||
void rec_print_csv_hdr(struct util_rec *rec)
|
||||
{
|
||||
const char *col_sep = rec->fmt.d.csv_p.col_sep;
|
||||
struct util_rec_fld *fld;
|
||||
int fld_count = 0;
|
||||
|
||||
util_list_iterate(rec->list, fld) {
|
||||
if (fld_count)
|
||||
printf("%c", *col_sep);
|
||||
if (fld->hdr) {
|
||||
printf("%s", fld->hdr);
|
||||
fld_count++;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Print record field values in "csv" output format
|
||||
*/
|
||||
void rec_print_csv(struct util_rec *rec)
|
||||
{
|
||||
const char argz_sep = rec->fmt.d.csv_p.argz_sep;
|
||||
const char *col_sep = rec->fmt.d.csv_p.col_sep;
|
||||
struct util_rec_fld *fld;
|
||||
int fld_count = 0;
|
||||
char *item = NULL;
|
||||
|
||||
util_list_iterate(rec->list, fld) {
|
||||
item = argz_next(fld->val, fld->len, item);
|
||||
if (fld_count)
|
||||
printf("%c", *col_sep);
|
||||
if (fld->hdr) {
|
||||
printf("%s", item);
|
||||
while ((item = argz_next(fld->val, fld->len, item)))
|
||||
printf("%c%s", argz_sep, item);
|
||||
fld_count++;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Free private memory of record
|
||||
*/
|
||||
static void rec_free_csv(struct util_rec *rec)
|
||||
{
|
||||
free(rec->fmt.d.csv_p.col_sep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a new field for the record
|
||||
*
|
||||
* @param[in] rec Pointer of the record
|
||||
* @param[in] key Key of the filed is to be created. It should be unique
|
||||
* within the record.
|
||||
* @param[in] align Alignment of field
|
||||
* @param[in] width Width of field
|
||||
* @param[in] hdr This information is printed in record headers. If it is
|
||||
* NULL, the field is prohibited form printing completely.
|
||||
*/
|
||||
void util_rec_def(struct util_rec *rec, const char *key,
|
||||
enum util_rec_align align, int width, const char *hdr)
|
||||
{
|
||||
struct util_rec_fld *fld = util_malloc(sizeof(struct util_rec_fld));
|
||||
|
||||
fld->key = util_strdup(key);
|
||||
fld->hdr = util_strdup(hdr);
|
||||
fld->val = NULL;
|
||||
fld->align = align;
|
||||
fld->width = width;
|
||||
util_list_add_tail(rec->list, fld);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free record and associated fields
|
||||
*
|
||||
* @param[in] rec Record pointer
|
||||
*/
|
||||
void util_rec_free(struct util_rec *rec)
|
||||
{
|
||||
struct util_rec_fld *fld, *tmp;
|
||||
|
||||
util_list_iterate_safe(rec->list, fld, tmp) {
|
||||
util_list_remove(rec->list, fld);
|
||||
free(fld->key);
|
||||
free(fld->hdr);
|
||||
free(fld->val);
|
||||
free(fld);
|
||||
}
|
||||
util_list_free(rec->list);
|
||||
switch (rec->fmt.type) {
|
||||
case REC_FMT_WIDE:
|
||||
rec_free_wide(rec);
|
||||
break;
|
||||
case REC_FMT_LONG:
|
||||
rec_free_long(rec);
|
||||
break;
|
||||
case REC_FMT_CSV:
|
||||
rec_free_csv(rec);
|
||||
break;
|
||||
}
|
||||
free(rec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print record field values according to output format
|
||||
*
|
||||
* @param[in] rec Record pointer
|
||||
*/
|
||||
void util_rec_print(struct util_rec *rec)
|
||||
{
|
||||
switch (rec->fmt.type) {
|
||||
case REC_FMT_WIDE:
|
||||
rec_print_wide(rec);
|
||||
break;
|
||||
case REC_FMT_LONG:
|
||||
rec_print_long(rec);
|
||||
break;
|
||||
case REC_FMT_CSV:
|
||||
rec_print_csv(rec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print record header according to output format
|
||||
*
|
||||
* @param[in] rec Record pointer
|
||||
*/
|
||||
void util_rec_print_hdr(struct util_rec *rec)
|
||||
{
|
||||
switch (rec->fmt.type) {
|
||||
case REC_FMT_WIDE:
|
||||
rec_print_wide_hdr(rec);
|
||||
break;
|
||||
case REC_FMT_LONG:
|
||||
break;
|
||||
case REC_FMT_CSV:
|
||||
rec_print_csv_hdr(rec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a field value to an argz vector
|
||||
*
|
||||
* @param[in] rec Record pointer
|
||||
* @param[in] key Key of the desired field
|
||||
* @param[in] argz Pointer to the series of strings
|
||||
* @param[in] len Length of the argz buffer
|
||||
*/
|
||||
void util_rec_set_argz(struct util_rec *rec, const char *key, const char *argz,
|
||||
size_t len)
|
||||
{
|
||||
struct util_rec_fld *fld;
|
||||
char *val;
|
||||
|
||||
fld = rec_get_fld(rec, key);
|
||||
if (!fld)
|
||||
return;
|
||||
val = util_malloc(len);
|
||||
val = memcpy(val, argz, len);
|
||||
free(fld->val);
|
||||
fld->val = val;
|
||||
fld->len = len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a field value to a formatted string
|
||||
*
|
||||
* @param[in] rec Record pointer
|
||||
* @param[in] key Key of the desired field
|
||||
* @param[in] fmt Format string for generation of value string
|
||||
* @param[in] ... Parameters for format string
|
||||
*
|
||||
* @returns Pointer to the field which was modified or NULL in the case of
|
||||
* any error.
|
||||
*/
|
||||
void util_rec_set(struct util_rec *rec, const char *key, const char *fmt, ...)
|
||||
{
|
||||
struct util_rec_fld *fld;
|
||||
va_list ap;
|
||||
char *str;
|
||||
|
||||
util_assert(fmt != NULL, "Parameter 'fmt' pointer must not be NULL\n");
|
||||
fld = rec_get_fld(rec, key);
|
||||
if (!fld)
|
||||
return;
|
||||
UTIL_VASPRINTF(&str, fmt, ap);
|
||||
free(fld->val);
|
||||
fld->val = str;
|
||||
fld->len = strlen(str) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the string value of a desired field. If the field value stored in argz
|
||||
* format, pointer to the first argz element is returned.
|
||||
*
|
||||
* @param[in] rec Record pointer
|
||||
* @param[in] key Key of the field
|
||||
*
|
||||
* @returns If the desired field was found, the pointer to its value.
|
||||
* NULL in the case of any error or if the field is empty.
|
||||
*/
|
||||
const char *util_rec_get(struct util_rec *rec, const char *key)
|
||||
{
|
||||
struct util_rec_fld *fld = rec_get_fld(rec, key);
|
||||
|
||||
return (fld != NULL) ? fld->val : NULL;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* util_rec_example - Example program for util_rec
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
//! [code]
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_rec.h"
|
||||
|
||||
/*
|
||||
* Print three records in specified format
|
||||
*/
|
||||
static void print_records(const char *format, struct util_rec *rec)
|
||||
{
|
||||
static const char * const size_vec[] = {"small", "medium", "large"};
|
||||
static const char * const name_vec[] = {"zero", "one", "two"};
|
||||
int i;
|
||||
|
||||
printf("###########################################################\n");
|
||||
printf("# %s\n\n", format);
|
||||
|
||||
/* Define fields of record */
|
||||
util_rec_def(rec, "number", UTIL_REC_ALIGN_LEFT, 6, "Number");
|
||||
util_rec_def(rec, "name", UTIL_REC_ALIGN_LEFT, 10, "Name");
|
||||
util_rec_def(rec, "size", UTIL_REC_ALIGN_RIGHT, 15, "Size");
|
||||
|
||||
/* Print record header (is a nop for long format) */
|
||||
util_rec_print_hdr(rec);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
/* Fill fields of record with values */
|
||||
util_rec_set(rec, "number", "%d", i);
|
||||
util_rec_set(rec, "name", name_vec[i]);
|
||||
util_rec_set(rec, "size", size_vec[i]);
|
||||
/* Print the record */
|
||||
util_rec_print(rec);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Print keys for record fields
|
||||
*/
|
||||
static void print_fields(struct util_rec *rec)
|
||||
{
|
||||
struct util_rec_fld *fld;
|
||||
int i = 1;
|
||||
|
||||
printf("###########################################################\n");
|
||||
printf("# Keys of record fields\n");
|
||||
|
||||
util_rec_iterate(rec, fld) {
|
||||
printf("Field %d : %s\n", i++, util_rec_fld_get_key(fld));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print records in "wide", "long", and "csv" format
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
struct util_rec *rec;
|
||||
|
||||
rec = util_rec_new_wide("-");
|
||||
print_records("Wide format", rec);
|
||||
util_rec_free(rec);
|
||||
|
||||
rec = util_rec_new_long("-", ":", "number", 30, 20);
|
||||
print_records("Long format", rec);
|
||||
util_rec_free(rec);
|
||||
|
||||
rec = util_rec_new_csv(",");
|
||||
print_records("CSV format", rec);
|
||||
print_fields(rec);
|
||||
util_rec_free(rec);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
//! [code]
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Scan a directory for matching entries
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <regex.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
/// @cond
|
||||
struct util_scandir_filter {
|
||||
regex_t reg_buf;
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/*
|
||||
* Check directory entry
|
||||
*/
|
||||
static int filter_regexp(const struct dirent *de, void *data)
|
||||
{
|
||||
struct util_scandir_filter *filter = data;
|
||||
regmatch_t pmatch[1];
|
||||
|
||||
if (regexec(&filter->reg_buf, de->d_name, (size_t) 1, pmatch, 0) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*__compar_fn_t) (const void *, const void *);
|
||||
|
||||
/*
|
||||
* Return sorted "struct dirent" array for entries that match "filter_fn"
|
||||
*/
|
||||
static int __scandir(struct dirent ***de_vec, const char *path,
|
||||
int (*filter_fn)(const struct dirent *, void *),
|
||||
void *filter_data,
|
||||
int (*compar_fn)(const struct dirent **,
|
||||
const struct dirent **))
|
||||
{
|
||||
struct dirent *de, *de_new, **de_vec_new = NULL;
|
||||
int count = 0;
|
||||
DIR *dirp;
|
||||
|
||||
dirp = opendir(path);
|
||||
if (!dirp)
|
||||
return -1;
|
||||
while ((de = readdir(dirp))) {
|
||||
if (filter_fn(de, filter_data) == 0)
|
||||
continue;
|
||||
de_new = util_malloc(sizeof(*de_new));
|
||||
*de_new = *de;
|
||||
de_vec_new = realloc(de_vec_new, sizeof(void *) * (count + 1));
|
||||
de_vec_new[count++] = de_new;
|
||||
}
|
||||
closedir(dirp);
|
||||
if (compar_fn)
|
||||
qsort(de_vec_new, count, sizeof(void *),
|
||||
(__compar_fn_t) compar_fn);
|
||||
*de_vec = de_vec_new;
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return sorted "struct dirent" array for entries that match "pattern"
|
||||
*/
|
||||
static int scandir_regexp(struct dirent ***de_vec, const char *path,
|
||||
const char *pattern,
|
||||
int compar_fn(const struct dirent **,
|
||||
const struct dirent **))
|
||||
{
|
||||
struct util_scandir_filter filter;
|
||||
char err_buf[256];
|
||||
int count, rc;
|
||||
|
||||
rc = regcomp(&filter.reg_buf, pattern, REG_EXTENDED);
|
||||
if (rc) {
|
||||
regerror(rc, &filter.reg_buf, err_buf, sizeof(err_buf));
|
||||
util_panic("Function regcomp(%s) failed: %s\n", pattern,
|
||||
err_buf);
|
||||
}
|
||||
count = __scandir(de_vec, path, filter_regexp, &filter, compar_fn);
|
||||
regfree(&filter.reg_buf);
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two hexadecimal string dirents numerically
|
||||
*
|
||||
* @param[in] de1 First directory entry
|
||||
* @param[in] de2 Second directory entry
|
||||
*
|
||||
* @retval -1 de1 < de2
|
||||
* @retval 0 de1 = de2
|
||||
* @retval 1 de1 > de2
|
||||
*/
|
||||
int util_scandir_hexsort(const struct dirent **de1, const struct dirent **de2)
|
||||
{
|
||||
unsigned long val1 = strtoul((*de1)->d_name, NULL, 16);
|
||||
unsigned long val2 = strtoul((*de2)->d_name, NULL, 16);
|
||||
|
||||
if (val1 < val2)
|
||||
return -1;
|
||||
if (val1 == val2)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a list of direcotry entries using POSIX regular expressions
|
||||
*
|
||||
* A desired directory in sysfs is scanned for entries of a given name
|
||||
* pattern. The name pattern is constructed with sprintf() using a format
|
||||
* string and variable argument list. After constructing the pattern it
|
||||
* is used with regcomp() and regexec() to find matching entries.
|
||||
* The returned list of matches consist of an array of pointers to
|
||||
* directory entries. The entries as well as the pointer array itself are
|
||||
* allocated by the function and has to be released by the user via free.
|
||||
*
|
||||
* @param[out] de_vec Vector of matched directory entries
|
||||
* @param[in] compar_fn Callback function for sorting the entry list
|
||||
* @param[in] path Path to the directory to scan
|
||||
* @param[in] fmt Format string, describes the search pattern as POSIX regex
|
||||
* @param[in] ... Values for format string
|
||||
*
|
||||
* @returns Number of returned directory entries
|
||||
*/
|
||||
int util_scandir(struct dirent ***de_vec,
|
||||
int compar_fn(const struct dirent **first,
|
||||
const struct dirent **second),
|
||||
const char *path,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char *pattern;
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, fmt);
|
||||
rc = vasprintf(&pattern, fmt, ap);
|
||||
va_end(ap);
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
rc = scandir_regexp(de_vec, path, pattern, compar_fn);
|
||||
free(pattern);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free list of directory entries
|
||||
*
|
||||
* @param[in] de_vec Vector of directory entries
|
||||
* @param[in] count Count of directory entries
|
||||
*/
|
||||
void util_scandir_free(struct dirent **de_vec, int count)
|
||||
{
|
||||
util_ptr_vec_free((void **) de_vec, count);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* util_dir_example - Example program for util_dir
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
//! [code]
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/util_scandir.h"
|
||||
|
||||
/*
|
||||
* Print real sysfs cpu directory contents
|
||||
*/
|
||||
static void show_cpu_dir(const char *sys_cpu_path)
|
||||
{
|
||||
char cmd_ls[256];
|
||||
|
||||
sprintf(cmd_ls, "ls -l %s", sys_cpu_path);
|
||||
printf("$ %s\n", cmd_ls);
|
||||
fflush(stdout);
|
||||
if (system(cmd_ls)) {
|
||||
perror("system() failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Show all CPUs on Linux system
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
struct dirent **de_vec;
|
||||
int count, i;
|
||||
const char *path = "/sys/devices/system/cpu";
|
||||
const char *prefix = "cpu";
|
||||
|
||||
show_cpu_dir(path);
|
||||
|
||||
/*
|
||||
* Process all files that match regular expression "cpu[0-9]+"
|
||||
* and sort them alphabetically. Note that the regular expression
|
||||
* is constructed with a variable argument list.
|
||||
*/
|
||||
count = util_scandir(&de_vec, alphasort, path, "%s[0-9]+", prefix);
|
||||
if (count == -1) {
|
||||
perror("util_dir_scan failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Print all directories */
|
||||
printf("Found cpus:\n\n");
|
||||
for (i = 0; i < count; i++) {
|
||||
if (de_vec[i]->d_type != DT_DIR)
|
||||
continue;
|
||||
printf(" - %s\n", de_vec[i]->d_name);
|
||||
}
|
||||
|
||||
/* Free directory entries */
|
||||
util_scandir_free(de_vec, count);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
//! [code]
|
||||
Reference in New Issue
Block a user