libopkg: do not rely on getline()
[oweals/opkg-lede.git] / libopkg / parse_util.c
index e01b12427efee895f38a173e4360346864af7e4f..fbecba6eb700954b811c6e09a712e02a00cee2f7 100644 (file)
@@ -22,6 +22,7 @@
 #include "libbb/libbb.h"
 
 #include "parse_util.h"
+#include "pkg_parse.h"
 
 int
 is_field(const char *type, const char *line)
@@ -86,3 +87,84 @@ parse_list(const char *raw, unsigned int *count, const char sep, int skip_field)
        *count = line_count;
        return depends;
 }
+
+int
+parse_from_stream_nomalloc(parse_line_t parse_line, void *item, FILE *fp, uint mask,
+                                               char **buf0, size_t buf0len)
+{
+       int ret, lineno;
+       char *buf, *nl;
+       size_t buflen;
+
+       lineno = 1;
+       ret = 0;
+
+       buflen = buf0len;
+       buf = *buf0;
+       buf[0] = '\0';
+
+       while (1) {
+               if (fgets(buf, (int)buflen, fp) == NULL) {
+                       if (ferror(fp)) {
+                               opkg_perror(ERROR, "fgets");
+                               ret = -1;
+                       } else if (strlen(*buf0) == buf0len-1) {
+                               opkg_msg(ERROR, "Missing new line character"
+                                               " at end of file!\n");
+                               parse_line(item, *buf0, mask);
+                       }
+                       break;
+               }
+
+               nl = strchr(buf, '\n');
+               if (nl == NULL) {
+                       if (strlen(buf) < buflen-1) {
+                               /*
+                                * Line could be exactly buflen-1 long and
+                                * missing a newline, but we won't know until
+                                * fgets fails to read more data.
+                                */
+                               opkg_msg(ERROR, "Missing new line character"
+                                               " at end of file!\n");
+                               parse_line(item, *buf0, mask);
+                               break;
+                       }
+                       if (buf0len >= EXCESSIVE_LINE_LEN) {
+                               opkg_msg(ERROR, "Excessively long line at "
+                                       "%d. Corrupt file?\n",
+                                       lineno);
+                               ret = -1;
+                               break;
+                       }
+
+                       /*
+                        * Realloc and point buf past the data already read,
+                        * at the NULL terminator inserted by fgets.
+                        * |<--------------- buf0len ----------------->|
+                        * |                     |<------- buflen ---->|
+                        * |---------------------|---------------------|
+                        * buf0                   buf
+                        */
+                       buflen = buf0len +1;
+                       buf0len *= 2;
+                       *buf0 = xrealloc(*buf0, buf0len);
+                       buf = *buf0 + buflen -2;
+
+                       continue;
+               }
+
+               *nl = '\0';
+
+               lineno++;
+
+               if (parse_line(item, *buf0, mask))
+                       break;
+
+               buf = *buf0;
+               buflen = buf0len;
+               buf[0] = '\0';
+       }
+
+       return ret;
+}
+