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 37 #ifndef lint 38 static char copyright[] = 39 "@(#) Copyright (c) 1989, 1993\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 static char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 5/4/95"; 45 #endif /* not lint */ 46 47 #include <ctype.h> 48 #include <err.h> 49 #include <errno.h> 50 #include <limits.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 int cflag; 57 char dchar; 58 int dflag; 59 int fflag; 60 int sflag; 61 62 void c_cut __P((FILE *, char *)); 63 void f_cut __P((FILE *, char *)); 64 void get_list __P((char *)); 65 static void usage __P((void)); 66 67 int 68 main(argc, argv) 69 int argc; 70 char *argv[]; 71 { 72 FILE *fp; 73 void (*fcn) __P((FILE *, char *)); 74 int ch; 75 76 dchar = '\t'; /* default delimiter is \t */ 77 78 while ((ch = getopt(argc, argv, "c:d:f:s")) != -1) 79 switch(ch) { 80 case 'c': 81 fcn = c_cut; 82 get_list(optarg); 83 cflag = 1; 84 break; 85 case 'd': 86 dchar = *optarg; 87 dflag = 1; 88 break; 89 case 'f': 90 get_list(optarg); 91 fcn = f_cut; 92 fflag = 1; 93 break; 94 case 's': 95 sflag = 1; 96 break; 97 case '?': 98 default: 99 usage(); 100 } 101 argc -= optind; 102 argv += optind; 103 104 if (fflag) { 105 if (cflag) 106 usage(); 107 } else if (!cflag || dflag || sflag) 108 usage(); 109 110 if (*argv) 111 for (; *argv; ++argv) { 112 if (!(fp = fopen(*argv, "r"))) 113 err(1, "%s", *argv); 114 fcn(fp, *argv); 115 (void)fclose(fp); 116 } 117 else 118 fcn(stdin, "stdin"); 119 exit(0); 120 } 121 122 int autostart, autostop, maxval; 123 124 char positions[_POSIX2_LINE_MAX + 1]; 125 126 void 127 get_list(list) 128 char *list; 129 { 130 register int setautostart, start, stop; 131 register char *pos; 132 char *p; 133 134 /* 135 * set a byte in the positions array to indicate if a field or 136 * column is to be selected; use +1, it's 1-based, not 0-based. 137 * This parser is less restrictive than the Draft 9 POSIX spec. 138 * POSIX doesn't allow lists that aren't in increasing order or 139 * overlapping lists. We also handle "-3-5" although there's no 140 * real reason too. 141 */ 142 for (; p = strtok(list, ", \t"); list = NULL) { 143 setautostart = start = stop = 0; 144 if (*p == '-') { 145 ++p; 146 setautostart = 1; 147 } 148 if (isdigit(*p)) { 149 start = stop = strtol(p, &p, 10); 150 if (setautostart && start > autostart) 151 autostart = start; 152 } 153 if (*p == '-') { 154 if (isdigit(p[1])) 155 stop = strtol(p + 1, &p, 10); 156 if (*p == '-') { 157 ++p; 158 if (!autostop || autostop > stop) 159 autostop = stop; 160 } 161 } 162 if (*p) 163 errx(1, "[-cf] list: illegal list value"); 164 if (!stop || !start) 165 errx(1, "[-cf] list: values may not include zero"); 166 if (stop > _POSIX2_LINE_MAX) 167 errx(1, "[-cf] list: %d too large (max %d)", 168 stop, _POSIX2_LINE_MAX); 169 if (maxval < stop) 170 maxval = stop; 171 for (pos = positions + start; start++ <= stop; *pos++ = 1); 172 } 173 174 /* overlapping ranges */ 175 if (autostop && maxval > autostop) 176 maxval = autostop; 177 178 /* set autostart */ 179 if (autostart) 180 memset(positions + 1, '1', autostart); 181 } 182 183 /* ARGSUSED */ 184 void 185 c_cut(fp, fname) 186 FILE *fp; 187 char *fname; 188 { 189 register int ch, col; 190 register char *pos; 191 192 for (;;) { 193 pos = positions + 1; 194 for (col = maxval; col; --col) { 195 if ((ch = getc(fp)) == EOF) 196 return; 197 if (ch == '\n') 198 break; 199 if (*pos++) 200 (void)putchar(ch); 201 } 202 if (ch != '\n') 203 if (autostop) 204 while ((ch = getc(fp)) != EOF && ch != '\n') 205 (void)putchar(ch); 206 else 207 while ((ch = getc(fp)) != EOF && ch != '\n'); 208 (void)putchar('\n'); 209 } 210 } 211 212 void 213 f_cut(fp, fname) 214 FILE *fp; 215 char *fname; 216 { 217 register int ch, field, isdelim; 218 register char *pos, *p, sep; 219 int output; 220 char lbuf[_POSIX2_LINE_MAX + 1]; 221 222 for (sep = dchar; fgets(lbuf, sizeof(lbuf), fp);) { 223 output = 0; 224 for (isdelim = 0, p = lbuf;; ++p) { 225 if (!(ch = *p)) 226 errx(1, "%s: line too long", fname); 227 /* this should work if newline is delimiter */ 228 if (ch == sep) 229 isdelim = 1; 230 if (ch == '\n') { 231 if (!isdelim && !sflag) 232 (void)printf("%s", lbuf); 233 break; 234 } 235 } 236 if (!isdelim) 237 continue; 238 239 pos = positions + 1; 240 for (field = maxval, p = lbuf; field; --field, ++pos) { 241 if (*pos) { 242 if (output++) 243 (void)putchar(sep); 244 while ((ch = *p++) != '\n' && ch != sep) 245 (void)putchar(ch); 246 } else 247 while ((ch = *p++) != '\n' && ch != sep); 248 if (ch == '\n') 249 break; 250 } 251 if (ch != '\n') 252 if (autostop) { 253 if (output) 254 (void)putchar(sep); 255 for (; (ch = *p) != '\n'; ++p) 256 (void)putchar(ch); 257 } else 258 for (; (ch = *p) != '\n'; ++p); 259 (void)putchar('\n'); 260 } 261 } 262 263 static void 264 usage() 265 { 266 (void)fprintf(stderr, "%s\n%s\n", 267 "usage: cut -c list [file1 ...]", 268 " cut -f list [-s] [-d delim] [file ...]"); 269 exit(1); 270 } 271