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