blob: e90a0e2e9094d3af777ba83df9063e1d4dbc5118 [file] [log] [blame]
Erik Andersen7ab9c7e2000-05-12 19:41:47 +00001/*
Mark Whitley807f0fd2000-08-02 18:30:11 +00002 * cut.c - minimalist version of cut
Erik Andersen7ab9c7e2000-05-12 19:41:47 +00003 *
Mark Whitley807f0fd2000-08-02 18:30:11 +00004 * Copyright (C) 1999,2000 by Lineo, inc.
5 * Written by Mark Whitley <markw@lineo.com>, <markw@enol.com>
Erik Andersen7ab9c7e2000-05-12 19:41:47 +00006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Mark Whitley807f0fd2000-08-02 18:30:11 +000020 *
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000021 */
22
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000023#include <stdio.h>
Mark Whitley807f0fd2000-08-02 18:30:11 +000024#include <stdlib.h>
25#include <unistd.h> /* getopt */
26#include <string.h>
27#include <ctype.h>
28#include <errno.h>
29#include "internal.h"
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000030
31
Mark Whitley807f0fd2000-08-02 18:30:11 +000032/* globals from other files */
33extern int optind;
34extern char *optarg;
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000035
36
Mark Whitley807f0fd2000-08-02 18:30:11 +000037/* globals in this file only */
38static char part = 0; /* (b)yte, (c)har, (f)ields */
39static int startpos = 1;
40static int endpos = -1;
41static char delim = '\t'; /* delimiter, default is tab */
42static unsigned int supress_non_delimited_lines = 0;
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000043
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000044
45/*
Mark Whitley807f0fd2000-08-02 18:30:11 +000046 * decompose_list() - parses a list and puts values into startpos and endpos.
47 * valid list formats: N, N-, N-M, -M
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000048 */
Mark Whitley807f0fd2000-08-02 18:30:11 +000049static void decompose_list(const char *list)
50{
51 unsigned int nminus = 0;
52 char *ptr;
53
54 /* the list must contain only digits and no more than one minus sign */
55 for (ptr = (char *)list; *ptr; ptr++) {
56 if (!isdigit(*ptr) && *ptr != '-') {
57 fatalError("invalid byte or field list\n");
58 }
59 if (*ptr == '-') {
60 nminus++;
61 if (nminus > 1) {
62 fatalError("invalid byte or field list\n");
63 }
64 }
65 }
66
67 /* handle single value 'N' case */
68 if (nminus == 0) {
69 startpos = strtol(list, &ptr, 10);
70 if (startpos == 0) {
71 fatalError("missing list of fields\n");
72 }
73 endpos = startpos;
74 }
75 /* handle multi-value cases */
76 else if (nminus == 1) {
77 /* handle 'N-' case */
78 if (list[strlen(list) - 1] == '-') {
79 startpos = strtol(list, &ptr, 10);
80 }
81 /* handle '-M' case */
82 else if (list[0] == '-') {
83 endpos = strtol(&list[1], NULL, 10);
84 }
85 /* handle 'N-M' case */
86 else {
87 startpos = strtol(list, &ptr, 10);
88 endpos = strtol(ptr+1, &ptr, 10);
89 }
90
91 /* a sanity check */
92 if (startpos == 0) {
93 startpos = 1;
94 }
95 }
96}
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000097
98
Mark Whitley807f0fd2000-08-02 18:30:11 +000099/*
100 * snippy-snip
101 */
102static void cut_file(FILE *file)
103{
104 char *line;
105
106 /* go through every line in the file */
107 for (line = NULL; (line = get_line_from_file(file)) != NULL; free(line)) {
108
109 /* cut based on chars/bytes */
110 if (part == 'c' || part == 'b') {
111 int i;
112 /* a valid end position has been specified */
113 if (endpos > 0) {
114 for (i = startpos-1; i < endpos; i++) {
115 fputc(line[i], stdout);
116 }
117 fputc('\n', stdout);
118 }
119 /* otherwise, just go to the end of the line */
120 else {
121 for (i = startpos-1; line[i]; i++) {
122 fputc(line[i], stdout);
123 }
124 }
125 }
126 /* cut based on fields */
127 else if (part == 'f') {
128 char *ptr;
129 char *start = line;
130 unsigned int delims_hit = 0;
131
132 for (ptr = line; (ptr = strchr(ptr, delim)) != NULL; ptr++) {
133 delims_hit++;
134 if (delims_hit == (startpos - 1)) {
135 start = ptr+1;
136 }
137 if (delims_hit == endpos) {
138 break;
139 }
140 }
141 /* we didn't hit any delimeters */
142 if (delims_hit == 0 && !supress_non_delimited_lines) {
143 fputs(line, stdout);
144 }
145 /* we =did= hit some delimiters */
146 else if (delims_hit > 0) {
147 /* we have a fixed end point */
148 if (ptr) {
149 while (start < ptr) {
150 fputc(*start, stdout);
151 start++;
152 }
153 fputc('\n', stdout);
154 }
155 /* or we're just going til the end of the line */
156 else {
157 while (*start) {
158 fputc(*start, stdout);
159 start++;
160 }
161 }
162 }
163 }
164 }
165}
166
167extern int cut_main(int argc, char **argv)
168{
169 int opt;
170
171 while ((opt = getopt(argc, argv, "b:c:d:f:ns")) > 0) {
172 switch (opt) {
173 case 'b':
174 case 'c':
175 case 'f':
176 /* make sure they didn't ask for two types of lists */
177 if (part != 0) {
178 fatalError("only one type of list may be specified");
179 }
180 part = (char)opt;
181 decompose_list(optarg);
182 break;
183 case 'd':
184 if (strlen(optarg) > 1) {
185 fatalError("the delimiter must be a single character\n");
186 }
187 delim = optarg[0];
188 break;
189 case 'n':
190 /* no-op */
191 break;
192 case 's':
193 supress_non_delimited_lines++;
194 break;
195 }
196 }
197
198 if (part == 0) {
199 fatalError("you must specify a list of bytes, characters, or fields\n");
200 }
201
202 if (supress_non_delimited_lines && part != 'f') {
203 fatalError("suppressing non-delimited lines makes sense
204 only when operating on fields\n");
205 }
206
207 if (delim != '\t' && part != 'f') {
208 fatalError("a delimiter may be specified only when operating on fields\n");
209 }
210
211 /* argv[(optind)..(argc-1)] should be names of file to process. If no
212 * files were specified or '-' was specified, take input from stdin.
213 * Otherwise, we process all the files specified. */
214 if (argv[optind] == NULL || (strcmp(argv[optind], "-") == 0)) {
215 cut_file(stdin);
216 }
217 else {
218 int i;
219 FILE *file;
220 for (i = optind; i < argc; i++) {
221 file = fopen(argv[i], "r");
222 if (file == NULL) {
223 /* errorMsg("%s: %s\n", argv[i], strerror(errno)); */
224 fprintf(stderr, "%s: %s\n", argv[i], strerror(errno));
225 } else {
226 cut_file(file);
227 fclose(file);
228 }
229 }
230 }
231
232 return 0;
233}