#include <sys/capsicum.h>
#include <sys/types.h>
#include <sys/sbuf.h>
#include <capsicum_helpers.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <xlocale.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;
locale_t l;
l = newlocale(LC_ALL_MASK, "C", NULL);
sz = 0;
buf = NULL;
buffp = open_memstream(&buf, &sz);
if (buffp == NULL)
err(EXIT_FAILURE, "open_memstream()");
if (name != NULL)
printf("%s:\n", name);
while ((c = fgetc(fp)) != EOF) {
switch (state) {
case INIT:
if (c == '$') {
state = DELIM_SEEN;
} else {
continue;
}
break;
case DELIM_SEEN:
if (isalpha_l(c, l)) {
if (buf != NULL)
memset(buf, 0, sz);
rewind(buffp);
fputc('$', buffp);
fputc(c, buffp);
state = KEYWORD;
continue;
} else if (c == '$') {
continue;
} else {
state = INIT;
}
break;
case KEYWORD:
fputc(c, buffp);
if (isalpha_l(c, l)) {
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:
fputc(c, buffp);
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:
fputc(c, buffp);
if (iscntrl_l(c, l)) {
state = INIT;
} else if (c == '$') {
fflush(buffp);
c = buf[strlen(buf) -2 ];
if (c == ' ' || (subversion && c == '#')) {
printf(" %s\n", buf);
hasid = true;
}
state = INIT;
}
break;
}
}
fclose(buffp);
free(buf);
freelocale(l);
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);
}
int
main(int argc, char **argv)
{
bool quiet = false;
int ch, i, *fds, fd;
int ret = EXIT_SUCCESS;
size_t nfds;
FILE *fp;
while ((ch = getopt(argc, argv, "qV")) != -1) {
switch (ch) {
case 'q':
quiet = true;
break;
case 'V':
return (EXIT_SUCCESS);
default:
errx(EXIT_FAILURE, "usage: %s [-q] [-V] [file...]",
getprogname());
}
}
argc -= optind;
argv += optind;
if (caph_limit_stdio() < 0)
err(EXIT_FAILURE, "unable to limit stdio");
if (argc == 0) {
nfds = 1;
fds = malloc(sizeof(*fds));
if (fds == NULL)
err(EXIT_FAILURE, "unable to allocate fds array");
fds[0] = STDIN_FILENO;
} else {
nfds = argc;
fds = malloc(sizeof(*fds) * nfds);
if (fds == NULL)
err(EXIT_FAILURE, "unable to allocate fds array");
for (i = 0; i < argc; i++) {
fds[i] = fd = open(argv[i], O_RDONLY);
if (fd < 0) {
warn("%s", argv[i]);
ret = EXIT_FAILURE;
continue;
}
if (caph_limit_stream(fd, CAPH_READ) < 0)
err(EXIT_FAILURE,
"unable to limit fcntls/rights for %s",
argv[i]);
}
}
if (caph_enter() < 0)
err(EXIT_FAILURE, "unable to enter capability mode");
for (i = 0; i < (int)nfds; i++) {
if (fds[i] < 0)
continue;
fp = fdopen(fds[i], "r");
if (fp == NULL) {
warn("%s", argv[i]);
ret = EXIT_FAILURE;
continue;
}
if (scan(fp, argc == 0 ? NULL : argv[i], quiet) != EXIT_SUCCESS)
ret = EXIT_FAILURE;
fclose(fp);
}
return (ret);
}