busybox/libbb/get_line_from_file.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Utility routines.
   4 *
   5 * Copyright (C) 2005, 2006 Rob Landley <rob@landley.net>
   6 * Copyright (C) 2004 Erik Andersen <andersen@codepoet.org>
   7 * Copyright (C) 2001 Matt Krai
   8 *
   9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  10 */
  11
  12#include "libbb.h"
  13
  14char* FAST_FUNC bb_get_chunk_from_file(FILE *file, int *end)
  15{
  16        int ch;
  17        unsigned idx = 0;
  18        char *linebuf = NULL;
  19
  20        while ((ch = getc(file)) != EOF) {
  21                /* grow the line buffer as necessary */
  22                if (!(idx & 0xff))
  23                        linebuf = xrealloc(linebuf, idx + 0x100);
  24                linebuf[idx++] = (char) ch;
  25                if (ch == '\0')
  26                        break;
  27                if (end && ch == '\n')
  28                        break;
  29        }
  30        if (end)
  31                *end = idx;
  32        if (linebuf) {
  33                // huh, does fgets discard prior data on error like this?
  34                // I don't think so....
  35                //if (ferror(file)) {
  36                //      free(linebuf);
  37                //      return NULL;
  38                //}
  39                linebuf = xrealloc(linebuf, idx + 1);
  40                linebuf[idx] = '\0';
  41        }
  42        return linebuf;
  43}
  44
  45/* Get line, including trailing \n if any */
  46char* FAST_FUNC xmalloc_fgets(FILE *file)
  47{
  48        int i;
  49
  50        return bb_get_chunk_from_file(file, &i);
  51}
  52/* Get line.  Remove trailing \n */
  53char* FAST_FUNC xmalloc_fgetline(FILE *file)
  54{
  55        int i;
  56        char *c = bb_get_chunk_from_file(file, &i);
  57
  58        if (i && c[--i] == '\n')
  59                c[i] = '\0';
  60
  61        return c;
  62}
  63
  64#if 0
  65/* GNUism getline() should be faster (not tested) than a loop with fgetc */
  66
  67/* Get line, including trailing \n if any */
  68char* FAST_FUNC xmalloc_fgets(FILE *file)
  69{
  70        char *res_buf = NULL;
  71        size_t res_sz;
  72
  73        if (getline(&res_buf, &res_sz, file) == -1) {
  74                free(res_buf); /* uclibc allocates a buffer even on EOF. WTF? */
  75                res_buf = NULL;
  76        }
  77//TODO: trimming to res_sz?
  78        return res_buf;
  79}
  80/* Get line.  Remove trailing \n */
  81char* FAST_FUNC xmalloc_fgetline(FILE *file)
  82{
  83        char *res_buf = NULL;
  84        size_t res_sz;
  85
  86        res_sz = getline(&res_buf, &res_sz, file);
  87
  88        if ((ssize_t)res_sz != -1) {
  89                if (res_buf[res_sz - 1] == '\n')
  90                        res_buf[--res_sz] = '\0';
  91//TODO: trimming to res_sz?
  92        } else {
  93                free(res_buf); /* uclibc allocates a buffer even on EOF. WTF? */
  94                res_buf = NULL;
  95        }
  96        return res_buf;
  97}
  98
  99#endif
 100
 101#if 0
 102/* Faster routines (~twice as fast). +170 bytes. Unused as of 2008-07.
 103 *
 104 * NB: they stop at NUL byte too.
 105 * Performance is important here. Think "grep 50gigabyte_file"...
 106 * Ironically, grep can't use it because of NUL issue.
 107 * We sorely need C lib to provide fgets which reports size!
 108 *
 109 * Update:
 110 * Actually, uclibc and glibc have it. man getline. It's GNUism,
 111 *   but very useful one (if it's as fast as this code).
 112 * TODO:
 113 * - currently, sed and sort use bb_get_chunk_from_file and heavily
 114 *   depend on its "stop on \n or \0" behavior, and STILL they fail
 115 *   to handle all cases with embedded NULs correctly. So:
 116 * - audit sed and sort; convert them to getline FIRST.
 117 * - THEN ditch bb_get_chunk_from_file, replace it with getline.
 118 * - provide getline implementation for non-GNU systems.
 119 */
 120
 121static char* xmalloc_fgets_internal(FILE *file, int *sizep)
 122{
 123        int len;
 124        int idx = 0;
 125        char *linebuf = NULL;
 126
 127        while (1) {
 128                char *r;
 129
 130                linebuf = xrealloc(linebuf, idx + 0x100);
 131                r = fgets(&linebuf[idx], 0x100, file);
 132                if (!r) {
 133                        /* need to terminate in case this is error
 134                         * (EOF puts NUL itself) */
 135                        linebuf[idx] = '\0';
 136                        break;
 137                }
 138                /* stupid. fgets knows the len, it should report it somehow */
 139                len = strlen(&linebuf[idx]);
 140                idx += len;
 141                if (len != 0xff || linebuf[idx - 1] == '\n')
 142                        break;
 143        }
 144        *sizep = idx;
 145        if (idx) {
 146                /* xrealloc(linebuf, idx + 1) is up to caller */
 147                return linebuf;
 148        }
 149        free(linebuf);
 150        return NULL;
 151}
 152
 153/* Get line, remove trailing \n */
 154char* FAST_FUNC xmalloc_fgetline_fast(FILE *file)
 155{
 156        int sz;
 157        char *r = xmalloc_fgets_internal(file, &sz);
 158        if (r && r[sz - 1] == '\n')
 159                r[--sz] = '\0';
 160        return r; /* not xrealloc(r, sz + 1)! */
 161}
 162
 163char* FAST_FUNC xmalloc_fgets(FILE *file)
 164{
 165        int sz;
 166        return xmalloc_fgets_internal(file, &sz);
 167}
 168
 169/* Get line, remove trailing \n */
 170char* FAST_FUNC xmalloc_fgetline(FILE *file)
 171{
 172        int sz;
 173        char *r = xmalloc_fgets_internal(file, &sz);
 174        if (!r)
 175                return r;
 176        if (r[sz - 1] == '\n')
 177                r[--sz] = '\0';
 178        return xrealloc(r, sz + 1);
 179}
 180#endif
 181