/**************************************************************************** * libs/libc/stdio/lib_fmemopen.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 #include #include #include #include #include #include #include #include "libc.h" /**************************************************************************** * Private Types ****************************************************************************/ struct fmemopen_cookie_s { FAR char *buf; /* Memory buffer */ off_t pos; /* Current position in the buffer */ off_t end; /* End buffer position */ size_t size; /* Buffer size */ bool custom; /* True if custom buffer is used */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: fmemopen_read ****************************************************************************/ static ssize_t fmemopen_read(FAR void *c, FAR char *buf, size_t size) { FAR struct fmemopen_cookie_s *fmemopen_cookie = (FAR struct fmemopen_cookie_s *)c; if (fmemopen_cookie->pos + size > fmemopen_cookie->end) { size = fmemopen_cookie->end - fmemopen_cookie->pos; } memcpy(buf, fmemopen_cookie->buf + fmemopen_cookie->pos, size); fmemopen_cookie->pos += size; return size; } /**************************************************************************** * Name: fmemopen_write ****************************************************************************/ static ssize_t fmemopen_write(FAR void *c, FAR const char *buf, size_t size) { FAR struct fmemopen_cookie_s *fmemopen_cookie = (FAR struct fmemopen_cookie_s *)c; if (size + fmemopen_cookie->pos > fmemopen_cookie->size) { size = fmemopen_cookie->size - fmemopen_cookie->pos; } memcpy(fmemopen_cookie->buf + fmemopen_cookie->pos, buf, size); fmemopen_cookie->pos += size; if (fmemopen_cookie->pos > fmemopen_cookie->end) { fmemopen_cookie->end = fmemopen_cookie->pos; } /* POSIX states that NULL byte shall be written at the current position * or end of the buffer. */ if (fmemopen_cookie->pos < fmemopen_cookie->size && fmemopen_cookie->buf[fmemopen_cookie->pos - 1] != '\0') { fmemopen_cookie->buf[fmemopen_cookie->pos] = '\0'; } return size; } /**************************************************************************** * Name: fmemopen_seek ****************************************************************************/ static off_t fmemopen_seek(FAR void *c, FAR off_t *offset, int whence) { FAR struct fmemopen_cookie_s *fmemopen_cookie = (FAR struct fmemopen_cookie_s *)c; off_t new_offset; switch (whence) { case SEEK_SET: new_offset = *offset; break; case SEEK_END: new_offset = fmemopen_cookie->end + *offset; break; case SEEK_CUR: new_offset = fmemopen_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 > fmemopen_cookie->end) { set_errno(EINVAL); return -1; } fmemopen_cookie->pos = new_offset; *offset = new_offset; return new_offset; } /**************************************************************************** * Name: fmemopen_close ****************************************************************************/ static int fmemopen_close(FAR void *c) { FAR struct fmemopen_cookie_s *fmemopen_cookie = (FAR struct fmemopen_cookie_s *)c; if (fmemopen_cookie->custom) { lib_free(fmemopen_cookie->buf); } lib_free(fmemopen_cookie); return 0; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: fmemopen ****************************************************************************/ FAR FILE *fmemopen(FAR void *buf, size_t size, FAR const char *mode) { cookie_io_functions_t fmemopen_io; FAR struct fmemopen_cookie_s *fmemopen_cookie; FAR FILE *filep; int oflags; fmemopen_cookie = lib_zalloc(sizeof(struct fmemopen_cookie_s)); if (fmemopen_cookie == NULL) { set_errno(ENOMEM); return NULL; } oflags = lib_mode2oflags(mode); if (buf == NULL) { /* POSIX standard states: * Because this feature is only useful when the stream is opened for * updating (because there is no way to get a pointer to the buffer) * the fmemopen() call may fail if the mode argument does not * include a '+'. */ if ((oflags & O_RDWR) != O_RDWR) { lib_free(fmemopen_cookie); set_errno(EINVAL); return NULL; } /* Buf argument is NULL pointer and mode is correct. This buffer * will be freed when stream is closed so we have to keep the * information in fmemopen_cookie->custom. */ fmemopen_cookie->custom = true; fmemopen_cookie->buf = lib_zalloc(size); if (fmemopen_cookie->buf == NULL) { lib_free(fmemopen_cookie); set_errno(ENOMEM); return NULL; } /* If buf is a null pointer, the initial position shall always be set * to the beginning of the buffer. */ fmemopen_cookie->buf[0] = '\0'; } else { /* Buffer was already allocated by the user. */ fmemopen_cookie->custom = false; fmemopen_cookie->buf = buf; } fmemopen_cookie->size = size; fmemopen_cookie->pos = 0; /* For modes w and w+ the initial size shall be zero. */ if ((oflags & O_TRUNC) != 0) { fmemopen_cookie->end = 0; fmemopen_cookie->buf[0] = '\0'; } /* For modes r and r+ the size shall be set to the value given * by the size argument. */ if ((oflags & O_RDWR) == O_RDOK) { fmemopen_cookie->end = size; } /* For modes a and a+ the initial size shall be: * - Zero, if buf is a null pointer * - The position of the first null byte in the buffer, if one is foun * - The value of the size argument, if buf is not a null pointer and * no null byte is found */ if ((oflags & O_APPEND) != 0) { fmemopen_cookie->pos = fmemopen_cookie->end = strnlen(fmemopen_cookie->buf, fmemopen_cookie->size); } /* Assign fmemopen callbacks. */ fmemopen_io.read = fmemopen_read; fmemopen_io.write = fmemopen_write; fmemopen_io.seek = fmemopen_seek; fmemopen_io.close = fmemopen_close; /* Let fopencookie do the rest. */ filep = fopencookie(fmemopen_cookie, mode, fmemopen_io); if (filep == NULL) { if (fmemopen_cookie->custom) { lib_free(fmemopen_cookie->buf); } lib_free(fmemopen_cookie); return NULL; } return filep; }