| /* |
| * cut.c - minimalist version of cut |
| * |
| * Copyright (C) 1999,2000 by Lineo, inc. |
| * Written by Mark Whitley <markw@lineo.com>, <markw@enol.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> /* getopt */ |
| #include <string.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include "internal.h" |
| |
| |
| /* globals from other files */ |
| extern int optind; |
| extern char *optarg; |
| |
| |
| /* globals in this file only */ |
| static char part = 0; /* (b)yte, (c)har, (f)ields */ |
| static int startpos = 1; |
| static int endpos = -1; |
| static char delim = '\t'; /* delimiter, default is tab */ |
| static unsigned int supress_non_delimited_lines = 0; |
| |
| |
| /* |
| * decompose_list() - parses a list and puts values into startpos and endpos. |
| * valid list formats: N, N-, N-M, -M |
| */ |
| static void decompose_list(const char *list) |
| { |
| unsigned int nminus = 0; |
| char *ptr; |
| |
| /* the list must contain only digits and no more than one minus sign */ |
| for (ptr = (char *)list; *ptr; ptr++) { |
| if (!isdigit(*ptr) && *ptr != '-') { |
| fatalError("invalid byte or field list\n"); |
| } |
| if (*ptr == '-') { |
| nminus++; |
| if (nminus > 1) { |
| fatalError("invalid byte or field list\n"); |
| } |
| } |
| } |
| |
| /* handle single value 'N' case */ |
| if (nminus == 0) { |
| startpos = strtol(list, &ptr, 10); |
| if (startpos == 0) { |
| fatalError("missing list of fields\n"); |
| } |
| endpos = startpos; |
| } |
| /* handle multi-value cases */ |
| else if (nminus == 1) { |
| /* handle 'N-' case */ |
| if (list[strlen(list) - 1] == '-') { |
| startpos = strtol(list, &ptr, 10); |
| } |
| /* handle '-M' case */ |
| else if (list[0] == '-') { |
| endpos = strtol(&list[1], NULL, 10); |
| } |
| /* handle 'N-M' case */ |
| else { |
| startpos = strtol(list, &ptr, 10); |
| endpos = strtol(ptr+1, &ptr, 10); |
| } |
| |
| /* a sanity check */ |
| if (startpos == 0) { |
| startpos = 1; |
| } |
| } |
| } |
| |
| |
| /* |
| * snippy-snip |
| */ |
| static void cut_file(FILE *file) |
| { |
| char *line; |
| |
| /* go through every line in the file */ |
| for (line = NULL; (line = get_line_from_file(file)) != NULL; free(line)) { |
| |
| /* cut based on chars/bytes */ |
| if (part == 'c' || part == 'b') { |
| int i; |
| /* a valid end position has been specified */ |
| if (endpos > 0) { |
| for (i = startpos-1; i < endpos; i++) { |
| fputc(line[i], stdout); |
| } |
| fputc('\n', stdout); |
| } |
| /* otherwise, just go to the end of the line */ |
| else { |
| for (i = startpos-1; line[i]; i++) { |
| fputc(line[i], stdout); |
| } |
| } |
| } |
| /* cut based on fields */ |
| else if (part == 'f') { |
| char *ptr; |
| char *start = line; |
| unsigned int delims_hit = 0; |
| |
| for (ptr = line; (ptr = strchr(ptr, delim)) != NULL; ptr++) { |
| delims_hit++; |
| if (delims_hit == (startpos - 1)) { |
| start = ptr+1; |
| } |
| if (delims_hit == endpos) { |
| break; |
| } |
| } |
| /* we didn't hit any delimeters */ |
| if (delims_hit == 0 && !supress_non_delimited_lines) { |
| fputs(line, stdout); |
| } |
| /* we =did= hit some delimiters */ |
| else if (delims_hit > 0) { |
| /* we have a fixed end point */ |
| if (ptr) { |
| while (start < ptr) { |
| fputc(*start, stdout); |
| start++; |
| } |
| fputc('\n', stdout); |
| } |
| /* or we're just going til the end of the line */ |
| else { |
| while (*start) { |
| fputc(*start, stdout); |
| start++; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| extern int cut_main(int argc, char **argv) |
| { |
| int opt; |
| |
| while ((opt = getopt(argc, argv, "b:c:d:f:ns")) > 0) { |
| switch (opt) { |
| case 'b': |
| case 'c': |
| case 'f': |
| /* make sure they didn't ask for two types of lists */ |
| if (part != 0) { |
| fatalError("only one type of list may be specified"); |
| } |
| part = (char)opt; |
| decompose_list(optarg); |
| break; |
| case 'd': |
| if (strlen(optarg) > 1) { |
| fatalError("the delimiter must be a single character\n"); |
| } |
| delim = optarg[0]; |
| break; |
| case 'n': |
| /* no-op */ |
| break; |
| case 's': |
| supress_non_delimited_lines++; |
| break; |
| } |
| } |
| |
| if (part == 0) { |
| fatalError("you must specify a list of bytes, characters, or fields\n"); |
| } |
| |
| if (supress_non_delimited_lines && part != 'f') { |
| fatalError("suppressing non-delimited lines makes sense |
| only when operating on fields\n"); |
| } |
| |
| if (delim != '\t' && part != 'f') { |
| fatalError("a delimiter may be specified only when operating on fields\n"); |
| } |
| |
| /* argv[(optind)..(argc-1)] should be names of file to process. If no |
| * files were specified or '-' was specified, take input from stdin. |
| * Otherwise, we process all the files specified. */ |
| if (argv[optind] == NULL || (strcmp(argv[optind], "-") == 0)) { |
| cut_file(stdin); |
| } |
| else { |
| int i; |
| FILE *file; |
| for (i = optind; i < argc; i++) { |
| file = fopen(argv[i], "r"); |
| if (file == NULL) { |
| errorMsg("%s: %s\n", argv[i], strerror(errno)); |
| } else { |
| cut_file(file); |
| fclose(file); |
| } |
| } |
| } |
| |
| return 0; |
| } |