#include <sys/types.h>
#include <sys/capsicum.h>
#include <sys/sbuf.h>
#include <capsicum_helpers.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <locale.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libcasper.h>
#include <casper/cap_fileargs.h>
typedef enum {
INIT,
DELIM_SEEN,
KEYWORD,
PUNC_SEEN,
PUNC_SEEN_SVN,
TEXT
} analyzer_states;
static int
scan(FILE *fp, const char *name, bool quiet)
{
int c;
bool hasid = false;
bool subversion = false;
analyzer_states state = INIT;
FILE *buffp;
char *buf;
size_t sz;
sz = 0;
buf = NULL;
if ((buffp = open_memstream(&buf, &sz)) == NULL)
goto bufferr;
if (name != NULL) {
printf("%s:\n", name);
if (fflush(stdout) == EOF)
err(EXIT_FAILURE, "stdout");
}
while ((c = fgetc(fp)) != EOF) {
switch (state) {
case INIT:
if (c == '$') {
state = DELIM_SEEN;
} else {
continue;
}
break;
case DELIM_SEEN:
if (isalpha(c)) {
if (buf != NULL)
memset(buf, 0, sz);
if (fseek(buffp, 0, SEEK_SET) != 0 ||
fputc('$', buffp) == EOF ||
fputc(c, buffp) == EOF)
goto bufferr;
state = KEYWORD;
continue;
} else if (c == '$') {
continue;
} else {
state = INIT;
}
break;
case KEYWORD:
if (fputc(c, buffp) == EOF)
goto bufferr;
if (isalpha(c)) {
continue;
} else if (c == ':') {
state = PUNC_SEEN;
subversion = false;
} else if (c == '$') {
state = DELIM_SEEN;
} else {
state = INIT;
}
break;
case PUNC_SEEN:
case PUNC_SEEN_SVN:
if (fputc(c, buffp) == EOF)
goto bufferr;
switch (c) {
case ':':
if (state == PUNC_SEEN) {
state = PUNC_SEEN_SVN;
subversion = true;
} else {
state = INIT;
}
break;
case ' ':
state = TEXT;
break;
default:
state = INIT;
break;
}
break;
case TEXT:
if (fputc(c, buffp) == EOF)
goto bufferr;
if (iscntrl(c)) {
state = INIT;
} else if (c == '$') {
if (fflush(buffp) == EOF)
goto bufferr;
c = buf[strlen(buf) - 2];
if (c == ' ' || (subversion && c == '#')) {
printf(" %s\n", buf);
if (fflush(stdout) == EOF)
err(EXIT_FAILURE, "stdout");
hasid = true;
}
state = INIT;
}
break;
}
}
if (fclose(buffp) == EOF)
goto bufferr;
free(buf);
if (!hasid) {
if (!quiet) {
fprintf(stderr, "%s warning: no id keywords in %s\n",
getprogname(), name ? name : "standard input");
}
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
bufferr:
err(EXIT_FAILURE, "buffer");
}
static void
usage(void)
{
fprintf(stderr, "usage: %s [-q] [-V] [file...]", getprogname());
exit(EXIT_FAILURE);
}
static struct option longopts[] = {
{ "quiet", no_argument, NULL, 'q' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
int
main(int argc, char **argv)
{
fileargs_t *fa;
cap_rights_t rights;
bool quiet = false;
int i, opt, ret;
FILE *fp;
setlocale(LC_CTYPE, "C");
while ((opt = getopt_long(argc, argv, "+qV", longopts, NULL)) != -1) {
switch (opt) {
case 'q':
quiet = true;
break;
case 'V':
return (EXIT_SUCCESS);
default:
usage();
}
}
argc -= optind;
argv += optind;
cap_rights_init(&rights, CAP_READ, CAP_FSTAT, CAP_FCNTL);
fa = fileargs_init(argc, argv, O_RDONLY, 0, &rights, FA_OPEN);
if (fa == NULL)
err(EXIT_FAILURE, "Unable to initialize casper");
caph_cache_catpages();
if (caph_limit_stdio() != 0)
err(EXIT_FAILURE, "Unable to limit stdio");
if (caph_enter_casper() != 0)
err(EXIT_FAILURE, "Unable to enter capability mode");
if (argc == 0) {
ret = scan(stdin, NULL, quiet);
} else {
ret = EXIT_SUCCESS;
for (i = 0; i < argc; i++) {
if ((fp = fileargs_fopen(fa, argv[i], "r")) == NULL)
err(EXIT_FAILURE, "%s", argv[i]);
if (scan(fp, argv[i], quiet) != EXIT_SUCCESS)
ret = EXIT_FAILURE;
(void)fclose(fp);
}
}
fileargs_free(fa);
return (ret);
}