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