Dump some old code up in here.

This commit is contained in:
Kyle Isom 2020-02-11 18:36:15 -08:00
parent d7c4c0800d
commit a2af41e2a4
21 changed files with 2981 additions and 0 deletions

View File

@ -1,2 +1,6 @@
.o$ .o$
ke/ke ke/ke
kte/kte
.a$
src/srm
src/iniparser-test

16
LICENSE Normal file
View File

@ -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.

8
README Normal file
View File

@ -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.

36
doc/README.kst Normal file
View File

@ -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.

95
doc/libdirutils.3 Normal file
View File

@ -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 .

285
doc/libiniparser.3 Normal file
View File

@ -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.

84
doc/srm.1 Normal file
View File

@ -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.

42
include/kst/dirlist.h Normal file
View File

@ -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

53
include/kst/dirutils.h Normal file
View File

@ -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

51
include/kst/iniparser.h Normal file
View File

@ -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

568
include/kst/queue.h Normal file
View File

@ -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_ */

View File

@ -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

36
src/Makefile Normal file
View File

@ -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

121
src/dirlist.c Normal file
View File

@ -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;
}

212
src/dirlist_test.c Normal file
View File

@ -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);
}

192
src/dirutils.c Normal file
View File

@ -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;
}
}

267
src/dirutils_test.c Normal file
View File

@ -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;
}

163
src/dirwalk.c Normal file
View File

@ -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;
}

280
src/iniparser.c Normal file
View File

@ -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(&section_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(&section_regex);
regfree(&skipline_regex);
regfree(&comment_regex);
regfree(&keyvalue_regex);
}
int
iniparser_init()
{
int ret = -1;
ret = regcomp(&section_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;
}

96
src/iniparser_test.c Normal file
View File

@ -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;
}

370
src/srm.c Normal file
View File

@ -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);
}