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 | |||
