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 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 static char copyright[] = 13 "@(#) Copyright (c) 1989, 1993\n\ 14 The Regents of the University of California. All rights reserved.\n"; 15 #endif /* not lint */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)cut.c 8.2 (Berkeley) 04/28/95"; 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; fgets(lbuf, sizeof(lbuf), fp);) { 196 output = 0; 197 for (isdelim = 0, p = lbuf;; ++p) { 198 if (!(ch = *p)) 199 err("%s: line too long.\n", fname); 200 /* this should work if newline is delimiter */ 201 if (ch == sep) 202 isdelim = 1; 203 if (ch == '\n') { 204 if (!isdelim && !sflag) 205 (void)printf("%s", lbuf); 206 break; 207 } 208 } 209 if (!isdelim) 210 continue; 211 212 pos = positions + 1; 213 for (field = maxval, p = lbuf; field; --field, ++pos) { 214 if (*pos) { 215 if (output++) 216 (void)putchar(sep); 217 while ((ch = *p++) != '\n' && ch != sep) 218 (void)putchar(ch); 219 } else 220 while ((ch = *p++) != '\n' && ch != sep); 221 if (ch == '\n') 222 break; 223 } 224 if (ch != '\n') 225 if (autostop) { 226 if (output) 227 (void)putchar(sep); 228 for (; (ch = *p) != '\n'; ++p) 229 (void)putchar(ch); 230 } else 231 for (; (ch = *p) != '\n'; ++p); 232 (void)putchar('\n'); 233 } 234 } 235 236 void 237 usage() 238 { 239 (void)fprintf(stderr, 240 "usage:\tcut -c list [file1 ...]\n\tcut -f list [-s] [-d delim] [file ...]\n"); 241 exit(1); 242 } 243 244 #if __STDC__ 245 #include <stdarg.h> 246 #else 247 #include <varargs.h> 248 #endif 249 250 void 251 #if __STDC__ 252 err(const char *fmt, ...) 253 #else 254 err(fmt, va_alist) 255 char *fmt; 256 va_dcl 257 #endif 258 { 259 va_list ap; 260 #if __STDC__ 261 va_start(ap, fmt); 262 #else 263 va_start(ap); 264 #endif 265 (void)fprintf(stderr, "cut: "); 266 (void)vfprintf(stderr, fmt, ap); 267 va_end(ap); 268 (void)fprintf(stderr, "\n"); 269 exit(1); 270 /* NOTREACHED */ 271 } 272