cut.c revision 71726
1/* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Adam S. Moskowitz of Menlo Consulting and Marciano Pitargue. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * $FreeBSD: head/usr.bin/cut/cut.c 71726 2001-01-28 00:35:40Z will $ 37 * 38 */ 39 40#ifndef lint 41static const char copyright[] = 42"@(#) Copyright (c) 1989, 1993\n\ 43 The Regents of the University of California. All rights reserved.\n"; 44static const char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 5/4/95"; 45static const char rcsid[] = 46 "$FreeBSD: head/usr.bin/cut/cut.c 71726 2001-01-28 00:35:40Z will $"; 47#endif /* not lint */ 48 49#include <ctype.h> 50#include <err.h> 51#include <errno.h> 52#include <limits.h> 53#include <locale.h> 54#include <stdio.h> 55#include <stdlib.h> 56#include <string.h> 57#include <unistd.h> 58 59int cflag; 60char dchar; 61int dflag; 62int fflag; 63int sflag; 64 65void c_cut (FILE *, const char *); 66void f_cut (FILE *, const char *); 67void get_list (char *); 68int main (int, char **); 69static void usage (void); 70 71int 72main(argc, argv) 73 int argc; 74 char *argv[]; 75{ 76 FILE *fp; 77 void (*fcn) (FILE *, const char *) = NULL; 78 int ch; 79 80 fcn = NULL; 81 setlocale (LC_ALL, ""); 82 83 dchar = '\t'; /* default delimiter is \t */ 84 85 /* Since we don't support multi-byte characters, the -c and -b 86 options are equivalent, and the -n option is meaningless. */ 87 while ((ch = getopt(argc, argv, "b:c:d:f:sn")) != -1) 88 switch(ch) { 89 case 'b': 90 case 'c': 91 fcn = c_cut; 92 get_list(optarg); 93 cflag = 1; 94 break; 95 case 'd': 96 dchar = *optarg; 97 dflag = 1; 98 break; 99 case 'f': 100 get_list(optarg); 101 fcn = f_cut; 102 fflag = 1; 103 break; 104 case 's': 105 sflag = 1; 106 break; 107 case 'n': 108 break; 109 case '?': 110 default: 111 usage(); 112 } 113 argc -= optind; 114 argv += optind; 115 116 if (fflag) { 117 if (cflag) 118 usage(); 119 } else if (!cflag || dflag || sflag) 120 usage(); 121 122 if (*argv) 123 for (; *argv; ++argv) { 124 if (!(fp = fopen(*argv, "r"))) 125 err(1, "%s", *argv); 126 fcn(fp, *argv); 127 (void)fclose(fp); 128 } 129 else 130 fcn(stdin, "stdin"); 131 exit(0); 132} 133 134size_t autostart, autostop, maxval; 135 136char positions[_POSIX2_LINE_MAX + 1]; 137 138void 139get_list(list) 140 char *list; 141{ 142 size_t setautostart, start, stop; 143 char *pos; 144 char *p; 145 146 /* 147 * set a byte in the positions array to indicate if a field or 148 * column is to be selected; use +1, it's 1-based, not 0-based. 149 * This parser is less restrictive than the Draft 9 POSIX spec. 150 * POSIX doesn't allow lists that aren't in increasing order or 151 * overlapping lists. We also handle "-3-5" although there's no 152 * real reason too. 153 */ 154 for (; (p = strsep(&list, ", \t")) != NULL;) { 155 setautostart = start = stop = 0; 156 if (*p == '-') { 157 ++p; 158 setautostart = 1; 159 } 160 if (isdigit((unsigned char)*p)) { 161 start = stop = strtol(p, &p, 10); 162 if (setautostart && start > autostart) 163 autostart = start; 164 } 165 if (*p == '-') { 166 if (isdigit((unsigned char)p[1])) 167 stop = strtol(p + 1, &p, 10); 168 if (*p == '-') { 169 ++p; 170 if (!autostop || autostop > stop) 171 autostop = stop; 172 } 173 } 174 if (*p) 175 errx(1, "[-cf] list: illegal list value"); 176 if (!stop || !start) 177 errx(1, "[-cf] list: values may not include zero"); 178 if (stop > _POSIX2_LINE_MAX) 179 errx(1, "[-cf] list: %d too large (max %d)", 180 stop, _POSIX2_LINE_MAX); 181 if (maxval < stop) 182 maxval = stop; 183 for (pos = positions + start; start++ <= stop; *pos++ = 1); 184 } 185 186 /* overlapping ranges */ 187 if (autostop && maxval > autostop) 188 maxval = autostop; 189 190 /* set autostart */ 191 if (autostart) 192 memset(positions + 1, '1', autostart); 193} 194 195/* ARGSUSED */ 196void 197c_cut(fp, fname) 198 FILE *fp; 199 const char *fname; 200{ 201 int ch, col; 202 char *pos; 203 fname = NULL; 204 205 ch = 0; 206 for (;;) { 207 pos = positions + 1; 208 for (col = maxval; col; --col) { 209 if ((ch = getc(fp)) == EOF) 210 return; 211 if (ch == '\n') 212 break; 213 if (*pos++) 214 (void)putchar(ch); 215 } 216 if (ch != '\n') { 217 if (autostop) 218 while ((ch = getc(fp)) != EOF && ch != '\n') 219 (void)putchar(ch); 220 else 221 while ((ch = getc(fp)) != EOF && ch != '\n'); 222 } 223 (void)putchar('\n'); 224 } 225} 226 227void 228f_cut(fp, fname) 229 FILE *fp; 230 const char *fname; 231{ 232 int ch, field, isdelim; 233 char *pos, *p, sep; 234 int output; 235 char lbuf[_POSIX2_LINE_MAX + 1]; 236 237 for (sep = dchar; fgets(lbuf, sizeof(lbuf), fp);) { 238 output = 0; 239 for (isdelim = 0, p = lbuf;; ++p) { 240 if (!(ch = *p)) 241 errx(1, "%s: line too long.", fname); 242 /* this should work if newline is delimiter */ 243 if (ch == sep) 244 isdelim = 1; 245 if (ch == '\n') { 246 if (!isdelim && !sflag) 247 (void)printf("%s", lbuf); 248 break; 249 } 250 } 251 if (!isdelim) 252 continue; 253 254 pos = positions + 1; 255 for (field = maxval, p = lbuf; field; --field, ++pos) { 256 if (*pos) { 257 if (output++) 258 (void)putchar(sep); 259 while ((ch = *p++) != '\n' && ch != sep) 260 (void)putchar(ch); 261 } else { 262 while ((ch = *p++) != '\n' && ch != sep) 263 continue; 264 } 265 if (ch == '\n') 266 break; 267 } 268 if (ch != '\n') { 269 if (autostop) { 270 if (output) 271 (void)putchar(sep); 272 for (; (ch = *p) != '\n'; ++p) 273 (void)putchar(ch); 274 } else 275 for (; (ch = *p) != '\n'; ++p); 276 } 277 (void)putchar('\n'); 278 } 279} 280 281static void 282usage() 283{ 284 (void)fprintf(stderr, "%s\n%s\n%s\n", 285 "usage: cut -b list [-n] [file ...]", 286 " cut -c list [file ...]", 287 " cut -f list [-s] [-d delim] [file ...]"); 288 exit(1); 289} 290