summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--el.c195
-rw-r--r--mystdlib.c56
-rw-r--r--mystdlib.h32
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 @@
1CC?=gcc
2CFLAGS=-Os
3LDFLAGS=-s
4
5el: el.c mystdlib.c mystdlib.h
6 $(CC) -o el el.c mystdlib.c $(CFLAGS) $(LDFLAGS)
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}
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
12MAP 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
41void 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
49int 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
4typedef 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*/
20MAP 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*/
27void unmap_file ( MAP *pMap );
28
29/* Gets file size of open file
30 returns != 0 in case of error */
31inline int getfilesize( int fd, unsigned long *size );
32