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