Dump some old code up in here.
This commit is contained in:
parent
d7c4c0800d
commit
a2af41e2a4
|
@ -0,0 +1,16 @@
|
||||||
|
Copyright (c) 2011-2020 Kyle Isom <kyle@imap.cc>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for
|
||||||
|
any purpose with or without fee is hereby granted, provided that
|
||||||
|
the above copyright notice and this permission notice appear in all
|
||||||
|
copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
.:[ kst: kyle's standard/system tools ]:.
|
||||||
|
|
||||||
|
This is stuff I use to get things done. The code is released open
|
||||||
|
source, but as it's tooling that I wrote for me, I'd be reticent
|
||||||
|
to receive patches that alter the functionality in ways that I don't
|
||||||
|
want. I also don't provide support for this. You can ask questions,
|
||||||
|
I probably won't answer. It's nothing personal, I just probably
|
||||||
|
don't have time.
|
|
@ -0,0 +1,36 @@
|
||||||
|
srm - securely wipe files
|
||||||
|
--------------------------
|
||||||
|
srm is a utility to overwrite files with random data in one or more passes.
|
||||||
|
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
None.
|
||||||
|
|
||||||
|
|
||||||
|
Compatibility
|
||||||
|
-------------
|
||||||
|
srm has been tested on the following operating systems:
|
||||||
|
* OpenBSD (5.1-snap)
|
||||||
|
* OS X (10.8)
|
||||||
|
* Linux (Debian 6.0)
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
make build install
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
srm [-v] [-n number] file list
|
||||||
|
|
||||||
|
options:
|
||||||
|
-n <number of passes>: specify number of passes
|
||||||
|
(default is 3 passes)
|
||||||
|
-v: verbose mode. display list of failures and wiped files after wiping
|
||||||
|
|
||||||
|
|
||||||
|
Known bugs / caveats
|
||||||
|
--------------------
|
||||||
|
srm can't recursively remove files, i.e. it can't remove directories.
|
|
@ -0,0 +1,95 @@
|
||||||
|
.Dd November 20, 2012
|
||||||
|
.Dt LIBDIRUTILS 3
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm libdirutils
|
||||||
|
.Nd Python-esque file utility functions for C
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.In dirutils.h
|
||||||
|
.Ft int
|
||||||
|
.Fo makedirs
|
||||||
|
.Fa "const char *path"
|
||||||
|
.Fc
|
||||||
|
.Ft EXISTS_STATUS
|
||||||
|
.Fo path_exists
|
||||||
|
.Fa "const char *path"
|
||||||
|
.Fc
|
||||||
|
.Ft int
|
||||||
|
.Fo rmdirs
|
||||||
|
.Fa "const char *path"
|
||||||
|
.Fc
|
||||||
|
.Ft int
|
||||||
|
.Fo walkpath
|
||||||
|
.Fa "const char *root"
|
||||||
|
.Fa "dirwalk_action action"
|
||||||
|
.Fa "unsigned char mask"
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
provides a number of convenience functions for working with files and
|
||||||
|
directories; as the name implies, it is aimed primarily at directories.
|
||||||
|
.Nm makedirs
|
||||||
|
provides functionality similar to
|
||||||
|
.Ic mkdir -p ,
|
||||||
|
creating all the parent directories required. They are created with the
|
||||||
|
caller's umask applied to the mode 0777.
|
||||||
|
.Nm path_exists
|
||||||
|
returns an EXISTS_STATUS value (described below) indicated the status
|
||||||
|
of the file.
|
||||||
|
.Nm rmdirs
|
||||||
|
provides functionality similar to
|
||||||
|
.Ic rm -r .
|
||||||
|
.Nm walkpath
|
||||||
|
walks through a path on disk and calls an action on a file if it
|
||||||
|
matches the file type mask. The mask should include some of the
|
||||||
|
file types listed in
|
||||||
|
.Xr readdir 3 ,
|
||||||
|
such as DT_DIR or DT_REG. The convenience definitions
|
||||||
|
.Ic FT_ANY
|
||||||
|
and
|
||||||
|
.Ic FT_STD
|
||||||
|
are provided; the former matches any file type and the latter matches
|
||||||
|
regular files and directories. Note that this will not follow links.
|
||||||
|
The action is defined in the dirutils.h header as
|
||||||
|
.Ic typedef int (*dirwalk_action)(const char *) ;
|
||||||
|
it takes a NULL-terminated path and does what it will with that path.
|
||||||
|
It should return -1 on failure and 0 on success. Note that in the case
|
||||||
|
where the path provided to
|
||||||
|
.Nm walkdir
|
||||||
|
is not a directory, but its type matches the mask, the action will
|
||||||
|
still be run on it.
|
||||||
|
.Sh RETURN VALUES
|
||||||
|
.Nm makedirs
|
||||||
|
and
|
||||||
|
.Nm rmdirs
|
||||||
|
return EXIT_SUCCESS on success and EXIT_FAILURE on failure. The enum
|
||||||
|
returned by
|
||||||
|
.Nm path_exists
|
||||||
|
is defined in
|
||||||
|
.Sy dirutils.h
|
||||||
|
as:
|
||||||
|
.Bd -literal
|
||||||
|
enum E_EXISTS_STATUS {
|
||||||
|
EXISTS_ERROR, /* an error occurred looking at the file */
|
||||||
|
EXISTS_NOENT, /* the path does not exist */
|
||||||
|
EXISTS_NOPERM, /* the process does not have appropriate permissions */
|
||||||
|
EXISTS_DIR, /* the path exists and is a directory */
|
||||||
|
EXISTS_FILE, /* the path exists and is a regular file */
|
||||||
|
EXISTS_OTHER /* the path exists and is not a directory or regular */
|
||||||
|
/* file. */
|
||||||
|
};
|
||||||
|
.Ed
|
||||||
|
.Sh ERRORS
|
||||||
|
The most common error will be a permissions error; it may be prudent to
|
||||||
|
compare the value of
|
||||||
|
.Nm errno
|
||||||
|
before and after calling the function.
|
||||||
|
.Sh HISTORY
|
||||||
|
.Nm
|
||||||
|
was inspired by similar functions in the Python and Go programming languages.
|
||||||
|
The author found himself in need of such functions in several projects,
|
||||||
|
and decided to write a utility library to handle these functions.
|
||||||
|
.Sh AUTHORS
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
library was written by
|
||||||
|
.An Kyle Isom Aq Mt kyle@tyrfingr.is .
|
|
@ -0,0 +1,285 @@
|
||||||
|
.Dd Sep 9, 2015
|
||||||
|
.Dt LIBINIPARSER 3
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm libiniparser
|
||||||
|
.Nd simple parser for .ini files
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.In stdint.h
|
||||||
|
.In iniparser/iniparser.h
|
||||||
|
.Ft int
|
||||||
|
.Fo iniparser_init
|
||||||
|
.Fa void
|
||||||
|
.Fc
|
||||||
|
.Ft void
|
||||||
|
.Fo iniparser_destroy
|
||||||
|
.Fa void
|
||||||
|
.Fc
|
||||||
|
.Ft int
|
||||||
|
.Fo iniparser_open
|
||||||
|
.Fa "const char *path"
|
||||||
|
.Fa "iniparser_file_s **file"
|
||||||
|
.Fc
|
||||||
|
.Ft int
|
||||||
|
.Fo iniparser_close
|
||||||
|
.Fa "iniparser_file_s *file"
|
||||||
|
.Fc
|
||||||
|
.Ft int
|
||||||
|
.Fo iniparser_readline
|
||||||
|
.Fa "iniparser_file_s *file"
|
||||||
|
.Fa "inipaser_line_s *line"
|
||||||
|
.Fc
|
||||||
|
.Ft void
|
||||||
|
.Fo iniparser_line_init
|
||||||
|
.Fa "iniparser_line_s *line"
|
||||||
|
.Fc
|
||||||
|
.Ft void
|
||||||
|
.Fo iniparser_line_destroy
|
||||||
|
.Fa "iniparser_line_s *line"
|
||||||
|
.Fc
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
is a parsing library for .ini files. An ini file is of the form
|
||||||
|
.Bd -literal
|
||||||
|
[ section1 ]
|
||||||
|
keyA = valueA
|
||||||
|
keyB = valueB
|
||||||
|
|
||||||
|
[ section2 ]
|
||||||
|
keyA = valueA
|
||||||
|
keyB = valueB
|
||||||
|
keyC =
|
||||||
|
.Ed
|
||||||
|
.Ss OVERVIEW
|
||||||
|
The library is initialised with
|
||||||
|
.Nm iniparser_init ,
|
||||||
|
which will
|
||||||
|
.Sy must
|
||||||
|
be called before using the library. When no longer needed, the
|
||||||
|
.Nm libparser_destroy
|
||||||
|
function should be called to free resources.
|
||||||
|
Configuration files are represented by the
|
||||||
|
.Nm iniparser_file_s structure, which is defined as
|
||||||
|
.Bd -literal
|
||||||
|
typedef struct {
|
||||||
|
FILE *source;
|
||||||
|
char *lineptr;
|
||||||
|
size_t linelen;
|
||||||
|
ssize_t readlen;
|
||||||
|
} iniparser_file_s;
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
A configuration file may be opened with
|
||||||
|
.Nm iniparser_open ,
|
||||||
|
which if given a pointer to a NULL pointer to an
|
||||||
|
.Nm iniparser_file_s ,
|
||||||
|
will allocate space using
|
||||||
|
.Xr calloc 3 .
|
||||||
|
The caller must be sure then to free this memory when it is no longer
|
||||||
|
needed. Callers may also explicitly set the
|
||||||
|
.Ic source
|
||||||
|
pointer to the appropriate source file handle, and ensure that the
|
||||||
|
other fields are at zero values. Similarly,
|
||||||
|
.Nm iniparser_close
|
||||||
|
will close the source file handle and free any memory allocated inside
|
||||||
|
the structure. Users should still ensure that the structure itself is
|
||||||
|
freed as required.
|
||||||
|
.Nm iniparser_readline
|
||||||
|
will attempt to fill in the results of parsing the next line in the
|
||||||
|
configuration file. Note that it will keep reading lines, skipping
|
||||||
|
comments and blank lines, until it reads a valid section or key-value
|
||||||
|
line or until it encounters an error. The
|
||||||
|
.Nm iniparser_line_s
|
||||||
|
structure is defined as
|
||||||
|
.Bd -literal
|
||||||
|
typedef struct {
|
||||||
|
uint8_t is_section;
|
||||||
|
uint8_t is_set;
|
||||||
|
char *name;
|
||||||
|
char *value;
|
||||||
|
} iniparser_line_s;
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
A line should be initialised before first use with
|
||||||
|
.Nm iniparser_line_init ,
|
||||||
|
and should be destroyed after last use with
|
||||||
|
.Nm iniparser_line_destroy .
|
||||||
|
The fields are defined as:
|
||||||
|
.Bl -bullet -width Ds
|
||||||
|
.It
|
||||||
|
The is_section field will be set to 1 if the line was a new section.
|
||||||
|
.It
|
||||||
|
The is_set field will be set to 1 if valid data was set. This is
|
||||||
|
useful when an EOF is reached.
|
||||||
|
.It
|
||||||
|
The name field contains the section name if the is_section field is 1,
|
||||||
|
or the key if is_section is 0 and is_set is 1. It should be
|
||||||
|
disregarded outside of these two cases.
|
||||||
|
.It
|
||||||
|
The value field contains the key's value if is_section is 0 and is_set
|
||||||
|
is 1. It should be disregarded otherwise. If it is NULL, the key had
|
||||||
|
no value.
|
||||||
|
.El
|
||||||
|
Both the name and value field will have leading and trailing spaces
|
||||||
|
stripped. The line that is passed in is reset and its memory freed
|
||||||
|
every time
|
||||||
|
.Nm iniparser_readline
|
||||||
|
is run. Callers should copy any relevant data from the line elsewhere.
|
||||||
|
.Ss TYPICAL USAGE
|
||||||
|
Processing a file
|
||||||
|
generally follows the following steps:
|
||||||
|
.Bl -bullet
|
||||||
|
.It
|
||||||
|
Call
|
||||||
|
.Nm iniparser_open
|
||||||
|
with the path to the ini file and a pointer to an
|
||||||
|
.Nm iniparser_file_s
|
||||||
|
structure that will be used to manage the file parsing.
|
||||||
|
.It
|
||||||
|
Prepare the line storage variable with
|
||||||
|
.Nm iniparser_line_init .
|
||||||
|
.It
|
||||||
|
Call
|
||||||
|
.Nm iniparser_readline
|
||||||
|
to obtain successive values of the file.
|
||||||
|
.It
|
||||||
|
While iniparser_readline continues to succeed, keep processing the
|
||||||
|
results.
|
||||||
|
.It
|
||||||
|
Clean up with the following sequence:
|
||||||
|
.Bl -enum
|
||||||
|
.It
|
||||||
|
.Nm iniparser_line_destroy
|
||||||
|
.It
|
||||||
|
.Nm iniparser_close
|
||||||
|
.It
|
||||||
|
If the parser is no longer needed,
|
||||||
|
.Nm iniparser_destroy .
|
||||||
|
Once this is called, the library cannot be used until
|
||||||
|
.Nm iniparser_init
|
||||||
|
is called again.
|
||||||
|
.El
|
||||||
|
.El
|
||||||
|
.Ss THE REGULAR EXPRESSIONS
|
||||||
|
The following regular expressions are used:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It sections
|
||||||
|
"^\\[ *([a-zA-Z0-9_-]+) *\\]$"
|
||||||
|
.It blank lines
|
||||||
|
"^[ \t]*$"
|
||||||
|
.It comments
|
||||||
|
"^[ \t]*[#;]"
|
||||||
|
.It key-value lines
|
||||||
|
"^[ \t]*([a-zA-Z0-9_-]+)[ \t]*=(.*)$"
|
||||||
|
.El
|
||||||
|
.Sh RETURN VALUES
|
||||||
|
.Nm iniparser_init ,
|
||||||
|
.Nm iniparser_open ,
|
||||||
|
and
|
||||||
|
.Nm iniparser_readline
|
||||||
|
all return 0 on success and -1 on failure. Additionally,
|
||||||
|
.Nm iniparser_readline
|
||||||
|
uses 1 to signal EOF; the line value should be checked to determine if
|
||||||
|
there is new data and the file closed.
|
||||||
|
.Sh EXAMPLES
|
||||||
|
The following program will parse the files specified on the command
|
||||||
|
line and print sections and key/value pairs.
|
||||||
|
.Bd -literal
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "iniparser/iniparser.h"
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
iniparser_file_s *file = NULL;
|
||||||
|
iniparser_line_s line;
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = iniparser_init();
|
||||||
|
if (0 != ret) {
|
||||||
|
fprintf(stderr, "init failed: %d\n", ret);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
printf("Processing %s\n", argv[i]);
|
||||||
|
ret = iniparser_open(argv[i], &file);
|
||||||
|
if (0 != ret) {
|
||||||
|
perror("_open");
|
||||||
|
fprintf(stderr, "retval: %d\n", ret);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
iniparser_line_init(&line);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
ret = iniparser_readline(file, &line);
|
||||||
|
/* -1 is returned on error. */
|
||||||
|
if (-1 == ret) {
|
||||||
|
perror("_readline");
|
||||||
|
fprintf(stderr, "retval: %d\n", ret);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
/* 1 means EOF. */
|
||||||
|
else if (1 == ret) {
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.is_section) {
|
||||||
|
printf("Now in section '%s'\n", line.name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Read key '%s' with value '%s'\n",
|
||||||
|
line.name, line.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
iniparser_line_destroy(&line);
|
||||||
|
}
|
||||||
|
|
||||||
|
iniparser_close(file);
|
||||||
|
free(file);
|
||||||
|
file = NULL;
|
||||||
|
iniparser_line_destroy(&line);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
iniparser_line_destroy(&line);
|
||||||
|
if (argc > 0) {
|
||||||
|
iniparser_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret==0;
|
||||||
|
}
|
||||||
|
.Ed
|
||||||
|
.Sh ERRORS
|
||||||
|
.Nm iniparser_open
|
||||||
|
will fail if its
|
||||||
|
.Ic file
|
||||||
|
argument is NULL, the call to
|
||||||
|
.Xr calloc 3
|
||||||
|
fails, or the source file could not be opened. Note that in the case
|
||||||
|
where memory is allocated by the library, it will be freed on exit.
|
||||||
|
.Pp
|
||||||
|
.Nm iniparser_close
|
||||||
|
returns the exit value of the call to
|
||||||
|
.Xr fclose 3 .
|
||||||
|
.Pp
|
||||||
|
.Nm iniparser_readline
|
||||||
|
will fail if there are any improperly formatted lines; comments and
|
||||||
|
blank lines will be skipped. It will also fail if it fails to read
|
||||||
|
a line from the file.
|
||||||
|
.Sh AUTHORS
|
||||||
|
.Nm
|
||||||
|
was written by
|
||||||
|
.An Kyle Isom Aq Mt kyle@tyrfingr.is .
|
||||||
|
.Sh LICENSE
|
||||||
|
.Nm
|
||||||
|
is released under the MIT license.
|
|
@ -0,0 +1,84 @@
|
||||||
|
.Dd $Mdocdate$
|
||||||
|
.Dt SRM 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm srm
|
||||||
|
.Nd securely delete files
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl h
|
||||||
|
.Op Fl n Ar number
|
||||||
|
.Op Fl r
|
||||||
|
.Op Fl v
|
||||||
|
.Op Fl V
|
||||||
|
.Ar files
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
is a simple secure file deletion tool. It overwrites the file with several
|
||||||
|
passes of random data before unlinking it. If no options are specified, Nm
|
||||||
|
defaults to three passes.
|
||||||
|
.Nm
|
||||||
|
supports the following options:
|
||||||
|
.Bl -tag -width .Ds
|
||||||
|
.It Fl h
|
||||||
|
Display a brief help message.
|
||||||
|
.It Fl n Ar number
|
||||||
|
Specify the number of times to overwrite each target with random data.
|
||||||
|
.It Fl r
|
||||||
|
Recursive mode. Delete any directories and all subdirectories underneath.
|
||||||
|
.It Fl v
|
||||||
|
Verbose mode. Displays a list of both files that failed to wipe and files that
|
||||||
|
were successfully wiped.
|
||||||
|
.It Fl V
|
||||||
|
Print version information.
|
||||||
|
.El
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
.Ex -std
|
||||||
|
The exit values are standard
|
||||||
|
.Xr sysexits 3
|
||||||
|
values.
|
||||||
|
.Sh EXAMPLES
|
||||||
|
Wipe files
|
||||||
|
.Pa foo
|
||||||
|
and
|
||||||
|
.Pa bar
|
||||||
|
with three passes:
|
||||||
|
.Dl $ srm foo bar
|
||||||
|
Wipe files
|
||||||
|
.Pa baz
|
||||||
|
and
|
||||||
|
.Pa quux
|
||||||
|
with ten passes:
|
||||||
|
.Dl $ srm -n 10 baz quux
|
||||||
|
Wipe all PGP keys, i.e. files with extension
|
||||||
|
.Pa *.asc :
|
||||||
|
.Dl $ srm *.asc
|
||||||
|
Recursive deletes aren't implemented yet. A workaround is to use
|
||||||
|
.Nm
|
||||||
|
and
|
||||||
|
.Xr find 1 ,
|
||||||
|
for example to delete all
|
||||||
|
.Pa *.pgp
|
||||||
|
files:
|
||||||
|
.Dl $ find . -iname '*.pgp' -exec srm '{}' \;
|
||||||
|
.Sh DIAGNOSTICS
|
||||||
|
.Nm
|
||||||
|
uses the standard
|
||||||
|
.Xr err 3
|
||||||
|
facilities to report any errors that occur.
|
||||||
|
.Sh SEE ALSO
|
||||||
|
The srm page on
|
||||||
|
.Lk http://www.tyrfingr.is/projects/srm/ "tyrfinger" .
|
||||||
|
.Sh STANDARDS
|
||||||
|
.Nm
|
||||||
|
conforms to
|
||||||
|
.St -ansiC .
|
||||||
|
.Sh AUTHORS
|
||||||
|
.Nm
|
||||||
|
was written by
|
||||||
|
.An "Kyle Isom" Aq Mt kyle@tyrfingr.is .
|
||||||
|
.Sh BUGS
|
||||||
|
None known. Report bugs to the author.
|
||||||
|
.Sh LICENSE
|
||||||
|
.Nm
|
||||||
|
is released under an ISC license.
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
* ---------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __DIRUTILS_DIRLIST_H
|
||||||
|
#define __DIRUTILS_DIRLIST_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include "queue.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct dirlst {
|
||||||
|
char path[FILENAME_MAX + 1];
|
||||||
|
TAILQ_ENTRY(dirlst) dirs;
|
||||||
|
};
|
||||||
|
TAILQ_HEAD(tq_dirlst, dirlst);
|
||||||
|
|
||||||
|
|
||||||
|
struct tq_dirlst *dirlst_create(const char *, size_t);
|
||||||
|
int dirlst_push(struct tq_dirlst *, const char *, size_t);
|
||||||
|
struct dirlst *dirlst_pop(struct tq_dirlst *);
|
||||||
|
int dirlst_destroy(struct tq_dirlst **);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
* ---------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __DIRUTILS_DIRUTILS_H
|
||||||
|
#define __DIRUTILS_DIRUTILS_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
enum E_EXISTS_STATUS {
|
||||||
|
EXISTS_ERROR,
|
||||||
|
EXISTS_NOENT,
|
||||||
|
EXISTS_NOPERM,
|
||||||
|
EXISTS_DIR,
|
||||||
|
EXISTS_FILE,
|
||||||
|
EXISTS_OTHER
|
||||||
|
};
|
||||||
|
typedef enum E_EXISTS_STATUS EXISTS_STATUS;
|
||||||
|
|
||||||
|
|
||||||
|
typedef int (*dirwalk_action)(const char *);
|
||||||
|
|
||||||
|
|
||||||
|
extern const unsigned char FT_ANY;
|
||||||
|
extern const unsigned char FT_STD;
|
||||||
|
extern const unsigned char FT_NODESCEND;
|
||||||
|
|
||||||
|
|
||||||
|
int makedirs(const char *);
|
||||||
|
int rmdirs(const char *);
|
||||||
|
EXISTS_STATUS path_exists(const char *);
|
||||||
|
int walkdir(const char *, dirwalk_action, unsigned char);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBINIPARSER_INIPARSER_H
|
||||||
|
#define __LIBINIPARSER_INIPARSER_H
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FILE *source;
|
||||||
|
char *lineptr;
|
||||||
|
size_t linelen;
|
||||||
|
ssize_t readlen;
|
||||||
|
} iniparser_file_s;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t is_section;
|
||||||
|
uint8_t is_set;
|
||||||
|
char *name;
|
||||||
|
char *value;
|
||||||
|
} iniparser_line_s;
|
||||||
|
|
||||||
|
|
||||||
|
int iniparser_init(void);
|
||||||
|
void iniparser_destroy(void);
|
||||||
|
int iniparser_open(const char *, iniparser_file_s **);
|
||||||
|
int iniparser_close(iniparser_file_s *);
|
||||||
|
int iniparser_readline(iniparser_file_s *, iniparser_line_s *);
|
||||||
|
void iniparser_line_init(iniparser_line_s *);
|
||||||
|
void iniparser_line_destroy(iniparser_line_s *);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,568 @@
|
||||||
|
/* $OpenBSD: queue.h,v 1.36 2012/04/11 13:29:14 naddy Exp $ */
|
||||||
|
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1991, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SYS_QUEUE_H_
|
||||||
|
#define _SYS_QUEUE_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file defines five types of data structures: singly-linked lists,
|
||||||
|
* lists, simple queues, tail queues, and circular queues.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* A singly-linked list is headed by a single forward pointer. The elements
|
||||||
|
* are singly linked for minimum space and pointer manipulation overhead at
|
||||||
|
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||||
|
* added to the list after an existing element or at the head of the list.
|
||||||
|
* Elements being removed from the head of the list should use the explicit
|
||||||
|
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||||
|
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||||
|
* for applications with large datasets and few or no removals or for
|
||||||
|
* implementing a LIFO queue.
|
||||||
|
*
|
||||||
|
* A list is headed by a single forward pointer (or an array of forward
|
||||||
|
* pointers for a hash table header). The elements are doubly linked
|
||||||
|
* so that an arbitrary element can be removed without a need to
|
||||||
|
* traverse the list. New elements can be added to the list before
|
||||||
|
* or after an existing element or at the head of the list. A list
|
||||||
|
* may only be traversed in the forward direction.
|
||||||
|
*
|
||||||
|
* A simple queue is headed by a pair of pointers, one the head of the
|
||||||
|
* list and the other to the tail of the list. The elements are singly
|
||||||
|
* linked to save space, so elements can only be removed from the
|
||||||
|
* head of the list. New elements can be added to the list before or after
|
||||||
|
* an existing element, at the head of the list, or at the end of the
|
||||||
|
* list. A simple queue may only be traversed in the forward direction.
|
||||||
|
*
|
||||||
|
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||||
|
* list and the other to the tail of the list. The elements are doubly
|
||||||
|
* linked so that an arbitrary element can be removed without a need to
|
||||||
|
* traverse the list. New elements can be added to the list before or
|
||||||
|
* after an existing element, at the head of the list, or at the end of
|
||||||
|
* the list. A tail queue may be traversed in either direction.
|
||||||
|
*
|
||||||
|
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||||
|
* list and the other to the tail of the list. The elements are doubly
|
||||||
|
* linked so that an arbitrary element can be removed without a need to
|
||||||
|
* traverse the list. New elements can be added to the list before or after
|
||||||
|
* an existing element, at the head of the list, or at the end of the list.
|
||||||
|
* A circle queue may be traversed in either direction, but has a more
|
||||||
|
* complex end of list detection.
|
||||||
|
*
|
||||||
|
* For details on the use of these macros, see the queue(3) manual page.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
|
||||||
|
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
|
||||||
|
#else
|
||||||
|
#define _Q_INVALIDATE(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Singly-linked List definitions.
|
||||||
|
*/
|
||||||
|
#define SLIST_HEAD(name, type) \
|
||||||
|
struct name { \
|
||||||
|
struct type *slh_first; /* first element */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SLIST_HEAD_INITIALIZER(head) \
|
||||||
|
{ NULL }
|
||||||
|
|
||||||
|
#define SLIST_ENTRY(type) \
|
||||||
|
struct { \
|
||||||
|
struct type *sle_next; /* next element */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Singly-linked List access methods.
|
||||||
|
*/
|
||||||
|
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||||
|
#define SLIST_END(head) NULL
|
||||||
|
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
|
||||||
|
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||||
|
|
||||||
|
#define SLIST_FOREACH(var, head, field) \
|
||||||
|
for((var) = SLIST_FIRST(head); \
|
||||||
|
(var) != SLIST_END(head); \
|
||||||
|
(var) = SLIST_NEXT(var, field))
|
||||||
|
|
||||||
|
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||||
|
for ((var) = SLIST_FIRST(head); \
|
||||||
|
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
|
||||||
|
(var) = (tvar))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Singly-linked List functions.
|
||||||
|
*/
|
||||||
|
#define SLIST_INIT(head) { \
|
||||||
|
SLIST_FIRST(head) = SLIST_END(head); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||||
|
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||||
|
(slistelm)->field.sle_next = (elm); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||||
|
(elm)->field.sle_next = (head)->slh_first; \
|
||||||
|
(head)->slh_first = (elm); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||||
|
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||||
|
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||||
|
if ((head)->slh_first == (elm)) { \
|
||||||
|
SLIST_REMOVE_HEAD((head), field); \
|
||||||
|
} else { \
|
||||||
|
struct type *curelm = (head)->slh_first; \
|
||||||
|
\
|
||||||
|
while (curelm->field.sle_next != (elm)) \
|
||||||
|
curelm = curelm->field.sle_next; \
|
||||||
|
curelm->field.sle_next = \
|
||||||
|
curelm->field.sle_next->field.sle_next; \
|
||||||
|
_Q_INVALIDATE((elm)->field.sle_next); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List definitions.
|
||||||
|
*/
|
||||||
|
#define LIST_HEAD(name, type) \
|
||||||
|
struct name { \
|
||||||
|
struct type *lh_first; /* first element */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LIST_HEAD_INITIALIZER(head) \
|
||||||
|
{ NULL }
|
||||||
|
|
||||||
|
#define LIST_ENTRY(type) \
|
||||||
|
struct { \
|
||||||
|
struct type *le_next; /* next element */ \
|
||||||
|
struct type **le_prev; /* address of previous next element */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List access methods
|
||||||
|
*/
|
||||||
|
#define LIST_FIRST(head) ((head)->lh_first)
|
||||||
|
#define LIST_END(head) NULL
|
||||||
|
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
|
||||||
|
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||||
|
|
||||||
|
#define LIST_FOREACH(var, head, field) \
|
||||||
|
for((var) = LIST_FIRST(head); \
|
||||||
|
(var)!= LIST_END(head); \
|
||||||
|
(var) = LIST_NEXT(var, field))
|
||||||
|
|
||||||
|
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||||
|
for ((var) = LIST_FIRST(head); \
|
||||||
|
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
|
||||||
|
(var) = (tvar))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List functions.
|
||||||
|
*/
|
||||||
|
#define LIST_INIT(head) do { \
|
||||||
|
LIST_FIRST(head) = LIST_END(head); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||||
|
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||||
|
(listelm)->field.le_next->field.le_prev = \
|
||||||
|
&(elm)->field.le_next; \
|
||||||
|
(listelm)->field.le_next = (elm); \
|
||||||
|
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||||
|
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||||
|
(elm)->field.le_next = (listelm); \
|
||||||
|
*(listelm)->field.le_prev = (elm); \
|
||||||
|
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||||
|
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||||
|
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||||
|
(head)->lh_first = (elm); \
|
||||||
|
(elm)->field.le_prev = &(head)->lh_first; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LIST_REMOVE(elm, field) do { \
|
||||||
|
if ((elm)->field.le_next != NULL) \
|
||||||
|
(elm)->field.le_next->field.le_prev = \
|
||||||
|
(elm)->field.le_prev; \
|
||||||
|
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||||
|
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||||
|
_Q_INVALIDATE((elm)->field.le_next); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LIST_REPLACE(elm, elm2, field) do { \
|
||||||
|
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||||
|
(elm2)->field.le_next->field.le_prev = \
|
||||||
|
&(elm2)->field.le_next; \
|
||||||
|
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||||
|
*(elm2)->field.le_prev = (elm2); \
|
||||||
|
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||||
|
_Q_INVALIDATE((elm)->field.le_next); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple queue definitions.
|
||||||
|
*/
|
||||||
|
#define SIMPLEQ_HEAD(name, type) \
|
||||||
|
struct name { \
|
||||||
|
struct type *sqh_first; /* first element */ \
|
||||||
|
struct type **sqh_last; /* addr of last next element */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||||
|
{ NULL, &(head).sqh_first }
|
||||||
|
|
||||||
|
#define SIMPLEQ_ENTRY(type) \
|
||||||
|
struct { \
|
||||||
|
struct type *sqe_next; /* next element */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple queue access methods.
|
||||||
|
*/
|
||||||
|
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||||
|
#define SIMPLEQ_END(head) NULL
|
||||||
|
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
|
||||||
|
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||||
|
|
||||||
|
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||||
|
for((var) = SIMPLEQ_FIRST(head); \
|
||||||
|
(var) != SIMPLEQ_END(head); \
|
||||||
|
(var) = SIMPLEQ_NEXT(var, field))
|
||||||
|
|
||||||
|
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||||
|
for ((var) = SIMPLEQ_FIRST(head); \
|
||||||
|
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
|
||||||
|
(var) = (tvar))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple queue functions.
|
||||||
|
*/
|
||||||
|
#define SIMPLEQ_INIT(head) do { \
|
||||||
|
(head)->sqh_first = NULL; \
|
||||||
|
(head)->sqh_last = &(head)->sqh_first; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||||
|
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||||
|
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||||
|
(head)->sqh_first = (elm); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||||
|
(elm)->field.sqe_next = NULL; \
|
||||||
|
*(head)->sqh_last = (elm); \
|
||||||
|
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||||
|
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
||||||
|
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||||
|
(listelm)->field.sqe_next = (elm); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||||
|
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||||
|
(head)->sqh_last = &(head)->sqh_first; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
||||||
|
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
|
||||||
|
== NULL) \
|
||||||
|
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tail queue definitions.
|
||||||
|
*/
|
||||||
|
#define TAILQ_HEAD(name, type) \
|
||||||
|
struct name { \
|
||||||
|
struct type *tqh_first; /* first element */ \
|
||||||
|
struct type **tqh_last; /* addr of last next element */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||||
|
{ NULL, &(head).tqh_first }
|
||||||
|
|
||||||
|
#define TAILQ_ENTRY(type) \
|
||||||
|
struct { \
|
||||||
|
struct type *tqe_next; /* next element */ \
|
||||||
|
struct type **tqe_prev; /* address of previous next element */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tail queue access methods
|
||||||
|
*/
|
||||||
|
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||||
|
#define TAILQ_END(head) NULL
|
||||||
|
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||||
|
#define TAILQ_LAST(head, headname) \
|
||||||
|
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||||
|
/* XXX */
|
||||||
|
#define TAILQ_PREV(elm, headname, field) \
|
||||||
|
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||||
|
#define TAILQ_EMPTY(head) \
|
||||||
|
(TAILQ_FIRST(head) == TAILQ_END(head))
|
||||||
|
|
||||||
|
#define TAILQ_FOREACH(var, head, field) \
|
||||||
|
for((var) = TAILQ_FIRST(head); \
|
||||||
|
(var) != TAILQ_END(head); \
|
||||||
|
(var) = TAILQ_NEXT(var, field))
|
||||||
|
|
||||||
|
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||||
|
for ((var) = TAILQ_FIRST(head); \
|
||||||
|
(var) != TAILQ_END(head) && \
|
||||||
|
((tvar) = TAILQ_NEXT(var, field), 1); \
|
||||||
|
(var) = (tvar))
|
||||||
|
|
||||||
|
|
||||||
|
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||||
|
for((var) = TAILQ_LAST(head, headname); \
|
||||||
|
(var) != TAILQ_END(head); \
|
||||||
|
(var) = TAILQ_PREV(var, headname, field))
|
||||||
|
|
||||||
|
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||||
|
for ((var) = TAILQ_LAST(head, headname); \
|
||||||
|
(var) != TAILQ_END(head) && \
|
||||||
|
((tvar) = TAILQ_PREV(var, headname, field), 1); \
|
||||||
|
(var) = (tvar))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tail queue functions.
|
||||||
|
*/
|
||||||
|
#define TAILQ_INIT(head) do { \
|
||||||
|
(head)->tqh_first = NULL; \
|
||||||
|
(head)->tqh_last = &(head)->tqh_first; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||||
|
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||||
|
(head)->tqh_first->field.tqe_prev = \
|
||||||
|
&(elm)->field.tqe_next; \
|
||||||
|
else \
|
||||||
|
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||||
|
(head)->tqh_first = (elm); \
|
||||||
|
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||||
|
(elm)->field.tqe_next = NULL; \
|
||||||
|
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||||
|
*(head)->tqh_last = (elm); \
|
||||||
|
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||||
|
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||||
|
(elm)->field.tqe_next->field.tqe_prev = \
|
||||||
|
&(elm)->field.tqe_next; \
|
||||||
|
else \
|
||||||
|
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||||
|
(listelm)->field.tqe_next = (elm); \
|
||||||
|
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||||
|
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||||
|
(elm)->field.tqe_next = (listelm); \
|
||||||
|
*(listelm)->field.tqe_prev = (elm); \
|
||||||
|
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||||
|
if (((elm)->field.tqe_next) != NULL) \
|
||||||
|
(elm)->field.tqe_next->field.tqe_prev = \
|
||||||
|
(elm)->field.tqe_prev; \
|
||||||
|
else \
|
||||||
|
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||||
|
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||||
|
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||||
|
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
||||||
|
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
|
||||||
|
(elm2)->field.tqe_next->field.tqe_prev = \
|
||||||
|
&(elm2)->field.tqe_next; \
|
||||||
|
else \
|
||||||
|
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
||||||
|
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||||
|
*(elm2)->field.tqe_prev = (elm2); \
|
||||||
|
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||||
|
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Circular queue definitions.
|
||||||
|
*/
|
||||||
|
#define CIRCLEQ_HEAD(name, type) \
|
||||||
|
struct name { \
|
||||||
|
struct type *cqh_first; /* first element */ \
|
||||||
|
struct type *cqh_last; /* last element */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
||||||
|
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
|
||||||
|
|
||||||
|
#define CIRCLEQ_ENTRY(type) \
|
||||||
|
struct { \
|
||||||
|
struct type *cqe_next; /* next element */ \
|
||||||
|
struct type *cqe_prev; /* previous element */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Circular queue access methods
|
||||||
|
*/
|
||||||
|
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
||||||
|
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
||||||
|
#define CIRCLEQ_END(head) ((void *)(head))
|
||||||
|
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
|
||||||
|
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
|
||||||
|
#define CIRCLEQ_EMPTY(head) \
|
||||||
|
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
|
||||||
|
|
||||||
|
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||||
|
for((var) = CIRCLEQ_FIRST(head); \
|
||||||
|
(var) != CIRCLEQ_END(head); \
|
||||||
|
(var) = CIRCLEQ_NEXT(var, field))
|
||||||
|
|
||||||
|
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||||
|
for ((var) = CIRCLEQ_FIRST(head); \
|
||||||
|
(var) != CIRCLEQ_END(head) && \
|
||||||
|
((tvar) = CIRCLEQ_NEXT(var, field), 1); \
|
||||||
|
(var) = (tvar))
|
||||||
|
|
||||||
|
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
||||||
|
for((var) = CIRCLEQ_LAST(head); \
|
||||||
|
(var) != CIRCLEQ_END(head); \
|
||||||
|
(var) = CIRCLEQ_PREV(var, field))
|
||||||
|
|
||||||
|
#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||||
|
for ((var) = CIRCLEQ_LAST(head, headname); \
|
||||||
|
(var) != CIRCLEQ_END(head) && \
|
||||||
|
((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
|
||||||
|
(var) = (tvar))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Circular queue functions.
|
||||||
|
*/
|
||||||
|
#define CIRCLEQ_INIT(head) do { \
|
||||||
|
(head)->cqh_first = CIRCLEQ_END(head); \
|
||||||
|
(head)->cqh_last = CIRCLEQ_END(head); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||||
|
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||||
|
(elm)->field.cqe_prev = (listelm); \
|
||||||
|
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||||
|
(head)->cqh_last = (elm); \
|
||||||
|
else \
|
||||||
|
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||||
|
(listelm)->field.cqe_next = (elm); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||||
|
(elm)->field.cqe_next = (listelm); \
|
||||||
|
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||||
|
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||||
|
(head)->cqh_first = (elm); \
|
||||||
|
else \
|
||||||
|
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||||
|
(listelm)->field.cqe_prev = (elm); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||||
|
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||||
|
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
||||||
|
if ((head)->cqh_last == CIRCLEQ_END(head)) \
|
||||||
|
(head)->cqh_last = (elm); \
|
||||||
|
else \
|
||||||
|
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||||
|
(head)->cqh_first = (elm); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||||
|
(elm)->field.cqe_next = CIRCLEQ_END(head); \
|
||||||
|
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||||
|
if ((head)->cqh_first == CIRCLEQ_END(head)) \
|
||||||
|
(head)->cqh_first = (elm); \
|
||||||
|
else \
|
||||||
|
(head)->cqh_last->field.cqe_next = (elm); \
|
||||||
|
(head)->cqh_last = (elm); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||||
|
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||||
|
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||||
|
else \
|
||||||
|
(elm)->field.cqe_next->field.cqe_prev = \
|
||||||
|
(elm)->field.cqe_prev; \
|
||||||
|
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||||
|
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||||
|
else \
|
||||||
|
(elm)->field.cqe_prev->field.cqe_next = \
|
||||||
|
(elm)->field.cqe_next; \
|
||||||
|
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||||
|
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
|
||||||
|
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
|
||||||
|
CIRCLEQ_END(head)) \
|
||||||
|
(head).cqh_last = (elm2); \
|
||||||
|
else \
|
||||||
|
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
|
||||||
|
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
|
||||||
|
CIRCLEQ_END(head)) \
|
||||||
|
(head).cqh_first = (elm2); \
|
||||||
|
else \
|
||||||
|
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
|
||||||
|
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||||
|
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif /* !_SYS_QUEUE_H_ */
|
|
@ -7,7 +7,9 @@
|
||||||
#ifndef KTE_DEFS_H
|
#ifndef KTE_DEFS_H
|
||||||
#define KTE_DEFS_H
|
#define KTE_DEFS_H
|
||||||
|
|
||||||
|
|
||||||
#include <sys/queue.h>
|
#include <sys/queue.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
|
||||||
#define KTE_TAB_STOP 8
|
#define KTE_TAB_STOP 8
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
BINS := srm
|
||||||
|
EDS := ke kte
|
||||||
|
LIBS := libdirutils.a libiniparser.a
|
||||||
|
|
||||||
|
LDFLAGS :=
|
||||||
|
CFLAGS := -pedantic -Wall -Werror -Wextra -O0 -std=c99 -g -I../include/
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(BINS) $(EDS) $(LIBS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -f $(BINS) $(EDS) $(LIBS) *.o *.core
|
||||||
|
|
||||||
|
|
||||||
|
ke kte:
|
||||||
|
cd ../$@ && make && cp $@ ../src
|
||||||
|
|
||||||
|
srm: srm.c
|
||||||
|
$(CC) $(CFLAGS) -o $@ srm.c
|
||||||
|
|
||||||
|
libiniparser.a: iniparser.o
|
||||||
|
$(AR) -rcs $@ iniparser.o
|
||||||
|
|
||||||
|
iniparser-test: iniparser_test.c libiniparser.a
|
||||||
|
$(CC) $(CFLAGS) -o $@ iniparser_test.c libiniparser.a
|
||||||
|
|
||||||
|
libdirutils.a: dirlist.o dirutils.o dirwalk.o
|
||||||
|
$(AR) -rcs $@ dirlist.o dirutils.o dirwalk.o
|
||||||
|
|
||||||
|
dirlist-test: dirlist_test.c libdirutils.a
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ dirlist_test.c libdirutils.a
|
||||||
|
|
||||||
|
dirutils-test: dirutils_test.c libdirutils.a
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ dirutils_test.c libdirutils.a
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
* ---------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "kst/dirlist.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create and initialise the directory list.
|
||||||
|
*/
|
||||||
|
struct tq_dirlst
|
||||||
|
*dirlst_create(const char *init_path, size_t init_path_sz)
|
||||||
|
{
|
||||||
|
struct tq_dirlst *path_lst;
|
||||||
|
struct dirlst *elm;
|
||||||
|
|
||||||
|
if (init_path_sz > FILENAME_MAX) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
path_lst = calloc(1, sizeof(struct tq_dirlst));
|
||||||
|
if (NULL == path_lst) {
|
||||||
|
return path_lst;
|
||||||
|
}
|
||||||
|
|
||||||
|
elm = calloc(1, sizeof(struct dirlst));
|
||||||
|
if (NULL == elm) {
|
||||||
|
free(path_lst);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy(elm->path, init_path, init_path_sz);
|
||||||
|
|
||||||
|
TAILQ_INIT(path_lst);
|
||||||
|
TAILQ_INSERT_HEAD(path_lst, elm, dirs);
|
||||||
|
|
||||||
|
return path_lst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* add a new directory to the list.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
dirlst_push(struct tq_dirlst *lst, const char *new_dir, size_t new_dir_sz)
|
||||||
|
{
|
||||||
|
struct dirlst *elm;
|
||||||
|
|
||||||
|
if (new_dir_sz > FILENAME_MAX) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
elm = calloc(1, sizeof(struct dirlst));
|
||||||
|
if (NULL == elm) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(elm->path, new_dir, FILENAME_MAX);
|
||||||
|
TAILQ_INSERT_HEAD(lst, elm, dirs);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* remove the first directory in the list and return it
|
||||||
|
*/
|
||||||
|
struct dirlst
|
||||||
|
*dirlst_pop(struct tq_dirlst *lst)
|
||||||
|
{
|
||||||
|
struct dirlst *elm;
|
||||||
|
|
||||||
|
if (TAILQ_EMPTY(lst)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
elm = TAILQ_FIRST(lst);
|
||||||
|
TAILQ_REMOVE(lst, elm, dirs);
|
||||||
|
return elm;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
dirlst_destroy(struct tq_dirlst **lstp)
|
||||||
|
{
|
||||||
|
struct dirlst *elm;
|
||||||
|
|
||||||
|
while ((elm = TAILQ_FIRST(*lstp))) {
|
||||||
|
TAILQ_REMOVE(*lstp, elm, dirs);
|
||||||
|
free(elm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TAILQ_EMPTY(*lstp)) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(*lstp);
|
||||||
|
*lstp = NULL;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
* ---------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <CUnit/CUnit.h>
|
||||||
|
#include <CUnit/Basic.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sysexits.h>
|
||||||
|
|
||||||
|
#include <dirlist.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* helper that, given a list and the expected key, determines whether
|
||||||
|
* the the popped path matches. Return values use if semantics: success
|
||||||
|
* is 1, failure is 0. It is up to the caller to ensure that keylen is
|
||||||
|
* in the acceptable range (FILENAME_MAX) for tests that are to be
|
||||||
|
* successful.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
test_pop_helper(struct tq_dirlst *lst, const char *key, size_t keylen)
|
||||||
|
{
|
||||||
|
struct dirlst *elm;
|
||||||
|
int match;
|
||||||
|
|
||||||
|
if (keylen > FILENAME_MAX)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
elm = dirlst_pop(lst);
|
||||||
|
if (NULL == elm)
|
||||||
|
return 0;
|
||||||
|
match = strncmp(elm->path, key, keylen);
|
||||||
|
free(elm);
|
||||||
|
return 0 == match;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
test_dirlst_create(void)
|
||||||
|
{
|
||||||
|
struct tq_dirlst *lst;
|
||||||
|
|
||||||
|
lst = dirlst_create((const char *)"foo", 3);
|
||||||
|
CU_ASSERT(lst != NULL);
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == dirlst_destroy(&lst));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_dirlst_single(void)
|
||||||
|
{
|
||||||
|
struct tq_dirlst *lst;
|
||||||
|
struct dirlst *elm;
|
||||||
|
int n_elms, ret;
|
||||||
|
|
||||||
|
lst = dirlst_create((const char *)"foo", 3);
|
||||||
|
ret = dirlst_push(lst, (const char *)"bar", 3);
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == ret);
|
||||||
|
n_elms = 0;
|
||||||
|
TAILQ_FOREACH(elm, lst, dirs)
|
||||||
|
n_elms++;
|
||||||
|
|
||||||
|
CU_ASSERT(2 == n_elms);
|
||||||
|
|
||||||
|
CU_ASSERT(test_pop_helper(lst, "bar", 3));
|
||||||
|
n_elms = 0;
|
||||||
|
TAILQ_FOREACH(elm, lst, dirs)
|
||||||
|
n_elms++;
|
||||||
|
|
||||||
|
CU_ASSERT(1 == n_elms);
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == dirlst_destroy(&lst));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ensure an invalid path_len aborts the push
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
test_dirlst_bad(void)
|
||||||
|
{
|
||||||
|
struct tq_dirlst *lst;
|
||||||
|
|
||||||
|
lst = dirlst_create((const char *)"foo", 3);
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == dirlst_push(lst, "bar", 3));
|
||||||
|
CU_ASSERT(EXIT_FAILURE == dirlst_push(lst, "baz", FILENAME_MAX * 2));
|
||||||
|
CU_ASSERT(test_pop_helper(lst, "bar", 3));
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == dirlst_destroy(&lst));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
test_dirlst_multi(void)
|
||||||
|
{
|
||||||
|
char testkeys[][4] = {"bar", "baz", "quux", ""};
|
||||||
|
int testkeylen[4];
|
||||||
|
struct tq_dirlst *lst;
|
||||||
|
struct dirlst *elm;
|
||||||
|
int i, n_elms, ret;
|
||||||
|
|
||||||
|
lst = dirlst_create((const char *)"foo", 3);
|
||||||
|
for(i = 0; i < 3; ++i) {
|
||||||
|
testkeylen[i] = strlen(testkeys[i]);
|
||||||
|
ret = dirlst_push(lst, testkeys[i], testkeylen[i]);
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
n_elms = 0;
|
||||||
|
TAILQ_FOREACH(elm, lst, dirs)
|
||||||
|
n_elms++;
|
||||||
|
CU_ASSERT(4 == n_elms);
|
||||||
|
|
||||||
|
for (i = 2; i >= 0; i--)
|
||||||
|
CU_ASSERT(test_pop_helper(lst, testkeys[i], testkeylen[i]));
|
||||||
|
CU_ASSERT(test_pop_helper(lst, "foo", 3));
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == dirlst_destroy(&lst));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stubs required by the test suite, but for which no functionality is
|
||||||
|
* required in this code. init_test is called each time a test is run,
|
||||||
|
* and cleanup is run after every test.
|
||||||
|
*/
|
||||||
|
int init_test(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cleanup_test(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fireball is the code called when adding test fails: cleanup the test
|
||||||
|
* registry and exit.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
fireball(void)
|
||||||
|
{
|
||||||
|
CU_cleanup_registry();
|
||||||
|
exit(CU_get_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The main function sets up the test suite, registers the test cases,
|
||||||
|
* runs through them, and hopefully doesn't explode.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
CU_pSuite tsuite = NULL;
|
||||||
|
unsigned int fails;
|
||||||
|
|
||||||
|
printf("\n\n[+] running tests for dirutils\n");
|
||||||
|
|
||||||
|
if (!(CUE_SUCCESS == CU_initialize_registry())) {
|
||||||
|
errx(EX_CONFIG, "failed to initialise test registry");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
tsuite = CU_add_suite(TEST_SUITE, init_test, cleanup_test);
|
||||||
|
if (NULL == tsuite)
|
||||||
|
fireball();
|
||||||
|
|
||||||
|
if (NULL == CU_add_test(tsuite, "dirlst create", test_dirlst_create))
|
||||||
|
fireball();
|
||||||
|
|
||||||
|
if (NULL == CU_add_test(tsuite, "single push/pop", test_dirlst_single))
|
||||||
|
fireball();
|
||||||
|
|
||||||
|
if (NULL == CU_add_test(tsuite, "multi push/pop", test_dirlst_multi))
|
||||||
|
fireball();
|
||||||
|
|
||||||
|
CU_basic_set_mode(CU_BRM_VERBOSE);
|
||||||
|
CU_basic_run_tests();
|
||||||
|
fails = CU_get_number_of_tests_failed();
|
||||||
|
warnx("%u tests failed", fails);
|
||||||
|
|
||||||
|
CU_cleanup_registry();
|
||||||
|
return fails;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an empty test provided for reference.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
empty_test()
|
||||||
|
{
|
||||||
|
CU_ASSERT(1 == 0);
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
* ---------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "kst/dirutils.h"
|
||||||
|
#include "kst/dirlist.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int _parent_exists(const char *);
|
||||||
|
static int _rmdirs(const char *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determines whether a directory exists.
|
||||||
|
*/
|
||||||
|
EXISTS_STATUS
|
||||||
|
path_exists(const char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = stat(path, &st);
|
||||||
|
if (rv == -1) {
|
||||||
|
switch (errno) {
|
||||||
|
case EACCES:
|
||||||
|
return EXISTS_NOPERM;
|
||||||
|
break;
|
||||||
|
case ENOENT:
|
||||||
|
return EXISTS_NOENT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return EXISTS_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.st_mode & S_IFDIR) {
|
||||||
|
return EXISTS_DIR;
|
||||||
|
}
|
||||||
|
else if (st.st_mode & S_IFREG) {
|
||||||
|
return EXISTS_FILE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return EXISTS_OTHER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create a path and any directories above it required
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
makedirs(const char *path)
|
||||||
|
{
|
||||||
|
struct tq_dirlst *lst;
|
||||||
|
struct dirlst *elm;
|
||||||
|
size_t path_sz;
|
||||||
|
char *dnam_p, *curpath;
|
||||||
|
|
||||||
|
path_sz = strlen(path);
|
||||||
|
lst = dirlst_create(path, path_sz);
|
||||||
|
if (NULL == lst)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
curpath = strdup(path);
|
||||||
|
while (!_parent_exists(curpath)) {
|
||||||
|
dnam_p = dirname(curpath);
|
||||||
|
curpath = strdup(dnam_p);
|
||||||
|
dirlst_push(lst, curpath, strlen(curpath));
|
||||||
|
}
|
||||||
|
free(curpath);
|
||||||
|
|
||||||
|
while (NULL != (elm = dirlst_pop(lst))) {
|
||||||
|
if (-1 == mkdir(elm->path, 0777)) {
|
||||||
|
free(elm);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
free(elm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirlst_destroy(&lst);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* remove a directory and any subdirectories
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
rmdirs(const char *path)
|
||||||
|
{
|
||||||
|
return _rmdirs(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine whether the parent directory exists, and return true if it
|
||||||
|
* does.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
_parent_exists(const char *path)
|
||||||
|
{
|
||||||
|
char *name_buf;
|
||||||
|
char *dnam_p;
|
||||||
|
|
||||||
|
name_buf = strdup(path);
|
||||||
|
|
||||||
|
dnam_p = dirname(name_buf);
|
||||||
|
if (EXISTS_DIR != path_exists(dnam_p)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* remove a directory, all files in it, and all subdirectories.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
_rmdirs(const char *path)
|
||||||
|
{
|
||||||
|
char child[FILENAME_MAX + 1];
|
||||||
|
struct dirent *dp;
|
||||||
|
DIR *dirp;
|
||||||
|
int fail;
|
||||||
|
|
||||||
|
if (NULL == (dirp = opendir(path))) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (NULL != (dp = readdir(dirp))) {
|
||||||
|
if (0 == strncmp("..", dp->d_name, 3)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == strncmp(".", dp->d_name, 2)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
snprintf(child, FILENAME_MAX, "%s/%s", path, dp->d_name);
|
||||||
|
if (DT_DIR == dp->d_type) {
|
||||||
|
fail = _rmdirs(child);
|
||||||
|
if (EXIT_FAILURE == fail) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fail = unlink(child);
|
||||||
|
if (-1 == fail) {
|
||||||
|
fail = EXIT_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-1 == closedir(dirp))
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
if (EXIT_FAILURE == fail) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
else if (-1 == rmdir(path)) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
* ---------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <CUnit/CUnit.h>
|
||||||
|
#include <CUnit/Basic.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sysexits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <dirutils.h>
|
||||||
|
|
||||||
|
static int test_write_file_helper(const char *, const char *);
|
||||||
|
static int test_touch_file_helper(const char *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* test the use of the exists function
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
test_exists(void)
|
||||||
|
{
|
||||||
|
char testdir[] = "testdata/testdir";
|
||||||
|
char testfil[] = "testdata/testfile";
|
||||||
|
char testnot[] = "testdata/nosuchfile";
|
||||||
|
EXISTS_STATUS ftype;
|
||||||
|
|
||||||
|
ftype = path_exists(testdir);
|
||||||
|
CU_ASSERT(EXISTS_DIR == ftype);
|
||||||
|
|
||||||
|
ftype = path_exists(testfil);
|
||||||
|
CU_ASSERT(EXISTS_FILE == ftype);
|
||||||
|
|
||||||
|
ftype = path_exists(testnot);
|
||||||
|
CU_ASSERT(EXISTS_NOENT == ftype);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_makedirs(void)
|
||||||
|
{
|
||||||
|
char testpath[] = "testdata/foo/bar/baz\0";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* use the system to ensure we have a clean slate for this test
|
||||||
|
*/
|
||||||
|
if (EXISTS_DIR == path_exists(testpath))
|
||||||
|
system("rm -fr testdata/foo/");
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == makedirs(testpath));
|
||||||
|
CU_ASSERT(EXISTS_DIR == path_exists(testpath));
|
||||||
|
/*
|
||||||
|
* we can't guarantee rmdirs yet; this ensures a clean slate.
|
||||||
|
*/
|
||||||
|
system("rm -r testdata/foo/");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_empty_rmdirs(void)
|
||||||
|
{
|
||||||
|
char testpath[20] = "testdata/foo";
|
||||||
|
char cmd[FILENAME_MAX];
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
memset(cmd, 0x0, FILENAME_MAX);
|
||||||
|
snprintf(cmd, FILENAME_MAX, "mkdir -p %s/bar/baz", testpath);
|
||||||
|
system(cmd);
|
||||||
|
rv = rmdirs(testpath);
|
||||||
|
if (EXIT_FAILURE == rv) {
|
||||||
|
printf("\n");
|
||||||
|
warn("rmdirs");
|
||||||
|
system("rm -fr testdata/foo/");
|
||||||
|
}
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == rv);
|
||||||
|
CU_ASSERT(EXISTS_NOENT == path_exists(testpath));
|
||||||
|
/*
|
||||||
|
* we can't guarantee rmdirs yet; this ensures a clean slate.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
test_rmdirs_simple(void)
|
||||||
|
{
|
||||||
|
char testpath[] = "testdata/foo";
|
||||||
|
char cmd[FILENAME_MAX];
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
memset(cmd, 0x0, FILENAME_MAX);
|
||||||
|
snprintf(cmd, FILENAME_MAX, "mkdir -p %s/bar/baz", testpath);
|
||||||
|
system(cmd);
|
||||||
|
memset(cmd, 0x0, FILENAME_MAX);
|
||||||
|
snprintf(cmd, FILENAME_MAX, "touch %s/bar/quux", testpath);
|
||||||
|
system(cmd);
|
||||||
|
rv = rmdirs(testpath);
|
||||||
|
if (EXIT_FAILURE == rv) {
|
||||||
|
printf("\n");
|
||||||
|
warn("rmdirs");
|
||||||
|
/*
|
||||||
|
* we can't guarantee rmdirs yet; this ensures a clean slate.
|
||||||
|
*/
|
||||||
|
system("rm -r testdata/foo/ 2>/dev/null");
|
||||||
|
}
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == rv);
|
||||||
|
CU_ASSERT(EXISTS_NOENT == path_exists(testpath));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
test_dirutils(void)
|
||||||
|
{
|
||||||
|
char testpath[] = "testdata/dirutils";
|
||||||
|
char tmp_path[FILENAME_MAX + 1];
|
||||||
|
char lnk_path[FILENAME_MAX + 1];
|
||||||
|
|
||||||
|
/* set up directory structure */
|
||||||
|
CU_ASSERT(EXISTS_NOENT == path_exists(testpath));
|
||||||
|
snprintf(tmp_path, FILENAME_MAX, "%s/foo/bar", testpath);
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == makedirs(tmp_path));
|
||||||
|
CU_ASSERT(EXISTS_DIR == path_exists(tmp_path));
|
||||||
|
snprintf(tmp_path, FILENAME_MAX, "%s/foo/baz", testpath);
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == makedirs(tmp_path));
|
||||||
|
CU_ASSERT(EXISTS_DIR == path_exists(tmp_path));
|
||||||
|
|
||||||
|
/* add a few files */
|
||||||
|
snprintf(tmp_path, FILENAME_MAX, "%s/foo/quux", testpath);
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == test_touch_file_helper(tmp_path));
|
||||||
|
CU_ASSERT(EXISTS_FILE == path_exists(tmp_path));
|
||||||
|
snprintf(tmp_path, FILENAME_MAX, "%s/foo/bar/xyzzy", testpath);
|
||||||
|
snprintf(lnk_path, FILENAME_MAX, "%s/foo/baz/xyzzy", testpath);
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == test_write_file_helper(tmp_path,
|
||||||
|
"some data should go here"));
|
||||||
|
CU_ASSERT(EXISTS_FILE == path_exists(tmp_path));
|
||||||
|
CU_ASSERT(-1 != link(tmp_path, lnk_path));
|
||||||
|
CU_ASSERT(EXISTS_FILE == path_exists(lnk_path));
|
||||||
|
snprintf(tmp_path, FILENAME_MAX, "%s/foo/bar/thud", testpath);
|
||||||
|
snprintf(lnk_path, FILENAME_MAX, "%s/foo/baz/fred", testpath);
|
||||||
|
CU_ASSERT(EXIT_SUCCESS == test_write_file_helper(tmp_path,
|
||||||
|
"we want something for the link"));
|
||||||
|
CU_ASSERT(EXISTS_FILE == path_exists(tmp_path));
|
||||||
|
CU_ASSERT(-1 != symlink(tmp_path, lnk_path));
|
||||||
|
|
||||||
|
CU_ASSERT_FATAL(EXIT_SUCCESS == rmdirs(testpath));
|
||||||
|
CU_ASSERT_FATAL(EXISTS_NOENT == path_exists(testpath));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* utility function to touch a file
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test_write_file_helper(const char *path, const char *data)
|
||||||
|
{
|
||||||
|
ssize_t wrsz;
|
||||||
|
size_t data_len;
|
||||||
|
int fail, fd;
|
||||||
|
|
||||||
|
fail = EXIT_SUCCESS;
|
||||||
|
data_len = strlen(data);
|
||||||
|
fd = open(path, O_WRONLY|O_CREAT, S_IRUSR| S_IWUSR);
|
||||||
|
if (-1 == fd)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
wrsz = write(fd, data, data_len);
|
||||||
|
if (wrsz != data_len)
|
||||||
|
fail = EXIT_FAILURE;
|
||||||
|
if (-1 == close(fd))
|
||||||
|
fail = EXIT_FAILURE;
|
||||||
|
return fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
test_touch_file_helper(const char *path)
|
||||||
|
{
|
||||||
|
return test_write_file_helper(path, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stubs required by the test suite, but for which no functionality is
|
||||||
|
* required in this code. init_test is called each time a test is run,
|
||||||
|
* and cleanup is run after every test.
|
||||||
|
*/
|
||||||
|
int init_test(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cleanup_test(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fireball is the code called when adding test fails: cleanup the test
|
||||||
|
* registry and exit.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
fireball(void)
|
||||||
|
{
|
||||||
|
CU_cleanup_registry();
|
||||||
|
exit(CU_get_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The main function sets up the test suite, registers the test cases,
|
||||||
|
* runs through them, and hopefully doesn't explode.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
CU_pSuite tsuite = NULL;
|
||||||
|
unsigned int fails;
|
||||||
|
|
||||||
|
printf("\n\n[+] running tests for dirutils\n");
|
||||||
|
|
||||||
|
if (!(CUE_SUCCESS == CU_initialize_registry())) {
|
||||||
|
errx(EX_CONFIG, "failed to initialise test registry");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
tsuite = CU_add_suite(TEST_SUITE, init_test, cleanup_test);
|
||||||
|
if (NULL == tsuite)
|
||||||
|
fireball();
|
||||||
|
|
||||||
|
if (NULL == CU_add_test(tsuite, "path_exists", test_exists))
|
||||||
|
fireball();
|
||||||
|
|
||||||
|
if (NULL == CU_add_test(tsuite, "makedirs simple", test_makedirs))
|
||||||
|
fireball();
|
||||||
|
|
||||||
|
if (NULL == CU_add_test(tsuite, "empty dir rmdirs", test_empty_rmdirs))
|
||||||
|
fireball();
|
||||||
|
|
||||||
|
if (NULL == CU_add_test(tsuite, "simple rmdirs", test_rmdirs_simple))
|
||||||
|
fireball();
|
||||||
|
|
||||||
|
if (NULL == CU_add_test(tsuite, "full test", test_dirutils))
|
||||||
|
fireball();
|
||||||
|
|
||||||
|
CU_basic_set_mode(CU_BRM_VERBOSE);
|
||||||
|
CU_basic_run_tests();
|
||||||
|
fails = CU_get_number_of_tests_failed();
|
||||||
|
warnx("%u tests failed", fails);
|
||||||
|
|
||||||
|
CU_cleanup_registry();
|
||||||
|
return fails;
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
* ---------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "kst/dirutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
const unsigned char FT_ANY = DT_BLK|DT_CHR|DT_DIR|DT_FIFO|DT_LNK|DT_REG|
|
||||||
|
DT_SOCK|DT_UNKNOWN;
|
||||||
|
const unsigned char FT_STD = DT_REG|DT_DIR;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The highest that the DT_* constants are seems to be around 14, so 0x80
|
||||||
|
* seems like a sane choice.
|
||||||
|
*/
|
||||||
|
const unsigned char FT_NODESCEND = 0x80;
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
walkpath(const char *root, dirwalk_action action, unsigned char mask)
|
||||||
|
{
|
||||||
|
char child[FILENAME_MAX+1];
|
||||||
|
struct dirent *ent;
|
||||||
|
DIR *dh;
|
||||||
|
|
||||||
|
if (NULL == (dh = opendir(root))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (NULL != (ent = readdir(dh))) {
|
||||||
|
if (0 == strncmp("..", ent->d_name, 3)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == strncmp(".", ent->d_name, 2)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(child, FILENAME_MAX, "%s/%s", root, ent->d_name);
|
||||||
|
if (mask | ent->d_type) {
|
||||||
|
if (-1 == action(child)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (DT_DIR == ent->d_type && (!(FT_NODESCEND | mask))) {
|
||||||
|
if (-1 == walkpath(child, action, mask)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-1 == closedir(dh)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a valid readdir(3) type from a mode.
|
||||||
|
*/
|
||||||
|
static unsigned char
|
||||||
|
stat_mode_to_dt_type(mode_t mode)
|
||||||
|
{
|
||||||
|
if (S_ISDIR(mode)) {
|
||||||
|
return DT_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISREG(mode)) {
|
||||||
|
return DT_REG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISCHR(mode)) {
|
||||||
|
return DT_CHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISBLK(mode)) {
|
||||||
|
return DT_BLK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISFIFO(mode)) {
|
||||||
|
return DT_FIFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISLNK(mode)) {
|
||||||
|
return DT_LNK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISSOCK(mode)) {
|
||||||
|
return DT_SOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* walkdir goes through every directory entry under root and calls action on
|
||||||
|
* every entry matching the mask.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
walkdir(const char *root, dirwalk_action action, unsigned char mask)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
char *rootdup = NULL;
|
||||||
|
size_t rootlen;
|
||||||
|
int ret = -1;
|
||||||
|
unsigned char type;
|
||||||
|
|
||||||
|
if (-1 == stat(root, &st)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = stat_mode_to_dt_type(st.st_mode);
|
||||||
|
if (mask | type) {
|
||||||
|
if (0 != (ret = action(root))) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DT_DIR == type) {
|
||||||
|
rootlen = strnlen(root, FILENAME_MAX);
|
||||||
|
rootdup = strndup(root, FILENAME_MAX);
|
||||||
|
if (NULL == rootdup) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('/' == rootdup[rootlen-1]) {
|
||||||
|
rootdup[rootlen-1] = 0;
|
||||||
|
}
|
||||||
|
ret = walkpath(rootdup, action, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(rootdup);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,280 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "kst/iniparser.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define SECTION_STRING "^\\[ *([a-zA-Z0-9_-]+) *\\]$"
|
||||||
|
#define SKIPLINE_STRING "^[ \t]*$"
|
||||||
|
#define COMMENT_STRING "^[ \t]*[#;]"
|
||||||
|
#define KEYVALUE_STRING "^[ \t]*([a-zA-Z0-9_-]+)[ \t]*=(.*)$"
|
||||||
|
|
||||||
|
|
||||||
|
static regex_t section_regex;
|
||||||
|
static regex_t skipline_regex;
|
||||||
|
static regex_t comment_regex;
|
||||||
|
static regex_t keyvalue_regex;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
iniparser_line_init(iniparser_line_s *line)
|
||||||
|
{
|
||||||
|
line->is_section = 0;
|
||||||
|
line->name = NULL;
|
||||||
|
line->value = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
iniparser_line_destroy(iniparser_line_s *line)
|
||||||
|
{
|
||||||
|
free(line->name);
|
||||||
|
free(line->value);
|
||||||
|
line->name = NULL;
|
||||||
|
line->value = NULL;
|
||||||
|
line->is_section = 0;
|
||||||
|
line->is_set = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
iniparser_open(const char *path, iniparser_file_s **file)
|
||||||
|
{
|
||||||
|
iniparser_file_s *f = NULL;
|
||||||
|
int allocated = 0;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If file is NULL, we can allocate space for it.
|
||||||
|
*/
|
||||||
|
if (NULL == file) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = *file;
|
||||||
|
if (NULL == f) {
|
||||||
|
if (NULL == (f = calloc(1, sizeof(*f)))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
allocated = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->source = fopen(path, "r");
|
||||||
|
if (NULL == f->source) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->lineptr = NULL;
|
||||||
|
f->linelen = 0;
|
||||||
|
f->readlen = 0;
|
||||||
|
*file = f;
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (allocated && ret) {
|
||||||
|
free(f);
|
||||||
|
*file = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
iniparser_close(iniparser_file_s *file)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (NULL != file->lineptr) {
|
||||||
|
free(file->lineptr);
|
||||||
|
file->lineptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
file->linelen = 0;
|
||||||
|
file->readlen = 0;
|
||||||
|
ret = fclose(file->source);
|
||||||
|
file->source = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
extract_match(const char *line, regmatch_t *pmatch)
|
||||||
|
{
|
||||||
|
ssize_t len;
|
||||||
|
regoff_t eo = pmatch->rm_eo;
|
||||||
|
regoff_t so = pmatch->rm_so;
|
||||||
|
char *s = NULL;
|
||||||
|
|
||||||
|
/* Technically, these are signed. They should be checked. */
|
||||||
|
if ((pmatch->rm_eo < 0) || (pmatch->rm_eo < 0)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* trim space from the end. */
|
||||||
|
while ((eo > pmatch->rm_so) && (0x20 == line[eo-1])) {
|
||||||
|
eo--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* trim space from the beginning. */
|
||||||
|
while ((so < eo) && (0x20 == line[so])) {
|
||||||
|
so++;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = eo - so;
|
||||||
|
|
||||||
|
if (len <= 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL == (s = calloc((size_t)len+1, sizeof(*s)))) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(s, line + so, (size_t)len);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
iniparser_readline(iniparser_file_s *file, iniparser_line_s *line)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The longest matching regex has two matches.
|
||||||
|
*/
|
||||||
|
regmatch_t pmatch[3];
|
||||||
|
|
||||||
|
iniparser_line_destroy(line);
|
||||||
|
if (NULL != file->lineptr) {
|
||||||
|
free(file->lineptr);
|
||||||
|
file->lineptr = NULL;
|
||||||
|
file->linelen = 0;
|
||||||
|
file->readlen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
file->readlen = getline(&(file->lineptr), &(file->linelen),
|
||||||
|
file->source);
|
||||||
|
if (file->readlen < 1) {
|
||||||
|
if (0 != feof(file->source)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0xa == file->lineptr[file->readlen-1]) {
|
||||||
|
file->lineptr[file->readlen-1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (REG_NOMATCH != regexec(§ion_regex, file->lineptr, 2, pmatch, 0)) {
|
||||||
|
line->is_section = 1;
|
||||||
|
line->name = extract_match(file->lineptr, &pmatch[1]);
|
||||||
|
if (NULL == line->name) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
line->is_set = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If a skipline or comment is detected, skip to the next line. */
|
||||||
|
if (REG_NOMATCH != regexec(&skipline_regex, file->lineptr,
|
||||||
|
0, NULL, 0)) {
|
||||||
|
return iniparser_readline(file, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (REG_NOMATCH != regexec(&comment_regex, file->lineptr,
|
||||||
|
0, NULL, 0)) {
|
||||||
|
|
||||||
|
return iniparser_readline(file, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to parse a key-value pair. */
|
||||||
|
if (REG_NOMATCH != regexec(&keyvalue_regex, file->lineptr,
|
||||||
|
3, pmatch, 0)) {
|
||||||
|
line->is_section = 0;
|
||||||
|
line->name = extract_match(file->lineptr, &pmatch[1]);
|
||||||
|
line->value = extract_match(file->lineptr, &pmatch[2]);
|
||||||
|
if (NULL == line->name) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
line->is_set = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reaching this point means there's an invalid line in the file. */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
iniparser_destroy()
|
||||||
|
{
|
||||||
|
regfree(§ion_regex);
|
||||||
|
regfree(&skipline_regex);
|
||||||
|
regfree(&comment_regex);
|
||||||
|
regfree(&keyvalue_regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
iniparser_init()
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
ret = regcomp(§ion_regex, SECTION_STRING, REG_EXTENDED);
|
||||||
|
if (0 != ret) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regcomp(&skipline_regex, SKIPLINE_STRING, REG_NOSUB|REG_EXTENDED);
|
||||||
|
if (0 != ret) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regcomp(&comment_regex, COMMENT_STRING, REG_NOSUB|REG_EXTENDED);
|
||||||
|
if (0 != ret) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regcomp(&keyvalue_regex, KEYVALUE_STRING, REG_EXTENDED);
|
||||||
|
if (0 != ret) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (ret) {
|
||||||
|
iniparser_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "kst/iniparser.h"
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
iniparser_file_s *file = NULL;
|
||||||
|
iniparser_line_s line;
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = iniparser_init();
|
||||||
|
if (0 != ret) {
|
||||||
|
fprintf(stderr, "init failed: %d\n", ret);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
printf("Processing %s\n", argv[i]);
|
||||||
|
ret = iniparser_open(argv[i], &file);
|
||||||
|
if (0 != ret) {
|
||||||
|
perror("_open");
|
||||||
|
fprintf(stderr, "retval: %d\n", ret);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
iniparser_line_init(&line);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
ret = iniparser_readline(file, &line);
|
||||||
|
/* -1 is returned on error. */
|
||||||
|
if (-1 == ret) {
|
||||||
|
perror("_readline");
|
||||||
|
fprintf(stderr, "retval: %d\n", ret);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
/* 1 means EOF. */
|
||||||
|
else if (1 == ret) {
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.is_section) {
|
||||||
|
printf("Now in section '%s'\n", line.name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Read key '%s' with value '%s'\n",
|
||||||
|
line.name, line.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
iniparser_line_destroy(&line);
|
||||||
|
}
|
||||||
|
|
||||||
|
iniparser_close(file);
|
||||||
|
free(file);
|
||||||
|
file = NULL;
|
||||||
|
iniparser_line_destroy(&line);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
iniparser_line_destroy(&line);
|
||||||
|
if (argc > 0) {
|
||||||
|
iniparser_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret==0;
|
||||||
|
}
|
|
@ -0,0 +1,370 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, 2012 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sysexits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define DEFAULT_PASSES 3
|
||||||
|
#define DEV_RANDOM "/dev/urandom"
|
||||||
|
#define MAX_CHUNK 4096
|
||||||
|
#define SRM_VERSION "1.3.1"
|
||||||
|
|
||||||
|
|
||||||
|
static int do_wipe(char *, size_t, int);
|
||||||
|
static int wipe(char *);
|
||||||
|
static int rmdirs(const char *, size_t);
|
||||||
|
static void usage(void);
|
||||||
|
static void version(void);
|
||||||
|
|
||||||
|
extern char *__progname;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* overwrite a file with one or more passes of random data, then unlink it.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
size_t passes, tmp_passes, wiped, failed, i;
|
||||||
|
char **file_ptr, **wipe_success, **wipe_fail;
|
||||||
|
int retval, opt, verbose, recur;
|
||||||
|
|
||||||
|
passes = DEFAULT_PASSES;
|
||||||
|
retval = EX_DATAERR;
|
||||||
|
wiped = 0;
|
||||||
|
failed = 0;
|
||||||
|
verbose = 0;
|
||||||
|
recur = 0;
|
||||||
|
|
||||||
|
if (argc == 1)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
while (-1 != (opt = getopt(argc, argv, "hn:rvV"))) {
|
||||||
|
switch( opt ) {
|
||||||
|
case 'h':
|
||||||
|
usage();
|
||||||
|
break; /* don't technically need it but meh */
|
||||||
|
case 'n':
|
||||||
|
tmp_passes = atoi(optarg);
|
||||||
|
passes = tmp_passes > 0 ? tmp_passes : passes;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
recur = 1;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose = 1;
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
version();
|
||||||
|
exit(EX_OK);
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
file_ptr = argv;
|
||||||
|
wipe_success = calloc(argc, sizeof(char *));
|
||||||
|
wipe_fail = calloc(argc, sizeof(char *));
|
||||||
|
|
||||||
|
|
||||||
|
while (NULL != *file_ptr) {
|
||||||
|
printf("%s: ", *file_ptr);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
if (EX_OK != do_wipe(*file_ptr, passes, recur)) {
|
||||||
|
wipe_fail[failed] = strdup(*file_ptr);
|
||||||
|
failed++;
|
||||||
|
} else {
|
||||||
|
wipe_success[wiped] = strdup(*file_ptr);
|
||||||
|
wiped++;
|
||||||
|
printf(" OK!");
|
||||||
|
}
|
||||||
|
file_ptr++;
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
if (0 < wiped) {
|
||||||
|
printf("success: ");
|
||||||
|
for( i = 0; i < wiped; ++i ) {
|
||||||
|
printf("%s ", wipe_success[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 < failed) {
|
||||||
|
printf("failures: ");
|
||||||
|
for( i = 0; i < failed; ++i ) {
|
||||||
|
printf("%s ", wipe_fail[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free allocated memory */
|
||||||
|
for (i = 0; i < failed; ++i) {
|
||||||
|
free(wipe_fail[i]);
|
||||||
|
wipe_fail[i] = NULL;
|
||||||
|
}
|
||||||
|
free(wipe_fail);
|
||||||
|
wipe_fail = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < wiped; ++i) {
|
||||||
|
free(wipe_success[i]);
|
||||||
|
wipe_success[i] = NULL;
|
||||||
|
}
|
||||||
|
free(wipe_success);
|
||||||
|
wipe_success = NULL;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* takes a filename and the number of passes to wipe the file
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
do_wipe(char *filename, size_t passes, int recur)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
size_t filesize, i;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = EX_IOERR;
|
||||||
|
if (-1 == stat(filename, &sb)) {
|
||||||
|
warn("%s", filename);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recur && sb.st_mode & S_IFDIR) {
|
||||||
|
if (EX_OK != rmdirs(filename, passes)) {
|
||||||
|
printf("!");
|
||||||
|
return retval;
|
||||||
|
} else {
|
||||||
|
printf(".");
|
||||||
|
fflush(stdout);
|
||||||
|
retval = EX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
filesize = sb.st_size;
|
||||||
|
|
||||||
|
for (i = 0; i < passes; ++i) {
|
||||||
|
if (EX_OK != wipe(filename)) {
|
||||||
|
printf("!");
|
||||||
|
return retval;
|
||||||
|
} else if (-1 == stat(filename, &sb)) {
|
||||||
|
printf("!");
|
||||||
|
return retval;
|
||||||
|
} else if (filesize != (size_t)sb.st_size) {
|
||||||
|
printf("!");
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-1 == truncate(filename, (off_t)0))
|
||||||
|
warn("%s", filename);
|
||||||
|
|
||||||
|
if (-1 == unlink(filename)) {
|
||||||
|
warn("%s", filename);
|
||||||
|
} else {
|
||||||
|
printf(".");
|
||||||
|
fflush(stdout);
|
||||||
|
retval = EX_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* takes a filename and attempts to overwrite it with random data
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
wipe(char *filename)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
|
size_t chunk, filesize, rdsz, wiped, wrsz;
|
||||||
|
FILE *devrandom, *target;
|
||||||
|
int retval;
|
||||||
|
char *rdata;
|
||||||
|
|
||||||
|
retval = EX_IOERR;
|
||||||
|
wiped = 0;
|
||||||
|
|
||||||
|
if (-1 == stat(filename, &sb))
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
filesize = sb.st_size;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* open devrandom first: if this fails, we don't want to touch the
|
||||||
|
* target file.
|
||||||
|
*/
|
||||||
|
devrandom = fopen(DEV_RANDOM, "r");
|
||||||
|
if (NULL == devrandom || -1 == ferror(devrandom)) {
|
||||||
|
warn("failed to open PRNG %s!", DEV_RANDOM);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* for security purposes, we want to make sure to actually overwrite
|
||||||
|
* the file. r+ gives us read/write but more importantly, sets the
|
||||||
|
* write stream at the beginning of the file. a side note is that when
|
||||||
|
* overwriting a file, its size will never seem to change.
|
||||||
|
*/
|
||||||
|
target = fopen(filename, "r+");
|
||||||
|
if (NULL == target || -1 == ferror(target)) {
|
||||||
|
warn("failed to open %s", filename);
|
||||||
|
fclose(devrandom);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
rewind(target);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* wait to calloc until we really need the data - makes cleaning up less
|
||||||
|
* tricky.
|
||||||
|
*/
|
||||||
|
rdata = calloc(MAX_CHUNK, sizeof(char));
|
||||||
|
if (NULL == rdata) {
|
||||||
|
warn("could not allocate random data memory");
|
||||||
|
fclose(devrandom);
|
||||||
|
fclose(target);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (wiped < filesize) {
|
||||||
|
chunk = filesize - wiped;
|
||||||
|
chunk = chunk > MAX_CHUNK ? MAX_CHUNK : chunk;
|
||||||
|
|
||||||
|
rdsz = fread( rdata, sizeof(char), chunk, devrandom );
|
||||||
|
wrsz = fwrite( rdata, sizeof(char), chunk, target );
|
||||||
|
|
||||||
|
if (-1 == stat(filename, &sb)) {
|
||||||
|
warn(" stat on %s failed", filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((rdsz != wrsz) || (filesize != (unsigned int)sb.st_size)) {
|
||||||
|
warn("invalid read/write size");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
wiped += chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((0 != fclose(devrandom)) || (0 != fclose(target)))
|
||||||
|
warn("%s", filename);
|
||||||
|
else
|
||||||
|
retval = EX_OK;
|
||||||
|
|
||||||
|
free(rdata);
|
||||||
|
rdata = NULL;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* remove a directory, all files in it, and all subdirectories.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
rmdirs(const char *path, size_t passes)
|
||||||
|
{
|
||||||
|
char child[FILENAME_MAX + 1];
|
||||||
|
struct dirent *dp;
|
||||||
|
DIR *dirp;
|
||||||
|
int fail = 0;
|
||||||
|
|
||||||
|
if (NULL == (dirp = opendir(path)))
|
||||||
|
return EX_IOERR;
|
||||||
|
while (NULL != (dp = readdir(dirp))) {
|
||||||
|
if (0 == strncmp("..", dp->d_name, 3))
|
||||||
|
continue;
|
||||||
|
if (0 == strncmp(".", dp->d_name, 2))
|
||||||
|
continue;
|
||||||
|
snprintf(child, FILENAME_MAX, "%s/%s", path, dp->d_name);
|
||||||
|
if (DT_DIR == dp->d_type) {
|
||||||
|
fail = rmdirs(child, passes);
|
||||||
|
if (EX_IOERR == fail)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
fail = do_wipe(child, passes, 1);
|
||||||
|
if (EX_IOERR == fail)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-1 == closedir(dirp))
|
||||||
|
return EX_IOERR;
|
||||||
|
if (EX_IOERR == fail)
|
||||||
|
return EX_IOERR;
|
||||||
|
|
||||||
|
if (-1 == rmdir(path))
|
||||||
|
return EX_IOERR;
|
||||||
|
else
|
||||||
|
return EX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* print a quick usage message
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
usage()
|
||||||
|
{
|
||||||
|
version();
|
||||||
|
printf("usage: %s [-v] [-n number] files\n", __progname);
|
||||||
|
printf(" -n passes specify number of passes\n");
|
||||||
|
printf(" (default is %d passes)\n", DEFAULT_PASSES);
|
||||||
|
printf(" -r recursive delete\n");
|
||||||
|
printf(" -v display list of failures and wiped files"
|
||||||
|
" after wiping\n"
|
||||||
|
" (verbose mode).\n");
|
||||||
|
printf(" -V print version information.");
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
exit(EX_USAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* print program version information
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
version()
|
||||||
|
{
|
||||||
|
printf("%s\n", SRM_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue