Blame utils.c

Packit 400c17
/*
Packit 400c17
	Copyright(C) 2016, Red Hat, Inc., Stanislav Kozina
Packit 400c17
Packit 400c17
	This program is free software: you can redistribute it and/or modify
Packit 400c17
	it under the terms of the GNU General Public License as published by
Packit 400c17
	the Free Software Foundation, either version 3 of the License, or
Packit 400c17
	(at your option) any later version.
Packit 400c17
Packit 400c17
	This program is distributed in the hope that it will be useful,
Packit 400c17
	but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 400c17
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 400c17
	GNU General Public License for more details.
Packit 400c17
Packit 400c17
	You should have received a copy of the GNU General Public License
Packit 400c17
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 400c17
*/
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * This file contains couple of generally useful functions.
Packit 400c17
 */
Packit 400c17
Packit 400c17
#include <sys/types.h>
Packit 400c17
#include <stdlib.h>
Packit 400c17
#include <stdbool.h>
Packit 400c17
#include <stdio.h>
Packit 400c17
#include <errno.h>
Packit 400c17
#include <string.h>
Packit 400c17
#include <sys/stat.h>
Packit 400c17
#include <unistd.h>
Packit 400c17
#include <dirent.h>
Packit 400c17
#include <assert.h>
Packit 400c17
#include <libgen.h> /* dirname() */
Packit 400c17
Packit 400c17
#include "main.h"
Packit 400c17
#include "utils.h"
Packit Service 28f424
#include "hash.h"
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Sort function for scandir.
Packit 400c17
 * Walk regular file first, then process subdirectories.
Packit 400c17
 */
Packit 400c17
int reg_first(const struct dirent **a, const struct dirent **b)
Packit 400c17
{
Packit 400c17
	if ((*a)->d_type == DT_REG && (*b)->d_type != DT_REG)
Packit 400c17
		return -1;
Packit 400c17
	if ((*b)->d_type == DT_REG && (*a)->d_type != DT_REG)
Packit 400c17
		return 1;
Packit 400c17
Packit 400c17
	/*
Packit 400c17
	 * Backup to default collation
Packit 400c17
	 * Note: the behavior depends on LC_COLLATE
Packit 400c17
	 */
Packit 400c17
	return alphasort(a, b);
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Call cb() on all nodes in the directory structure @path.
Packit 400c17
 * If list_dirs == true run cb() on subdirectories as well, otherwise list only
Packit 400c17
 * files.
Packit 400c17
 * The cb() has to return true if we continue directory walk or false if we're
Packit 400c17
 * all done.
Packit 400c17
 */
Packit 400c17
void walk_dir(char *path, bool list_dirs, walk_rv_t (*cb)(char *, void *),
Packit 400c17
		void *arg)
Packit 400c17
{
Packit 400c17
	struct dirent **entlist;
Packit 400c17
	walk_rv_t cb_rv = WALK_CONT;
Packit 400c17
	int entries, i;
Packit 400c17
Packit 400c17
	assert(path != NULL && strlen(path) >= 1);
Packit 400c17
Packit 400c17
	entries = scandir(path, &entlist, NULL, reg_first);
Packit 400c17
	if (entries == -1) {
Packit 400c17
		fail("Failed to scan module directory %s: %s\n", path,
Packit 400c17
		    strerror(errno));
Packit 400c17
	}
Packit 400c17
Packit 400c17
	/* process all the files and directories within directory */
Packit 400c17
	for (i = 0; i < entries; i++) {
Packit 400c17
		struct dirent *ent = entlist[i];
Packit 400c17
		struct stat entstat;
Packit 400c17
		char *new_path;
Packit 400c17
Packit 400c17
		if ((strcmp(ent->d_name, "..") == 0) ||
Packit 400c17
		    (strcmp(ent->d_name, ".") == 0)) {
Packit 400c17
			free(ent);
Packit 400c17
			continue;
Packit 400c17
		}
Packit 400c17
Packit 400c17
		if (path[strlen(path) - 1] == '/')
Packit 400c17
			safe_asprintf(&new_path, "%s%s", path, ent->d_name);
Packit 400c17
		else
Packit 400c17
			safe_asprintf(&new_path, "%s/%s", path, ent->d_name);
Packit 400c17
Packit 400c17
		if (lstat(new_path, &entstat) != 0) {
Packit 400c17
			fail("Failed to stat directory %s: %s\n", new_path,
Packit 400c17
			    strerror(errno));
Packit 400c17
		}
Packit 400c17
Packit 400c17
		if (S_ISDIR(entstat.st_mode)) {
Packit 400c17
			if (list_dirs) {
Packit 400c17
				cb_rv = cb(new_path, arg);
Packit 400c17
				if (cb_rv != WALK_CONT)
Packit 400c17
					goto out;
Packit 400c17
			}
Packit 400c17
Packit 400c17
			/* Ignore symlinks */
Packit 400c17
			if (!S_ISLNK(entstat.st_mode))
Packit 400c17
				walk_dir(new_path, list_dirs, cb, arg);
Packit 400c17
		} else if (S_ISREG(entstat.st_mode)) {
Packit 400c17
			cb_rv = cb(new_path, arg);
Packit 400c17
		}
Packit 400c17
Packit 400c17
out:
Packit 400c17
		free(new_path);
Packit 400c17
		free(ent);
Packit 400c17
Packit 400c17
		if (cb_rv == WALK_STOP)
Packit 400c17
			break;
Packit 400c17
		if (cb_rv == WALK_SKIP) {
Packit 400c17
			cb_rv = WALK_CONT;
Packit 400c17
			break;
Packit 400c17
		}
Packit 400c17
	}
Packit 400c17
Packit 400c17
	free(entlist);
Packit 400c17
}
Packit 400c17
Packit 400c17
int check_is_directory(char *dir)
Packit 400c17
{
Packit 400c17
	struct stat dirstat;
Packit 400c17
Packit 400c17
	if (stat(dir, &dirstat) != 0)
Packit 400c17
		return errno;
Packit 400c17
Packit 400c17
	if (!S_ISDIR(dirstat.st_mode))
Packit 400c17
		return ENOTDIR;
Packit 400c17
Packit 400c17
	return 0;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void safe_mkdir(char *path)
Packit 400c17
{
Packit 400c17
	if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0)
Packit 400c17
		fail("%s", strerror(errno));
Packit 400c17
}
Packit 400c17
Packit 400c17
void rec_mkdir(char *path)
Packit 400c17
{
Packit 400c17
	char *buf;
Packit 400c17
	char *pos;
Packit 400c17
	size_t len = strlen(path);
Packit 400c17
Packit 400c17
	assert(path != NULL && len > 0);
Packit 400c17
Packit 400c17
	buf = safe_strdup(path);
Packit 400c17
Packit 400c17
	/* Get rid of trailing slashes */
Packit 400c17
	for (pos = buf + len - 1; pos > buf && *pos == '/'; --pos)
Packit 400c17
		*pos = '\0';
Packit 400c17
Packit 400c17
	pos = buf;
Packit 400c17
	while (pos != NULL) {
Packit 400c17
		int rv;
Packit 400c17
		char *next;
Packit 400c17
Packit 400c17
		/* Skip multiple slashes */
Packit 400c17
		for (next = pos + 1; *next == '/'; next++)
Packit 400c17
			;
Packit 400c17
Packit 400c17
		pos = strchr(next, '/');
Packit 400c17
		if (pos != NULL)
Packit 400c17
			*pos = '\0';
Packit 400c17
		rv = check_is_directory(buf);
Packit 400c17
		if (rv != 0) {
Packit 400c17
			if (rv == ENOENT)
Packit 400c17
				safe_mkdir(buf);
Packit 400c17
			else
Packit 400c17
				fail("%s", strerror(rv));
Packit 400c17
		}
Packit 400c17
Packit 400c17
		if (pos != NULL)
Packit 400c17
			*pos = '/';
Packit 400c17
	}
Packit 400c17
Packit 400c17
	free(buf);
Packit 400c17
}
Packit 400c17
Packit 400c17
void safe_rename(const char *oldpath, const char *newpath)
Packit 400c17
{
Packit 400c17
	char *temp;
Packit 400c17
Packit 400c17
	temp = safe_strdup(newpath);
Packit 400c17
	/* dirname() modifies its buffer! */
Packit 400c17
	rec_mkdir(dirname(temp));
Packit 400c17
	free(temp);
Packit 400c17
Packit 400c17
	if (rename(oldpath, newpath) != 0)
Packit 400c17
		fail("rename() failed: %s\n", strerror(errno));
Packit 400c17
}
Packit 400c17
Packit 400c17
struct norm_ctx {
Packit 400c17
	char *path;
Packit 400c17
	char *p;
Packit 400c17
	char *outp;
Packit 400c17
};
Packit 400c17
Packit 400c17
/* actually, second last, skip the whole directory */
Packit 400c17
static char *last_slash(char *str, char *end)
Packit 400c17
{
Packit 400c17
	char c = '/';
Packit 400c17
	int met = 0;
Packit 400c17
Packit 400c17
	for (; end > str; end--) {
Packit 400c17
		if (*end == c) {
Packit 400c17
			if (met)
Packit 400c17
				return end;
Packit 400c17
			else
Packit 400c17
				met = 1;
Packit 400c17
		}
Packit 400c17
	}
Packit 400c17
	return NULL;
Packit 400c17
}
Packit 400c17
Packit 400c17
typedef void *(*state_t)(struct norm_ctx *);
Packit 400c17
Packit 400c17
static void *initial(struct norm_ctx *ctx);
Packit 400c17
static void *normal(struct norm_ctx *ctx);
Packit 400c17
static void *one_dot(struct norm_ctx *ctx);
Packit 400c17
static void *two_dots(struct norm_ctx *ctx);
Packit 400c17
static void *slash(struct norm_ctx *ctx);
Packit 400c17
static void *end(struct norm_ctx *ctx);
Packit 400c17
Packit 400c17
static void *initial(struct norm_ctx *ctx)
Packit 400c17
{
Packit 400c17
	char c = *ctx->p++;
Packit 400c17
Packit 400c17
	switch (c) {
Packit 400c17
	case '\0':
Packit 400c17
		*ctx->outp = c;
Packit 400c17
		return end;
Packit 400c17
	case '/':
Packit 400c17
		*ctx->outp++ = c;
Packit 400c17
		return slash;
Packit 400c17
	case '.':
Packit 400c17
		return one_dot;
Packit 400c17
	default:
Packit 400c17
		*ctx->outp++ = c;
Packit 400c17
	}
Packit 400c17
	return normal;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void *normal(struct norm_ctx *ctx)
Packit 400c17
{
Packit 400c17
	char c = *ctx->p++;
Packit 400c17
Packit 400c17
	switch (c) {
Packit 400c17
	case '\0':
Packit 400c17
		*ctx->outp++ = c;
Packit 400c17
		return end;
Packit 400c17
	case '/':
Packit 400c17
		*ctx->outp++ = c;
Packit 400c17
		return slash;
Packit 400c17
	default:
Packit 400c17
		*ctx->outp++ = c;
Packit 400c17
	}
Packit 400c17
	return normal;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void *slash(struct norm_ctx *ctx)
Packit 400c17
{
Packit 400c17
	char c = *ctx->p++;
Packit 400c17
Packit 400c17
	switch (c) {
Packit 400c17
	case '\0':
Packit 400c17
		fail("Cannot normalize path %s", ctx->path);
Packit 400c17
	case '/':
Packit 400c17
		return slash;
Packit 400c17
	case '.':
Packit 400c17
		return one_dot;
Packit 400c17
	default:
Packit 400c17
		*ctx->outp++ = c;
Packit 400c17
	}
Packit 400c17
	return normal;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void *one_dot(struct norm_ctx *ctx)
Packit 400c17
{
Packit 400c17
	char c = *ctx->p++;
Packit 400c17
Packit 400c17
	switch (c) {
Packit 400c17
	case '\0':
Packit 400c17
		*--ctx->outp = c;
Packit 400c17
		return end;
Packit 400c17
	case '/':
Packit 400c17
		return slash;
Packit 400c17
	case '.':
Packit 400c17
		return two_dots;
Packit 400c17
	default:
Packit 400c17
		*ctx->outp++ = '.';
Packit 400c17
		*ctx->outp++ = c;
Packit 400c17
	}
Packit 400c17
	return normal;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void *two_dots(struct norm_ctx *ctx)
Packit 400c17
{
Packit 400c17
	char c = *ctx->p++;
Packit 400c17
	char *p;
Packit 400c17
Packit 400c17
	switch (c) {
Packit 400c17
	case '\0':
Packit 400c17
		p = last_slash(ctx->path, ctx->outp);
Packit 400c17
		if (p == NULL)
Packit 400c17
			p = ctx->path;
Packit 400c17
		*p = c;
Packit 400c17
		return end;
Packit 400c17
	case '/':
Packit 400c17
		p = last_slash(ctx->path, ctx->outp);
Packit 400c17
		if (p == NULL) {
Packit 400c17
			ctx->outp = ctx->path;
Packit 400c17
			return normal;
Packit 400c17
		}
Packit 400c17
		ctx->outp = ++p;
Packit 400c17
		return slash;
Packit 400c17
	default:
Packit 400c17
		*ctx->outp++ = '.';
Packit 400c17
		*ctx->outp++ = '.';
Packit 400c17
		*ctx->outp++ = c;
Packit 400c17
	}
Packit 400c17
	return normal;
Packit 400c17
}
Packit 400c17
Packit 400c17
static void *end(struct norm_ctx *ctx)
Packit 400c17
{
Packit 400c17
	fail("Cannot normalize path %s", ctx->path);
Packit 400c17
}
Packit 400c17
Packit 400c17
char *path_normalize(char *path)
Packit 400c17
{
Packit 400c17
	struct norm_ctx ctx = {
Packit 400c17
		.path = path,
Packit 400c17
		.p = path,
Packit 400c17
		.outp = path,
Packit 400c17
	};
Packit 400c17
	state_t state = initial;
Packit 400c17
Packit 400c17
	while (state != end)
Packit 400c17
		state = state(&ctx;;
Packit 400c17
Packit 400c17
	return path;
Packit 400c17
}
Packit 400c17
Packit 400c17
/* Removes the two dashes at the end of the prefix */
Packit 400c17
#define IS_PREFIX(s, prefix) !strncmp(s, prefix, strlen(prefix) - 2)
Packit 400c17
Packit Service 28f424
static void split_filename(const char *filename, char **prefix,
Packit 400c17
			   char **name, int *version)
Packit 400c17
{
Packit Service 28f424
	/* GNU version of basename never modifies its argument */
Packit Service 28f424
	char *base = basename((char *)filename);
Packit 400c17
Packit 400c17
	version = 0;
Packit 400c17
Packit 400c17
	if ((sscanf(base, "%m[a-z]--%m[^.-].txt", prefix, name) != 2) &&
Packit 400c17
	    (sscanf(base, "%m[a-z]--%m[^.-]-%i.txt",
Packit 400c17
		    prefix, name, version) != 3))
Packit 400c17
		fail("Unexpected file name: %s\n", filename);
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Get the type of a symbol from the name of the kabi file
Packit 400c17
 *
Packit 400c17
 * It allocates the string which must be freed by the caller.
Packit 400c17
 */
Packit Service 28f424
char *filenametotype(const char *filename)
Packit 400c17
{
Packit 400c17
	char *prefix = NULL, *name = NULL, *type = NULL;
Packit 400c17
	int version = 0;
Packit 400c17
Packit 400c17
	split_filename(filename, &prefix, &name, &version);
Packit 400c17
Packit 400c17
	if (IS_PREFIX(prefix, TYPEDEF_FILE))
Packit 400c17
		type = name;
Packit 400c17
	else if (IS_PREFIX(prefix, STRUCT_FILE) ||
Packit 400c17
		 IS_PREFIX(prefix, UNION_FILE) ||
Packit 400c17
		 IS_PREFIX(prefix, ENUM_FILE))
Packit 400c17
		safe_asprintf(&type, "%s %s", prefix, name);
Packit 400c17
	else
Packit 400c17
		fail("Unexpected file prefix: %s\n", prefix);
Packit 400c17
Packit 400c17
	free(prefix);
Packit 400c17
	if (name != type)
Packit 400c17
		free(name);
Packit 400c17
Packit 400c17
	return type;
Packit 400c17
}
Packit 400c17
Packit 400c17
/*
Packit 400c17
 * Get the name of a symbol from the name of the kabi file
Packit 400c17
 *
Packit 400c17
 * It allocates the string which must be freed by the caller.
Packit 400c17
 */
Packit Service 28f424
char *filenametosymbol(const char *filename)
Packit 400c17
{
Packit 400c17
	char *prefix = NULL, *name = NULL;
Packit 400c17
	int version = 0;
Packit 400c17
Packit 400c17
	split_filename(filename, &prefix, &name, &version);
Packit 400c17
	free(prefix);
Packit 400c17
Packit 400c17
	return name;
Packit 400c17
}
Packit Service 28f424
Packit Service 28f424
struct hash *global_string_keeper;
Packit Service 28f424
Packit Service 28f424
void global_string_keeper_init(void)
Packit Service 28f424
{
Packit Service 28f424
	global_string_keeper = hash_new(1 << 20, free);
Packit Service 28f424
}
Packit Service 28f424
Packit Service 28f424
void global_string_keeper_free(void)
Packit Service 28f424
{
Packit Service 28f424
	hash_free(global_string_keeper);
Packit Service 28f424
}
Packit Service 28f424
Packit Service 28f424
const char *global_string_get_copy(const char *string)
Packit Service 28f424
{
Packit Service 28f424
	const char *result;
Packit Service 28f424
Packit Service 28f424
	if (string == NULL)
Packit Service 28f424
		return NULL;
Packit Service 28f424
Packit Service 28f424
	result = hash_find(global_string_keeper, string);
Packit Service 28f424
	if (result == NULL) {
Packit Service 28f424
		result = safe_strdup(string);
Packit Service 28f424
		hash_add(global_string_keeper, result, result);
Packit Service 28f424
	}
Packit Service 28f424
Packit Service 28f424
	return result;
Packit Service 28f424
}
Packit Service 28f424
Packit Service 28f424
const char *global_string_get_move(char *string)
Packit Service 28f424
{
Packit Service 28f424
	const char *result;
Packit Service 28f424
Packit Service 28f424
	if (string == NULL)
Packit Service 28f424
		return NULL;
Packit Service 28f424
Packit Service 28f424
	result = hash_find(global_string_keeper, string);
Packit Service 28f424
	if (result == NULL) {
Packit Service 28f424
		result = string;
Packit Service 28f424
		hash_add(global_string_keeper, result, result);
Packit Service 28f424
	} else {
Packit Service 28f424
		free(string);
Packit Service 28f424
	}
Packit Service 28f424
Packit Service 28f424
	return result;
Packit Service 28f424
}