diff options
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | el.c | 195 | ||||
-rw-r--r-- | mystdlib.c | 56 | ||||
-rw-r--r-- | mystdlib.h | 32 |
4 files changed, 289 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4e8c820 --- /dev/null +++ b/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | CC?=gcc | ||
2 | CFLAGS=-Os | ||
3 | LDFLAGS=-s | ||
4 | |||
5 | el: el.c mystdlib.c mystdlib.h | ||
6 | $(CC) -o el el.c mystdlib.c $(CFLAGS) $(LDFLAGS) | ||
@@ -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 | ||
15 | static unsigned char **index = NULL; | ||
16 | |||
17 | // this denotes the number of fields, NOT the | ||
18 | // memory occupied (would be a size_t, then!!!) | ||
19 | static 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 | ||
24 | static long indexfilled = 1; | ||
25 | |||
26 | // If set, the stream of linenums specified calls | ||
27 | // the first line number 0... *shiver* | ||
28 | static int zerobased = 0; | ||
29 | |||
30 | // This tells us, whether we need to scan for hex | ||
31 | // line numbers | ||
32 | static 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 | |||
40 | static 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 | |||
116 | static 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 | |||
138 | static void usage() { | ||
139 | fputs( "Usage: el [-0xh] filename < filenums\n", stderr); | ||
140 | } | ||
141 | |||
142 | int 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 | } | ||
diff --git a/mystdlib.c b/mystdlib.c new file mode 100644 index 0000000..b53beca --- /dev/null +++ b/mystdlib.c | |||
@@ -0,0 +1,56 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <sys/stat.h> | ||
3 | #include <sys/mman.h> | ||
4 | #include <unistd.h> | ||
5 | #include <fcntl.h> | ||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <string.h> | ||
9 | |||
10 | #include "mystdlib.h" | ||
11 | |||
12 | MAP map_file( char *filename, int readonly ) | ||
13 | { | ||
14 | struct stat fstatus; | ||
15 | MAP map = (MAP)malloc( sizeof( *map )); | ||
16 | |||
17 | if( map ) | ||
18 | { | ||
19 | memset( map, 0, sizeof( *map )); | ||
20 | |||
21 | if( ( map->fh = open( filename, readonly ? O_RDONLY : O_RDWR ) ) >= 0 ) | ||
22 | { | ||
23 | fstat( map->fh, &fstatus ); | ||
24 | if( ( map->addr = mmap( NULL, map->size = (size_t)fstatus.st_size, | ||
25 | PROT_READ | ( readonly ? 0 : PROT_WRITE), (readonly ? 0 : MAP_SHARED), map->fh, 0) ) == MAP_FAILED ) | ||
26 | { | ||
27 | fprintf( stderr, "Mapping file '%s' failed\n", filename ); | ||
28 | close( map->fh ); free( map ); map = NULL; | ||
29 | } | ||
30 | } else { | ||
31 | fprintf( stderr, "Couldn't open file: '%s'\n", filename ); | ||
32 | free( map ); map = NULL; | ||
33 | } | ||
34 | } else { | ||
35 | fputs( "Couldn't allocate memory", stderr ); | ||
36 | } | ||
37 | |||
38 | return map; | ||
39 | } | ||
40 | |||
41 | void unmap_file ( MAP *pMap ) | ||
42 | { | ||
43 | if( !pMap || !*pMap ) return; | ||
44 | munmap( (*pMap)->addr, (*pMap)->size); | ||
45 | close( (*pMap)->fh); | ||
46 | free( *pMap ); *pMap = NULL; | ||
47 | } | ||
48 | |||
49 | int getfilesize( int fd, unsigned long *size) | ||
50 | { | ||
51 | struct stat sb; | ||
52 | int ret; | ||
53 | if( fstat( fd, &sb )) return -1; | ||
54 | *size = sb.st_size; | ||
55 | return 0; | ||
56 | } | ||
diff --git a/mystdlib.h b/mystdlib.h new file mode 100644 index 0000000..2e9499f --- /dev/null +++ b/mystdlib.h | |||
@@ -0,0 +1,32 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <stdio.h> | ||
3 | |||
4 | typedef struct { int fh; unsigned char *addr; size_t size; } *MAP; | ||
5 | |||
6 | /* Mapps a file into memory | ||
7 | returns pointer to the mapping struct, | ||
8 | containing the file's size, the mapped | ||
9 | address and its file handle. | ||
10 | |||
11 | If readonly is true, the file will be | ||
12 | opened and mapped read only. File is | ||
13 | opened and mapped writable, if false. | ||
14 | |||
15 | Returns NULL if memory could not be | ||
16 | allocated, file could not be opened or | ||
17 | mapped. Gives out an diagnostic message | ||
18 | on stderr | ||
19 | */ | ||
20 | MAP map_file( char *filename, int readonly ); | ||
21 | |||
22 | /* Unmapps a file from memory. NULL pointer | ||
23 | checks are being done, so this is safe | ||
24 | to be called from cleanup without knowing | ||
25 | whether there actually is a map. | ||
26 | */ | ||
27 | void unmap_file ( MAP *pMap ); | ||
28 | |||
29 | /* Gets file size of open file | ||
30 | returns != 0 in case of error */ | ||
31 | inline int getfilesize( int fd, unsigned long *size ); | ||
32 | |||