summaryrefslogtreecommitdiff
path: root/el.c
diff options
context:
space:
mode:
authorerdgeist <>2007-04-19 19:54:07 +0000
committererdgeist <>2007-04-19 19:54:07 +0000
commitcf193c66d988637d9ddfb0acee82608a04f96402 (patch)
tree05d007e28ce0526ff150e67eecba782ea57e832d /el.c
Kickoff
Diffstat (limited to 'el.c')
-rw-r--r--el.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/el.c b/el.c
new file mode 100644
index 0000000..a1994b6
--- /dev/null
+++ b/el.c
@@ -0,0 +1,195 @@
1/* Extract Lines
2 Usage: echo -x 10 2 7 100 | el data.txt
3 extracts lines 16, 2, 7, 256 from the file data.txt
4*/
5
6#include <stdlib.h>
7#include "mystdlib.h"
8#include <unistd.h>
9
10// Our index into the already scanned lines.
11// memory buffer is estimated to 1/32th of the
12// indexed file's size, meaning each line
13// containing approx 31 letters. If it hits the
14// top, we increase in 1/32th steps
15static unsigned char **index = NULL;
16
17// this denotes the number of fields, NOT the
18// memory occupied (would be a size_t, then!!!)
19static long indexsize = 0;
20
21// this specifies, how many lines have been indexed
22// already, scanning always only happens to the
23// last reqeusted line
24static long indexfilled = 1;
25
26// If set, the stream of linenums specified calls
27// the first line number 0... *shiver*
28static int zerobased = 0;
29
30// This tells us, whether we need to scan for hex
31// line numbers
32static char *g_scanfmodifier = "%i";
33
34// scans the text file for the requested line
35// returns NULL, if line exceeds file's end
36// Note: we will not extend the index too early
37// to prevent huge numbers from stdin to steal our
38// memory
39
40static unsigned char * scanforline( MAP file, const long lineno, long *size ) {
41 unsigned char *scanline, *const e_o_f = file->addr + file->size;
42 static int alllinesscanned = 0;
43 *size = 0;
44
45 // lines start at 1
46 if( lineno < 1 ) return NULL;
47
48 // lines we already found can simply be returned
49 if( lineno < indexfilled ) {
50 *size = index[lineno] - index[lineno-1];
51 return index[lineno-1];
52 }
53
54 // if alllines were scanned, were either on
55 // or behind last line
56 if( alllinesscanned ) {
57 if( lineno == indexfilled ) {
58 *size = e_o_f - index[lineno-1];
59 return index[lineno-1];
60 }
61 return NULL;
62 }
63
64 // exploring undiscovered land...
65 scanline = index[indexfilled-1];
66
67 while( indexfilled <= lineno ) {
68 // scan for next line
69 while( ( scanline < e_o_f ) && ( *scanline++ != '\n' ));
70
71 // store pointer
72 if( scanline == e_o_f ) {
73 alllinesscanned = 1;
74 if( indexfilled == lineno ) {
75 *size = e_o_f - index[lineno-1];
76 return index[lineno-1];
77 } else
78 return NULL;
79 }
80
81 index[indexfilled++] = scanline;
82
83 // reallocate some memory
84 if( indexfilled == indexsize ) {
85 unsigned char ** newblock = (unsigned char**) realloc( index, sizeof(char *) * ( indexsize + file->size / 32 ) );
86 if( !newblock ) {
87 fputs( "Could not allocate memory, exiting.\n", stderr);
88 // unmap file and zero pointer
89 unmap_file( &file );
90 exit ( 1 );
91 }
92 indexsize += file->size / 32;
93 index = newblock;
94 }
95
96 }
97
98 *size = index[lineno] - index[lineno-1];
99 return index[lineno-1];
100}
101
102// Reads all characters up to the next white space
103// from stdin, eof is treated as a white space
104// If more than 13 characters without white space
105// occur, we assume a too large line number and return
106// 0 which is an invalid line (these start at 1) and will
107// result in an empty line being printed
108
109// Rationale:
110// Since we wont support linenos greater 2^31 and this
111// is 12 characters + leading 0 in octal, we stop parsing
112// after 13 characters (technically, 001 is 01, but
113// constructing such strings to annoy parser writers must
114// be punished
115
116static int acquire_lineno( int c ) {
117 char input[15];
118 int inputindex = 0, lineno;
119
120 while ( (c != EOF) && !isspace(c)) {
121 if( inputindex < 14 )
122 input[inputindex++] = (char)c;
123 c = getchar();
124 }
125
126 if( inputindex == 14 )
127 return 0;
128
129 input[inputindex] = 0;
130
131 // Try to read
132 if( sscanf( input, g_scanfmodifier, &lineno ) != 1 )
133 return 0;
134
135 return lineno + zerobased;
136}
137
138static void usage() {
139 fputs( "Usage: el [-0xh] filename < filenums\n", stderr);
140}
141
142int main( int argc, char **argv ) {
143 // Our handle to the mapped text file
144 MAP textfile = NULL;
145 int c;
146
147 while ((c = getopt(argc, argv, ":0x")) != -1) {
148 switch (c) {
149 case '0':
150 zerobased = 1;
151 break;
152 case 'x':
153 g_scanfmodifier = "%x";
154 break;
155 case 'h':
156 case '?':
157 default:
158 usage();
159 exit(1);
160 }
161 }
162 argc -= optind;
163 argv += optind;
164
165 if( argc != 1 ) { usage(); exit(1); }
166
167 // Map text file read only
168 if( (textfile = map_file( argv[0], 1 )) == NULL ) exit(1);
169
170 indexsize = textfile->size < 32 ? 32 : textfile->size / 32;
171 if( (index = malloc( sizeof(char *) * indexsize )) == NULL ) {
172 fputs( "Could not allocate memory, exiting.\n", stderr);
173 // unmap file and zero pointer
174 unmap_file( &textfile );
175 exit ( 1 );
176 }
177
178 // First line starts at begin of file.
179 index[0] = textfile->addr;
180
181 while( (c = getchar()) != EOF ) {
182 // get linenumber, pass on eof test char
183 long slen, lineno = acquire_lineno(c);
184 unsigned char *line = scanforline( textfile, lineno, &slen );
185
186 if( line && slen )
187 fwrite( line, slen, 1, stdout );
188 else
189 putchar('\n');
190 }
191
192 // unmap file and zero pointer
193 unmap_file( &textfile );
194 return 0;
195}