#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1989, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif
#ifndef lint
static const char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 5/4/95";
#endif
#include <config.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include "bashansi.h"
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#if !defined (errno)
extern int errno;
#endif
#if !defined (_POSIX2_LINE_MAX)
# define _POSIX2_LINE_MAX 2048
#endif
static int cflag;
static char dchar;
static int dflag;
static int fflag;
static int sflag;
static int autostart, autostop, maxval;
static char positions[_POSIX2_LINE_MAX + 1];
static int c_cut __P((FILE *, char *));
static int f_cut __P((FILE *, char *));
static int get_list __P((char *));
static char *_cut_strsep __P((char **, const char *));
int
cut_builtin(list)
WORD_LIST *list;
{
FILE *fp;
int (*fcn) __P((FILE *, char *)) = NULL;
int ch;
fcn = NULL;
dchar = '\t';
reset_internal_getopt ();
while ((ch = internal_getopt (list, "b:c:d:f:sn")) != -1)
switch(ch) {
case 'b':
case 'c':
fcn = c_cut;
if (get_list(list_optarg) < 0)
return (EXECUTION_FAILURE);
cflag = 1;
break;
case 'd':
dchar = *list_optarg;
dflag = 1;
break;
case 'f':
fcn = f_cut;
if (get_list(list_optarg) < 0)
return (EXECUTION_FAILURE);
fflag = 1;
break;
case 's':
sflag = 1;
break;
case 'n':
break;
case '?':
default:
builtin_usage();
return (EX_USAGE);
}
list = loptend;
if (fflag) {
if (cflag) {
builtin_usage();
return (EX_USAGE);
}
} else if (!cflag || dflag || sflag) {
builtin_usage();
return (EX_USAGE);
}
if (list) {
while (list) {
fp = fopen(list->word->word, "r");
if (fp == 0) {
builtin_error("%s", list->word->word);
return (EXECUTION_FAILURE);
}
ch = (*fcn)(fp, list->word->word);
(void)fclose(fp);
if (ch < 0)
return (EXECUTION_FAILURE);
list = list->next;
}
} else {
ch = (*fcn)(stdin, "stdin");
if (ch < 0)
return (EXECUTION_FAILURE);
}
return (EXECUTION_SUCCESS);
}
static int
get_list(list)
char *list;
{
int setautostart, start, stop;
char *pos;
char *p;
for (; (p = _cut_strsep(&list, ", \t")) != NULL;) {
setautostart = start = stop = 0;
if (*p == '-') {
++p;
setautostart = 1;
}
if (isdigit((unsigned char)*p)) {
start = stop = strtol(p, &p, 10);
if (setautostart && start > autostart)
autostart = start;
}
if (*p == '-') {
if (isdigit((unsigned char)p[1]))
stop = strtol(p + 1, &p, 10);
if (*p == '-') {
++p;
if (!autostop || autostop > stop)
autostop = stop;
}
}
if (*p) {
builtin_error("[-cf] list: illegal list value");
return -1;
}
if (!stop || !start) {
builtin_error("[-cf] list: values may not include zero");
return -1;
}
if (stop > _POSIX2_LINE_MAX) {
builtin_error("[-cf] list: %d too large (max %d)",
stop, _POSIX2_LINE_MAX);
return -1;
}
if (maxval < stop)
maxval = stop;
for (pos = positions + start; start++ <= stop; *pos++ = 1);
}
if (autostop && maxval > autostop)
maxval = autostop;
if (autostart)
memset(positions + 1, '1', autostart);
return 0;
}
static int
c_cut(fp, fname)
FILE *fp;
char *fname;
{
int ch, col;
char *pos;
ch = 0;
for (;;) {
pos = positions + 1;
for (col = maxval; col; --col) {
if ((ch = getc(fp)) == EOF)
return;
if (ch == '\n')
break;
if (*pos++)
(void)putchar(ch);
}
if (ch != '\n') {
if (autostop)
while ((ch = getc(fp)) != EOF && ch != '\n')
(void)putchar(ch);
else
while ((ch = getc(fp)) != EOF && ch != '\n');
}
(void)putchar('\n');
}
return (0);
}
static int
f_cut(fp, fname)
FILE *fp;
char *fname;
{
int ch, field, isdelim;
char *pos, *p, sep;
int output;
char lbuf[_POSIX2_LINE_MAX + 1];
for (sep = dchar; fgets(lbuf, sizeof(lbuf), fp);) {
output = 0;
for (isdelim = 0, p = lbuf;; ++p) {
if (!(ch = *p)) {
builtin_error("%s: line too long.", fname);
return -1;
}
if (ch == sep)
isdelim = 1;
if (ch == '\n') {
if (!isdelim && !sflag)
(void)printf("%s", lbuf);
break;
}
}
if (!isdelim)
continue;
pos = positions + 1;
for (field = maxval, p = lbuf; field; --field, ++pos) {
if (*pos) {
if (output++)
(void)putchar(sep);
while ((ch = *p++) != '\n' && ch != sep)
(void)putchar(ch);
} else {
while ((ch = *p++) != '\n' && ch != sep)
continue;
}
if (ch == '\n')
break;
}
if (ch != '\n') {
if (autostop) {
if (output)
(void)putchar(sep);
for (; (ch = *p) != '\n'; ++p)
(void)putchar(ch);
} else
for (; (ch = *p) != '\n'; ++p);
}
(void)putchar('\n');
}
return (0);
}
static char *
_cut_strsep(stringp, delim)
register char **stringp;
register const char *delim;
{
register char *s;
register const char *spanp;
register int c, sc;
char *tok;
if ((s = *stringp) == NULL)
return (NULL);
for (tok = s;;) {
c = *s++;
spanp = delim;
do {
if ((sc = *spanp++) == c) {
if (c == 0)
s = NULL;
else
s[-1] = 0;
*stringp = s;
return (tok);
}
} while (sc != 0);
}
}
static char *cut_doc[] = {
"Select portions of each line (as specified by LIST) from each FILE",
"(by default, the standard input), and write them to the standard output.",
"Items specified by LIST are either column positions or fields delimited",
"by a special character. Column numbering starts at 1.",
(char *)0
};
struct builtin cut_struct = {
"cut",
cut_builtin,
BUILTIN_ENABLED,
cut_doc,
"cut -b list [-n] [file ...] OR cut -c list [file ...] OR cut -f list [-s] [-d delim] [file ...]",
0
};