LCOV - code coverage report
Current view: top level - src - creadline.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 66 0.0 %
Date: 2024-01-19 10:32:55 Functions: 0 3 0.0 %

          Line data    Source code
       1             : /*!
       2             :  * \file
       3             :  * \author Christopher Stender
       4             :  * \date 2018-02-15
       5             :  * \version 0.1
       6             :  *
       7             :  * \brief Read line from file descriptor
       8             :  *
       9             :  * \details
      10             :  * This readline implementation reads a single line from a file descriptor
      11             :  * (e.g. a socket). Two versions are provided.
      12             :  *
      13             :  *\copyright
      14             :  * Copyright (c) 2023 Fraunhofer IIS
      15             :  *
      16             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
      17             :  * of this software and associated documentation files (the “Software”), to deal
      18             :  * in the Software without restriction, including without limitation the rights
      19             :  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      20             :  * copies of the Software, and to permit persons to whom the Software is
      21             :  * furnished to do so, subject to the following conditions:
      22             :  *
      23             :  * The above copyright notice and this permission notice shall be included in
      24             :  * all copies or substantial portions of the Software.
      25             :  *
      26             :  * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      27             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      28             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      29             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      30             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      31             :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      32             :  * THE SOFTWARE.
      33             :  */
      34             : 
      35             : #include <stdio.h>
      36             : #include <string.h>
      37             : #include <stdlib.h>
      38             : #include <unistd.h>
      39             : #include <errno.h>
      40             : 
      41             : #include "creadline.h"
      42             : 
      43             : /* Do not change the maximum line size here! Define
      44             :  * CREADLINE_MAX_LINE_SIZE in the header file to
      45             :  * overwrite the default value. */
      46             : #ifndef CREADLINE_MAX_LINE_SIZE
      47             : /*! Internal definition of the maximum allowed line size. Can be overwritten by
      48             :  * defining CREADLINE_MAX_LINE_SIZE. */
      49             : #define __MAX_LINEBUF_SIZE__ 64*1024*1024
      50             : #else
      51             : #define __MAX_LINEBUF_SIZE__ CREADLINE_MAX_LINE_SIZE
      52             : #endif
      53             : 
      54           0 : static int strnpos(const char *s, int c, size_t maxlen)
      55             : {
      56             :         int pos;
      57             : 
      58           0 :         for (pos = 0; pos < maxlen; pos++) {
      59             : 
      60           0 :                 if (s[pos] == c)
      61           0 :                         return pos;
      62             : 
      63           0 :                 if (s[pos] == '\0')
      64           0 :                         return -1;
      65             :         }
      66             :         
      67           0 :         return -1;
      68             : }
      69             : 
      70           0 : int creadline_r(int fd, void **dst, creadline_buf_t *buffer)
      71             : {
      72             :         char *linebuf;
      73             :         size_t linebuf_size;
      74             :         int linebuf_nbytes;
      75             : 
      76             :         int readret;
      77             :         int nl_pos;
      78             :         int rem_nbytes;
      79             : 
      80             :         /* Initialize internal line buffer */
      81           0 :         linebuf_nbytes = 0;
      82           0 :         linebuf_size = CREADLINE_BLOCK_SIZE;
      83           0 :         linebuf = malloc(linebuf_size);
      84           0 :         if(linebuf == NULL) {
      85           0 :                 perror("malloc() failed");
      86           0 :                 goto error_ret;
      87             :         }
      88             : 
      89             :         /* Restore remaining characters from the last call */
      90           0 :         if (buffer->nbytes != 0) {
      91           0 :                 strncpy(linebuf, buffer->buf, buffer->nbytes);
      92           0 :                 linebuf_nbytes = buffer->nbytes;
      93           0 :                 buffer->nbytes = 0;
      94             :         }
      95             : 
      96             :         /* Check if linebuf contains a newline character */
      97           0 :         nl_pos = strnpos(linebuf, '\n', linebuf_nbytes);
      98             : 
      99           0 :         while (nl_pos < 0) {
     100             : 
     101             :                 /* No newline character found -> read more bytes and check again */
     102             : 
     103             :                 /* Check if there is enough space left to call read again. If a new
     104             :                  * read could write beyond the line buffer it's size is doubled as long
     105             :                  * as __MAX_LINEBUF_SIZE__ is not reached. */
     106             : 
     107           0 :                 if (linebuf_nbytes + CREADLINE_BLOCK_SIZE > linebuf_size) {
     108             : 
     109           0 :                         char* new_linebuf = NULL;
     110           0 :                         int new_linebuf_size = 2*linebuf_size;
     111             : 
     112           0 :                         if (new_linebuf_size > __MAX_LINEBUF_SIZE__) {
     113           0 :                                 fprintf(stderr, "ERROR: Maximum line size of %i bytes exceeded!\n", __MAX_LINEBUF_SIZE__);
     114           0 :                                 goto error_free;
     115             :                         }
     116             : 
     117           0 :                         new_linebuf = realloc(linebuf, new_linebuf_size);
     118           0 :                         if (new_linebuf == NULL) {
     119           0 :                                 perror("realloc() failed");
     120           0 :                                 goto error_free;
     121             :                         }
     122           0 :                         linebuf = new_linebuf;
     123           0 :                         linebuf_size = new_linebuf_size;
     124             :                 }
     125             : 
     126             :                 /* Read more bytes... */
     127           0 :                 readret = read(fd, linebuf+linebuf_nbytes, CREADLINE_BLOCK_SIZE);
     128           0 :                 if (readret < 0) {
     129             : 
     130           0 :                         if (errno == EINTR) { /* EINTR is not an error */
     131           0 :                                 continue;
     132             :                         }
     133             : 
     134           0 :                         perror("read() failed");
     135           0 :                         goto error_free;
     136             : 
     137           0 :                 } else if (readret == 0) { /* EOF */
     138             : 
     139           0 :                         if (linebuf_nbytes == 0) {
     140             : 
     141             :                                 /* It is important to set dst to NULL because a return value
     142             :                                  * of 0 can also indicate an zero-length string ("\0") */
     143           0 :                                 *dst = NULL;
     144           0 :                                 free(linebuf);
     145           0 :                                 return 0;
     146             : 
     147             :                         } else {
     148           0 :                                 fprintf(stderr, "ERROR: Received EOF while line buffer is not empty\n");
     149           0 :                                 goto error_free;
     150             :                         }
     151             :                 }
     152             : 
     153             :                 /* Check if the read data contains a newline character */
     154           0 :                 nl_pos = strnpos(linebuf+linebuf_nbytes, '\n', readret);
     155             : 
     156             :                 /* If a newline was found, get its absolute position */
     157           0 :                 if(nl_pos >= 0) {
     158           0 :                         nl_pos += linebuf_nbytes;
     159             :                 }
     160             : 
     161           0 :                 linebuf_nbytes += readret;
     162             :         }
     163             : 
     164             :         /* Found newline character */
     165             : 
     166             :         /* Copy characters located after the newline to the (external) buffer */
     167           0 :         rem_nbytes = linebuf_nbytes - nl_pos - 1;
     168           0 :         if (rem_nbytes > 0) {
     169           0 :                 strncpy(buffer->buf, linebuf+nl_pos+1, rem_nbytes);
     170           0 :                 buffer->nbytes = rem_nbytes;
     171             :         }
     172             : 
     173             :         /* Ignore '\r' before '\n' to handle also "\r\n" sequences */
     174           0 :         if ( (nl_pos > 0) && (linebuf[nl_pos-1] == '\r') ) {
     175           0 :                 nl_pos--;
     176             :         }
     177             : 
     178             :         /* Replace '\n' (or '\r' in front of a '\n') by
     179             :          * '\0' to terminate the string */
     180           0 :         linebuf[nl_pos] = '\0';
     181             : 
     182             :         /* Set dst pointer and return string length */
     183           0 :         *dst = linebuf;
     184           0 :         return nl_pos;
     185             : 
     186             : error_free:
     187           0 :         free(linebuf);
     188             : 
     189             : error_ret:
     190           0 :         *dst = NULL;
     191           0 :         return -1;
     192             : }
     193             : 
     194           0 : int creadline(int fd, void **dst)
     195             : {
     196             :         static int fd_last = -1;
     197             :         static creadline_buf_t buffer;
     198             : 
     199             :         /* Reset buffer if a new fd is used */
     200           0 :         if(fd_last != fd) {
     201           0 :                 buffer.nbytes = 0;
     202           0 :                 fd_last = fd;
     203             :         }
     204             : 
     205           0 :         return creadline_r(fd, dst, &buffer);
     206             : }
     207             : 

Generated by: LCOV version 1.13