| /**************************************************************************** |
| * libs/libc/stdio/lib_open_memstream.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <errno.h> |
| |
| #include "libc.h" |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct memstream_cookie_s |
| { |
| FAR char **buf; /* Memory buffer */ |
| char saved; /* char at sizep before '\0' */ |
| size_t *sizep; |
| off_t size; /* Allocated buffer size */ |
| off_t end; /* Maximum position we have written to */ |
| off_t pos; /* Current position in the buffer */ |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: memstream_write |
| ****************************************************************************/ |
| |
| static ssize_t memstream_write(FAR void *c, FAR const char *buf, |
| size_t size) |
| { |
| FAR struct memstream_cookie_s *memstream_cookie = |
| (FAR struct memstream_cookie_s *)c; |
| FAR char *buf_grow; |
| size_t new_size; |
| |
| if (memstream_cookie->pos + size + 1 > memstream_cookie->size) |
| { |
| /* We have to reallocate the buffer. */ |
| |
| new_size = memstream_cookie->pos + size + 1; |
| buf_grow = lib_realloc(*memstream_cookie->buf, new_size); |
| if (buf_grow == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| memset(buf_grow + memstream_cookie->end, 0, |
| new_size - memstream_cookie->size); |
| |
| *memstream_cookie->buf = buf_grow; |
| memstream_cookie->size = new_size; |
| } |
| |
| memcpy(*memstream_cookie->buf + memstream_cookie->pos, buf, size); |
| |
| /* POSIX: If a write moves the position to a value larger than |
| * the current length, the current length shall be set to this position. |
| * In this case a null character shall be appended to the current buffer. |
| */ |
| |
| memstream_cookie->pos += size; |
| if (memstream_cookie->pos > memstream_cookie->end) |
| { |
| memstream_cookie->end = memstream_cookie->pos; |
| } |
| else |
| { |
| memstream_cookie->saved = *(*memstream_cookie->buf + |
| memstream_cookie->pos); |
| } |
| |
| *memstream_cookie->sizep = memstream_cookie->pos; |
| *(*memstream_cookie->buf + memstream_cookie->pos) = '\0'; |
| |
| return size; |
| } |
| |
| /**************************************************************************** |
| * Name: memstream_seek |
| ****************************************************************************/ |
| |
| static off_t memstream_seek(FAR void *c, FAR off_t *offset, int whence) |
| { |
| FAR struct memstream_cookie_s *memstream_cookie = |
| (FAR struct memstream_cookie_s *)c; |
| off_t new_offset; |
| |
| switch (whence) |
| { |
| case SEEK_SET: |
| new_offset = *offset; |
| break; |
| case SEEK_END: |
| new_offset = memstream_cookie->end + *offset; |
| break; |
| case SEEK_CUR: |
| new_offset = memstream_cookie->pos + *offset; |
| break; |
| default: |
| set_errno(ENOTSUP); |
| return -1; |
| } |
| |
| /* Seek to negative value or value larger than maximum size shall fail. */ |
| |
| if (new_offset < 0 || new_offset > memstream_cookie->end) |
| { |
| set_errno(EINVAL); |
| return -1; |
| } |
| |
| if (memstream_cookie->pos < memstream_cookie->end) |
| { |
| /* Retrieve saved character if we painted in already written area. */ |
| |
| *(*memstream_cookie->buf + memstream_cookie->pos) = |
| memstream_cookie->saved; |
| } |
| |
| memstream_cookie->pos = new_offset; |
| if (memstream_cookie->pos < memstream_cookie->end) |
| { |
| /* We go backwards, therefore we have to write null character |
| * at memstream_cookie->pos. But we might want to keep this |
| * character for future seeks, so keep it in memstream_cookie->saved. |
| */ |
| |
| memstream_cookie->saved = *(*memstream_cookie->buf + |
| memstream_cookie->pos); |
| *(*memstream_cookie->buf + memstream_cookie->pos) = '\0'; |
| *memstream_cookie->sizep = memstream_cookie->pos; |
| } |
| else |
| { |
| *memstream_cookie->sizep = memstream_cookie->end; |
| } |
| |
| *offset = new_offset; |
| return new_offset; |
| } |
| |
| /**************************************************************************** |
| * Name: memstream_close |
| ****************************************************************************/ |
| |
| static int memstream_close(FAR void *c) |
| { |
| FAR struct memstream_cookie_s *memstream_cookie = |
| (FAR struct memstream_cookie_s *)c; |
| |
| lib_free(memstream_cookie); |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| FAR FILE *open_memstream(FAR char **bufp, FAR size_t *sizep) |
| { |
| cookie_io_functions_t memstream_io; |
| FAR struct memstream_cookie_s *memstream_cookie; |
| FAR FILE *filep; |
| |
| if (bufp == NULL || sizep == NULL) |
| { |
| set_errno(EINVAL); |
| return NULL; |
| } |
| |
| memstream_cookie = lib_zalloc(sizeof(struct memstream_cookie_s)); |
| if (memstream_cookie == NULL) |
| { |
| set_errno(ENOMEM); |
| return NULL; |
| } |
| |
| *bufp = NULL; |
| *sizep = 0; |
| memstream_cookie->buf = bufp; |
| memstream_cookie->sizep = sizep; |
| |
| memstream_io.read = NULL; |
| memstream_io.write = memstream_write; |
| memstream_io.seek = memstream_seek; |
| memstream_io.close = memstream_close; |
| |
| filep = fopencookie(memstream_cookie, "w", memstream_io); |
| if (filep == NULL) |
| { |
| lib_free(memstream_cookie); |
| return NULL; |
| } |
| |
| return filep; |
| } |