Mercurial > hg > toybox
comparison toys/posix/cut.c @ 706:35a9f9c5b53f
Commit 698 adding cut should ahve included the actual cut.c file. (Oops.)
author | Rob Landley <rob@landley.net> |
---|---|
date | Tue, 20 Nov 2012 01:00:17 -0600 |
parents | |
children | 6cc69be43c42 |
comparison
equal
deleted
inserted
replaced
705:3e81cd0bad4b | 706:35a9f9c5b53f |
---|---|
1 /* cut.c - Cut from a file. | |
2 * | |
3 * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>, Kyungwan Han <asura321@gamil.com> | |
4 * | |
5 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cut.html | |
6 * | |
7 USE_CUT(NEWTOY(cut, "b:c:f:d:sn", TOYFLAG_BIN)) | |
8 | |
9 config CUT | |
10 bool "cut" | |
11 default y | |
12 help | |
13 usage: cut OPTION... [FILE]... | |
14 Print selected parts of lines from each FILE to standard output. | |
15 -b LIST select only these bytes from LIST. | |
16 -c LIST select only these characters from LIST. | |
17 -f LIST select only these fields. | |
18 -d DELIM use DELIM instead of TAB for field delimiter. | |
19 -s do not print lines not containing delimiters. | |
20 -n don't split multibyte characters (Ignored). | |
21 */ | |
22 #define FOR_cut | |
23 #include "toys.h" | |
24 | |
25 typedef struct _slist { | |
26 int start_position; | |
27 int end_position; | |
28 struct _slist *next; | |
29 }SLIST; | |
30 | |
31 GLOBALS( | |
32 char *delim; | |
33 char *flist; | |
34 char *clist; | |
35 char *blist; | |
36 struct _slist *slist_head; | |
37 unsigned nelem; | |
38 ) | |
39 | |
40 #define BEGINOFLINE 0 | |
41 #define ENDOFLINE INT_MAX | |
42 #define NORANGE -1 | |
43 | |
44 static int get_user_options(void); | |
45 void (*do_cut)(int); | |
46 static void do_fcut(int fd); | |
47 static void do_bccut(int fd); | |
48 static void free_list(void); | |
49 | |
50 /* | |
51 * add items in the slist. | |
52 */ | |
53 static void add_to_list(int start, int end) | |
54 { | |
55 SLIST *current, *head_ref, *temp1_node; | |
56 | |
57 head_ref = TT.slist_head; | |
58 temp1_node = (SLIST *)xzalloc(sizeof(SLIST)); | |
59 temp1_node->start_position = start; | |
60 temp1_node->end_position = end; | |
61 temp1_node->next = NULL; | |
62 | |
63 /* Special case for the head end */ | |
64 if (head_ref == NULL || (head_ref)->start_position >= start) { | |
65 temp1_node->next = head_ref; | |
66 head_ref = temp1_node; | |
67 } | |
68 else { | |
69 /* Locate the node before the point of insertion */ | |
70 current = head_ref; | |
71 while (current->next!=NULL && current->next->start_position < temp1_node->start_position) | |
72 current = current->next; | |
73 temp1_node->next = current->next; | |
74 current->next = temp1_node; | |
75 } | |
76 TT.slist_head = head_ref; | |
77 return; | |
78 } | |
79 | |
80 /* | |
81 * parse list and add to slist. | |
82 */ | |
83 static void parse_list(char *list) | |
84 { | |
85 char *ctoken, *dtoken; | |
86 int start = 0, end = 0; | |
87 while((ctoken = strsep(&list, ",")) != NULL) { | |
88 if(!ctoken[0]) continue; | |
89 | |
90 //Get start position. | |
91 dtoken = strsep(&ctoken, "-"); | |
92 if(!dtoken[0]) start = BEGINOFLINE; | |
93 else { | |
94 start = get_int_value(dtoken, 0, INT_MAX); | |
95 start = (start?(start-1):start); | |
96 } | |
97 //Get end position. | |
98 if(ctoken == NULL) end = NORANGE; //case e.g. 1,2,3 | |
99 else if(!ctoken[0]) end = ENDOFLINE; //case e.g. N- | |
100 else {//case e.g. N-M | |
101 end = get_int_value(ctoken, 0, INT_MAX); | |
102 end = (end?end:ENDOFLINE); | |
103 end--; | |
104 if(end == start) end = NORANGE; | |
105 } | |
106 add_to_list(start, end); | |
107 TT.nelem++; | |
108 } | |
109 //if list is missing in command line. | |
110 if(TT.nelem == 0) error_exit("missing positions list:"); | |
111 return; | |
112 } | |
113 | |
114 /* | |
115 * retrive data from the file/s. | |
116 */ | |
117 static void get_data(void) | |
118 { | |
119 char **argv = toys.optargs; //file name. | |
120 toys.exitval = EXIT_SUCCESS; | |
121 | |
122 if(!*argv) do_cut(0); //for stdin | |
123 else { | |
124 for(; *argv; ++argv) { | |
125 if(strcmp(*argv, "-") == 0) do_cut(0); //for stdin | |
126 else { | |
127 int fd = open(*argv, O_RDONLY, 0); | |
128 if(fd < 0) {//if file not present then continue with other files. | |
129 perror_msg(*argv); | |
130 toys.exitval = EXIT_FAILURE; | |
131 continue; | |
132 } | |
133 do_cut(fd); | |
134 xclose(fd); | |
135 } | |
136 } | |
137 } | |
138 return; | |
139 } | |
140 | |
141 /* | |
142 * cut main function. | |
143 */ | |
144 void cut_main(void) | |
145 { | |
146 char delimiter = '\t'; //default delimiter. | |
147 int num_of_options = 0; | |
148 char *list; | |
149 | |
150 TT.nelem = 0; | |
151 TT.slist_head = NULL; | |
152 //verify the number of options provided by user. | |
153 num_of_options = get_user_options(); | |
154 if(num_of_options == 0) error_exit("specify a list of bytes, characters, or fields"); | |
155 else if(num_of_options > 1) error_exit("only one type of list may be specified"); | |
156 | |
157 //Get list and assign the function. | |
158 if(toys.optflags & FLAG_f) { | |
159 list = TT.flist; | |
160 do_cut = do_fcut; | |
161 } | |
162 else if(toys.optflags & FLAG_c) { | |
163 list = TT.clist; | |
164 do_cut = do_bccut; | |
165 } | |
166 else { | |
167 list = TT.blist; | |
168 do_cut = do_bccut; | |
169 } | |
170 | |
171 if(toys.optflags & FLAG_d) { | |
172 //delimiter must be 1 char. | |
173 if(TT.delim[0] && TT.delim[1]) perror_exit("the delimiter must be a single character"); | |
174 delimiter = TT.delim[0]; | |
175 } | |
176 | |
177 if(!(toys.optflags & FLAG_d) && (toys.optflags & FLAG_f)) { | |
178 TT.delim = (char *)xzalloc(2); | |
179 TT.delim[0] = delimiter; | |
180 } | |
181 | |
182 //when field is not specified, cutting has some special handling. | |
183 if(!(toys.optflags & FLAG_f)) { | |
184 if(toys.optflags & FLAG_s) perror_exit("suppressing non-delimited lines operating on fields"); | |
185 if(delimiter != '\t') perror_exit("an input delimiter may be specified only when operating on fields"); | |
186 } | |
187 | |
188 parse_list(list); | |
189 get_data(); | |
190 if(!(toys.optflags & FLAG_d) && (toys.optflags & FLAG_f)) { | |
191 free(TT.delim); | |
192 TT.delim = NULL; | |
193 } | |
194 free_list(); | |
195 return; | |
196 } | |
197 | |
198 /* | |
199 * perform cut operation on the given delimiter. | |
200 */ | |
201 static void do_fcut(int fd) | |
202 { | |
203 char *buff; | |
204 char *delimiter = TT.delim; | |
205 while((buff = get_line(fd)) != NULL) { | |
206 //does line have any delimiter?. | |
207 if(strrchr(buff, (int)delimiter[0]) == NULL) { | |
208 //if not then print whole line and move to next line. | |
209 if(!(toys.optflags & FLAG_s)) xputs(buff); | |
210 continue; | |
211 } | |
212 | |
213 unsigned cpos = 0; | |
214 int start, ndelimiters = -1; | |
215 int nprinted_fields = 0; | |
216 char *pfield = xzalloc(strlen(buff) + 1); | |
217 SLIST *temp_node = TT.slist_head; | |
218 | |
219 if(temp_node != NULL) { | |
220 //process list on each line. | |
221 while(cpos < TT.nelem && buff) { | |
222 if(!temp_node) break; | |
223 start = temp_node->start_position; | |
224 do { | |
225 char *field = NULL; | |
226 //count number of delimeters per line. | |
227 while(buff) { | |
228 if(ndelimiters < start) { | |
229 ndelimiters++; | |
230 field = strsep(&buff, delimiter); | |
231 } | |
232 else break; | |
233 } | |
234 //print field (if not yet printed). | |
235 if(!pfield[ndelimiters]) { | |
236 if(ndelimiters == start) { | |
237 //put delimiter. | |
238 if(nprinted_fields++ > 0) xputc(delimiter[0]); | |
239 if(field) fputs(field, stdout); | |
240 //make sure this field won't print again. | |
241 pfield[ndelimiters] = (char) 0x23; //put some char at this position. | |
242 } | |
243 } | |
244 start++; | |
245 if((temp_node->end_position == NORANGE) || (!buff)) break; | |
246 }while(start <= temp_node->end_position); | |
247 temp_node = temp_node->next; | |
248 cpos++; | |
249 } | |
250 } | |
251 xputc('\n'); | |
252 free(pfield); | |
253 pfield = NULL; | |
254 }//End of while loop. | |
255 return; | |
256 } | |
257 | |
258 /* | |
259 * perform cut operation char or byte. | |
260 */ | |
261 static void do_bccut(int fd) | |
262 { | |
263 char *buff; | |
264 while((buff = get_line(fd)) != NULL) { | |
265 unsigned cpos = 0; | |
266 int buffln = strlen(buff); | |
267 char *pfield = xzalloc(buffln + 1); | |
268 SLIST *temp_node = TT.slist_head; | |
269 if(temp_node != NULL) { | |
270 while(cpos < TT.nelem) { | |
271 int start; | |
272 if(!temp_node) break; | |
273 start = temp_node->start_position; | |
274 while(start < buffln) { | |
275 //to avoid duplicate field printing. | |
276 if(pfield[start]) { | |
277 if(++start <= temp_node->end_position) continue; | |
278 temp_node = temp_node->next; | |
279 break; | |
280 } | |
281 else { | |
282 //make sure this field won't print again. | |
283 pfield[start] = (char) 0x23; //put some char at this position. | |
284 xputc(buff[start]); | |
285 } | |
286 if(++start > temp_node->end_position) { | |
287 temp_node = temp_node->next; | |
288 break; | |
289 } | |
290 } | |
291 cpos++; | |
292 } | |
293 xputc('\n'); | |
294 } | |
295 free(pfield); | |
296 pfield = NULL; | |
297 } | |
298 return; | |
299 } | |
300 | |
301 /* | |
302 * used to verify "c", "b" and "f" options are present in command line. | |
303 * As cut command can only except any of them at a time. | |
304 */ | |
305 static int get_user_options(void) | |
306 { | |
307 int i = 3, flag = 0; | |
308 for(; i<6; i++) {//verify 3rd, 4th and 5th bit is set. | |
309 int mask = 1; | |
310 mask = mask << i; | |
311 if(toys.optflags & mask) flag++; | |
312 } | |
313 return flag; | |
314 } | |
315 | |
316 /* | |
317 * free the slist. | |
318 */ | |
319 static void free_list(void) | |
320 { | |
321 SLIST *temp; | |
322 while(TT.slist_head != NULL) { | |
323 temp = TT.slist_head->next; | |
324 free(TT.slist_head); | |
325 TT.slist_head = temp; | |
326 } | |
327 return; | |
328 } |