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