14ef02da0SSiva Chandra Reddy //===--- Implementation of a platform independent file data structure -----===//
24ef02da0SSiva Chandra Reddy //
34ef02da0SSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44ef02da0SSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information.
54ef02da0SSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64ef02da0SSiva Chandra Reddy //
74ef02da0SSiva Chandra Reddy //===----------------------------------------------------------------------===//
84ef02da0SSiva Chandra Reddy 
94ef02da0SSiva Chandra Reddy #include "file.h"
104ef02da0SSiva Chandra Reddy 
114ef02da0SSiva Chandra Reddy #include "src/__support/CPP/ArrayRef.h"
124ef02da0SSiva Chandra Reddy 
134ef02da0SSiva Chandra Reddy #include <errno.h>
14a0f6d12cSSiva Chandra Reddy #include <stdio.h>
154ef02da0SSiva Chandra Reddy #include <stdlib.h>
164ef02da0SSiva Chandra Reddy 
174ef02da0SSiva Chandra Reddy namespace __llvm_libc {
184ef02da0SSiva Chandra Reddy 
write_unlocked(const void * data,size_t len)19945e0220SSiva Chandra Reddy size_t File::write_unlocked(const void *data, size_t len) {
204ef02da0SSiva Chandra Reddy   if (!write_allowed()) {
214ef02da0SSiva Chandra Reddy     errno = EBADF;
224ef02da0SSiva Chandra Reddy     err = true;
234ef02da0SSiva Chandra Reddy     return 0;
244ef02da0SSiva Chandra Reddy   }
254ef02da0SSiva Chandra Reddy 
264ef02da0SSiva Chandra Reddy   prev_op = FileOp::WRITE;
274ef02da0SSiva Chandra Reddy 
286ce490e5SMichael Jones   if (bufmode == _IOFBF) { // fully buffered
296ce490e5SMichael Jones     return write_unlocked_fbf(data, len);
306ce490e5SMichael Jones   } else if (bufmode == _IOLBF) { // line buffered
316ce490e5SMichael Jones     return write_unlocked_lbf(data, len);
326ce490e5SMichael Jones   } else /*if (bufmode == _IONBF) */ { // unbuffered
336ce490e5SMichael Jones     size_t ret_val = write_unlocked_nbf(data, len);
346ce490e5SMichael Jones     flush_unlocked();
356ce490e5SMichael Jones     return ret_val;
366ce490e5SMichael Jones   }
376ce490e5SMichael Jones }
386ce490e5SMichael Jones 
write_unlocked_nbf(const void * data,size_t len)396ce490e5SMichael Jones size_t File::write_unlocked_nbf(const void *data, size_t len) {
406ce490e5SMichael Jones   if (pos > 0) { // If the buffer is not empty
416ce490e5SMichael Jones     // Flush the buffer
426ce490e5SMichael Jones     const size_t write_size = pos;
436ce490e5SMichael Jones     size_t bytes_written = platform_write(this, buf, write_size);
446ce490e5SMichael Jones     pos = 0; // Buffer is now empty so reset pos to the beginning.
456ce490e5SMichael Jones     // If less bytes were written than expected, then an error occurred.
466ce490e5SMichael Jones     if (bytes_written < write_size) {
476ce490e5SMichael Jones       err = true;
486ce490e5SMichael Jones       return 0; // No bytes from data were written, so return 0.
496ce490e5SMichael Jones     }
506ce490e5SMichael Jones   }
516ce490e5SMichael Jones 
526ce490e5SMichael Jones   size_t written = platform_write(this, data, len);
536ce490e5SMichael Jones   if (written < len)
546ce490e5SMichael Jones     err = true;
556ce490e5SMichael Jones   return written;
566ce490e5SMichael Jones }
576ce490e5SMichael Jones 
write_unlocked_fbf(const void * data,size_t len)586ce490e5SMichael Jones size_t File::write_unlocked_fbf(const void *data, size_t len) {
596ce490e5SMichael Jones   const size_t init_pos = pos;
606ce490e5SMichael Jones   const size_t bufspace = bufsize - pos;
616ce490e5SMichael Jones 
626ce490e5SMichael Jones   // If data is too large to be buffered at all, then just write it unbuffered.
636ce490e5SMichael Jones   if (len > bufspace + bufsize)
646ce490e5SMichael Jones     return write_unlocked_nbf(data, len);
656ce490e5SMichael Jones 
666ce490e5SMichael Jones   // we split |data| (conceptually) using the split point. Then we handle the
676ce490e5SMichael Jones   // two pieces separately.
686ce490e5SMichael Jones   const size_t split_point = len < bufspace ? len : bufspace;
696ce490e5SMichael Jones 
706ce490e5SMichael Jones   // The primary piece is the piece of |data| we want to write to the buffer
716ce490e5SMichael Jones   // before flushing. It will always fit into the buffer, since the split point
726ce490e5SMichael Jones   // is defined as being min(len, bufspace), and it will always exist if len is
736ce490e5SMichael Jones   // non-zero.
746ce490e5SMichael Jones   cpp::ArrayRef<uint8_t> primary(data, split_point);
756ce490e5SMichael Jones 
766ce490e5SMichael Jones   // The second piece is the remainder of |data|. It is written to the buffer if
776ce490e5SMichael Jones   // it fits, or written directly to the output if it doesn't. If the primary
786ce490e5SMichael Jones   // piece fits entirely in the buffer, the remainder may be nothing.
796ce490e5SMichael Jones   cpp::ArrayRef<uint8_t> remainder(
806ce490e5SMichael Jones       static_cast<const uint8_t *>(data) + split_point, len - split_point);
816ce490e5SMichael Jones 
824ef02da0SSiva Chandra Reddy   cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
834ef02da0SSiva Chandra Reddy 
846ce490e5SMichael Jones   // Copy the first piece into the buffer.
854ef02da0SSiva Chandra Reddy   // TODO: Replace the for loop below with a call to internal memcpy.
866ce490e5SMichael Jones   for (size_t i = 0; i < primary.size(); ++i)
876ce490e5SMichael Jones     bufref[pos + i] = primary[i];
886ce490e5SMichael Jones   pos += primary.size();
896ce490e5SMichael Jones 
906ce490e5SMichael Jones   // If there is no remainder, we can return early, since the first piece has
916ce490e5SMichael Jones   // fit completely into the buffer.
926ce490e5SMichael Jones   if (remainder.size() == 0)
934ef02da0SSiva Chandra Reddy     return len;
944ef02da0SSiva Chandra Reddy 
956ce490e5SMichael Jones   // We need to flush the buffer now, since there is still data and the buffer
966ce490e5SMichael Jones   // is full.
976ce490e5SMichael Jones   const size_t write_size = pos;
986ce490e5SMichael Jones   size_t bytes_written = platform_write(this, buf, write_size);
994ef02da0SSiva Chandra Reddy   pos = 0; // Buffer is now empty so reset pos to the beginning.
1006ce490e5SMichael Jones   // If less bytes were written than expected, then an error occurred. Return
1016ce490e5SMichael Jones   // the number of bytes that have been written from |data|.
1026ce490e5SMichael Jones   if (bytes_written < write_size) {
1034ef02da0SSiva Chandra Reddy     err = true;
1046ce490e5SMichael Jones     return bytes_written <= init_pos ? 0 : bytes_written - init_pos;
1054ef02da0SSiva Chandra Reddy   }
1064ef02da0SSiva Chandra Reddy 
1076ce490e5SMichael Jones   // The second piece is handled basically the same as the first, although we
1086ce490e5SMichael Jones   // know that if the second piece has data in it then the buffer has been
1096ce490e5SMichael Jones   // flushed, meaning that pos is always 0.
1106ce490e5SMichael Jones   if (remainder.size() < bufsize) {
1114ef02da0SSiva Chandra Reddy     // TODO: Replace the for loop below with a call to internal memcpy.
1126ce490e5SMichael Jones     for (size_t i = 0; i < remainder.size(); ++i)
1136ce490e5SMichael Jones       bufref[i] = remainder[i];
1146ce490e5SMichael Jones     pos = remainder.size();
1156ce490e5SMichael Jones   } else {
1166ce490e5SMichael Jones     size_t bytes_written =
1176ce490e5SMichael Jones         platform_write(this, remainder.data(), remainder.size());
1186ce490e5SMichael Jones 
1196ce490e5SMichael Jones     // If less bytes were written than expected, then an error occurred. Return
1206ce490e5SMichael Jones     // the number of bytes that have been written from |data|.
1216ce490e5SMichael Jones     if (bytes_written < remainder.size()) {
1226ce490e5SMichael Jones       err = true;
1236ce490e5SMichael Jones       return primary.size() + bytes_written;
1246ce490e5SMichael Jones     }
1256ce490e5SMichael Jones   }
1266ce490e5SMichael Jones 
1274ef02da0SSiva Chandra Reddy   return len;
1284ef02da0SSiva Chandra Reddy }
1294ef02da0SSiva Chandra Reddy 
write_unlocked_lbf(const void * data,size_t len)1306ce490e5SMichael Jones size_t File::write_unlocked_lbf(const void *data, size_t len) {
1316ce490e5SMichael Jones   constexpr char NEWLINE_CHAR = '\n';
1326ce490e5SMichael Jones   size_t last_newline = len;
133*5bcda1d3SMichael Jones   for (size_t i = len; i > 1; --i) {
134*5bcda1d3SMichael Jones     if (static_cast<const char *>(data)[i - 1] == NEWLINE_CHAR) {
135*5bcda1d3SMichael Jones       last_newline = i - 1;
1366ce490e5SMichael Jones       break;
1374ef02da0SSiva Chandra Reddy     }
1386ce490e5SMichael Jones   }
1396ce490e5SMichael Jones 
1406ce490e5SMichael Jones   // If there is no newline, treat this as fully buffered.
1416ce490e5SMichael Jones   if (last_newline == len) {
1426ce490e5SMichael Jones     return write_unlocked_fbf(data, len);
1436ce490e5SMichael Jones   }
1446ce490e5SMichael Jones 
1456ce490e5SMichael Jones   // we split |data| (conceptually) using the split point. Then we handle the
1466ce490e5SMichael Jones   // two pieces separately.
1476ce490e5SMichael Jones   const size_t split_point = last_newline + 1;
1486ce490e5SMichael Jones 
1496ce490e5SMichael Jones   // The primary piece is everything in |data| up to the newline. It's written
1506ce490e5SMichael Jones   // unbuffered to the output.
1516ce490e5SMichael Jones   cpp::ArrayRef<uint8_t> primary(data, split_point);
1526ce490e5SMichael Jones 
1536ce490e5SMichael Jones   // The second piece is the remainder of |data|. It is written fully buffered,
1546ce490e5SMichael Jones   // meaning it may stay in the buffer if it fits.
1556ce490e5SMichael Jones   cpp::ArrayRef<uint8_t> remainder(
1566ce490e5SMichael Jones       static_cast<const uint8_t *>(data) + split_point, len - split_point);
1576ce490e5SMichael Jones 
1586ce490e5SMichael Jones   size_t written = 0;
1596ce490e5SMichael Jones 
1606ce490e5SMichael Jones   written = write_unlocked_nbf(primary.data(), primary.size());
1616ce490e5SMichael Jones   if (written < primary.size()) {
1626ce490e5SMichael Jones     err = true;
1636ce490e5SMichael Jones     return written;
1646ce490e5SMichael Jones   }
1656ce490e5SMichael Jones 
1666ce490e5SMichael Jones   flush_unlocked();
1676ce490e5SMichael Jones 
1686ce490e5SMichael Jones   written += write_unlocked_fbf(remainder.data(), remainder.size());
1690f72a0d2SSiva Chandra Reddy   if (written < len) {
1706ce490e5SMichael Jones     err = true;
1716ce490e5SMichael Jones     return written;
1726ce490e5SMichael Jones   }
1736ce490e5SMichael Jones 
1744ef02da0SSiva Chandra Reddy   return len;
1754ef02da0SSiva Chandra Reddy }
1764ef02da0SSiva Chandra Reddy 
read_unlocked(void * data,size_t len)177945e0220SSiva Chandra Reddy size_t File::read_unlocked(void *data, size_t len) {
1784ef02da0SSiva Chandra Reddy   if (!read_allowed()) {
1794ef02da0SSiva Chandra Reddy     errno = EBADF;
1804ef02da0SSiva Chandra Reddy     err = true;
1814ef02da0SSiva Chandra Reddy     return 0;
1824ef02da0SSiva Chandra Reddy   }
1834ef02da0SSiva Chandra Reddy 
1844ef02da0SSiva Chandra Reddy   prev_op = FileOp::READ;
1854ef02da0SSiva Chandra Reddy 
1864ef02da0SSiva Chandra Reddy   cpp::MutableArrayRef<uint8_t> bufref(buf, bufsize);
1874ef02da0SSiva Chandra Reddy   cpp::MutableArrayRef<uint8_t> dataref(data, len);
1884ef02da0SSiva Chandra Reddy 
1894ef02da0SSiva Chandra Reddy   // Because read_limit is always greater than equal to pos,
1904ef02da0SSiva Chandra Reddy   // available_data is never a wrapped around value.
1914ef02da0SSiva Chandra Reddy   size_t available_data = read_limit - pos;
1924ef02da0SSiva Chandra Reddy   if (len <= available_data) {
1934ef02da0SSiva Chandra Reddy     // TODO: Replace the for loop below with a call to internal memcpy.
1944ef02da0SSiva Chandra Reddy     for (size_t i = 0; i < len; ++i)
1954ef02da0SSiva Chandra Reddy       dataref[i] = bufref[i + pos];
1964ef02da0SSiva Chandra Reddy     pos += len;
1974ef02da0SSiva Chandra Reddy     return len;
1984ef02da0SSiva Chandra Reddy   }
1994ef02da0SSiva Chandra Reddy 
2004ef02da0SSiva Chandra Reddy   // Copy all of the available data.
2014ef02da0SSiva Chandra Reddy   // TODO: Replace the for loop with a call to internal memcpy.
2024ef02da0SSiva Chandra Reddy   for (size_t i = 0; i < available_data; ++i)
2034ef02da0SSiva Chandra Reddy     dataref[i] = bufref[i + pos];
2044ef02da0SSiva Chandra Reddy   read_limit = pos = 0; // Reset the pointers.
2054ef02da0SSiva Chandra Reddy 
2064ef02da0SSiva Chandra Reddy   size_t to_fetch = len - available_data;
2074ef02da0SSiva Chandra Reddy   if (to_fetch > bufsize) {
2084ef02da0SSiva Chandra Reddy     size_t fetched_size = platform_read(this, data, to_fetch);
2094ef02da0SSiva Chandra Reddy     if (fetched_size < to_fetch) {
2104ef02da0SSiva Chandra Reddy       if (errno == 0)
2114ef02da0SSiva Chandra Reddy         eof = true;
2124ef02da0SSiva Chandra Reddy       else
2134ef02da0SSiva Chandra Reddy         err = true;
2144ef02da0SSiva Chandra Reddy       return available_data + fetched_size;
2154ef02da0SSiva Chandra Reddy     }
2164ef02da0SSiva Chandra Reddy     return len;
2174ef02da0SSiva Chandra Reddy   }
2184ef02da0SSiva Chandra Reddy 
2194ef02da0SSiva Chandra Reddy   // Fetch and buffer another buffer worth of data.
2204ef02da0SSiva Chandra Reddy   size_t fetched_size = platform_read(this, buf, bufsize);
2214ef02da0SSiva Chandra Reddy   read_limit += fetched_size;
2224ef02da0SSiva Chandra Reddy   size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size;
2234ef02da0SSiva Chandra Reddy   for (size_t i = 0; i < transfer_size; ++i)
2244ef02da0SSiva Chandra Reddy     dataref[i] = bufref[i];
2254ef02da0SSiva Chandra Reddy   pos += transfer_size;
2264ef02da0SSiva Chandra Reddy   if (fetched_size < to_fetch) {
2274ef02da0SSiva Chandra Reddy     if (errno == 0)
2284ef02da0SSiva Chandra Reddy       eof = true;
2294ef02da0SSiva Chandra Reddy     else
2304ef02da0SSiva Chandra Reddy       err = true;
2314ef02da0SSiva Chandra Reddy   }
2324ef02da0SSiva Chandra Reddy   return transfer_size + available_data;
2334ef02da0SSiva Chandra Reddy }
2344ef02da0SSiva Chandra Reddy 
seek(long offset,int whence)2354ef02da0SSiva Chandra Reddy int File::seek(long offset, int whence) {
2364ef02da0SSiva Chandra Reddy   FileLock lock(this);
2374ef02da0SSiva Chandra Reddy   if (prev_op == FileOp::WRITE && pos > 0) {
2384ef02da0SSiva Chandra Reddy     size_t transferred_size = platform_write(this, buf, pos);
2394ef02da0SSiva Chandra Reddy     if (transferred_size < pos) {
2404ef02da0SSiva Chandra Reddy       err = true;
2414ef02da0SSiva Chandra Reddy       return -1;
2424ef02da0SSiva Chandra Reddy     }
243a0f6d12cSSiva Chandra Reddy   } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
244a0f6d12cSSiva Chandra Reddy     // More data could have been read out from the platform file than was
245a0f6d12cSSiva Chandra Reddy     // required. So, we have to adjust the offset we pass to platform seek
246a0f6d12cSSiva Chandra Reddy     // function. Note that read_limit >= pos is always true.
247a0f6d12cSSiva Chandra Reddy     offset -= (read_limit - pos);
2484ef02da0SSiva Chandra Reddy   }
2494ef02da0SSiva Chandra Reddy   pos = read_limit = 0;
2504ef02da0SSiva Chandra Reddy   prev_op = FileOp::SEEK;
2514ef02da0SSiva Chandra Reddy   // Reset the eof flag as a seek might move the file positon to some place
2524ef02da0SSiva Chandra Reddy   // readable.
2534ef02da0SSiva Chandra Reddy   eof = false;
2544ef02da0SSiva Chandra Reddy   return platform_seek(this, offset, whence);
2554ef02da0SSiva Chandra Reddy }
2564ef02da0SSiva Chandra Reddy 
flush_unlocked()2576ce490e5SMichael Jones int File::flush_unlocked() {
2584ef02da0SSiva Chandra Reddy   if (prev_op == FileOp::WRITE && pos > 0) {
2594ef02da0SSiva Chandra Reddy     size_t transferred_size = platform_write(this, buf, pos);
2604ef02da0SSiva Chandra Reddy     if (transferred_size < pos) {
2614ef02da0SSiva Chandra Reddy       err = true;
2624ef02da0SSiva Chandra Reddy       return -1;
2634ef02da0SSiva Chandra Reddy     }
2644ef02da0SSiva Chandra Reddy     pos = 0;
2654ef02da0SSiva Chandra Reddy     return platform_flush(this);
2664ef02da0SSiva Chandra Reddy   }
26722f9dca1SSiva Chandra Reddy   // TODO: Add POSIX behavior for input streams.
2684ef02da0SSiva Chandra Reddy   return 0;
2694ef02da0SSiva Chandra Reddy }
2704ef02da0SSiva Chandra Reddy 
close()2714ef02da0SSiva Chandra Reddy int File::close() {
2724ef02da0SSiva Chandra Reddy   {
2734ef02da0SSiva Chandra Reddy     FileLock lock(this);
2744ef02da0SSiva Chandra Reddy     if (prev_op == FileOp::WRITE && pos > 0) {
2754ef02da0SSiva Chandra Reddy       size_t transferred_size = platform_write(this, buf, pos);
2764ef02da0SSiva Chandra Reddy       if (transferred_size < pos) {
2774ef02da0SSiva Chandra Reddy         err = true;
2784ef02da0SSiva Chandra Reddy         return -1;
2794ef02da0SSiva Chandra Reddy       }
2804ef02da0SSiva Chandra Reddy     }
2814ef02da0SSiva Chandra Reddy     if (platform_close(this) != 0)
2824ef02da0SSiva Chandra Reddy       return -1;
2834ef02da0SSiva Chandra Reddy     if (own_buf)
2844ef02da0SSiva Chandra Reddy       free(buf);
2854ef02da0SSiva Chandra Reddy   }
2864ef02da0SSiva Chandra Reddy   free(this);
2874ef02da0SSiva Chandra Reddy   return 0;
2884ef02da0SSiva Chandra Reddy }
2894ef02da0SSiva Chandra Reddy 
set_buffer(void * buffer,size_t size,bool owned)2904ef02da0SSiva Chandra Reddy void File::set_buffer(void *buffer, size_t size, bool owned) {
2914ef02da0SSiva Chandra Reddy   if (own_buf)
2924ef02da0SSiva Chandra Reddy     free(buf);
2934ef02da0SSiva Chandra Reddy   buf = buffer;
2944ef02da0SSiva Chandra Reddy   bufsize = size;
2954ef02da0SSiva Chandra Reddy   own_buf = owned;
2964ef02da0SSiva Chandra Reddy }
2974ef02da0SSiva Chandra Reddy 
mode_flags(const char * mode)2984ef02da0SSiva Chandra Reddy File::ModeFlags File::mode_flags(const char *mode) {
2994ef02da0SSiva Chandra Reddy   // First character in |mode| should be 'a', 'r' or 'w'.
3004ef02da0SSiva Chandra Reddy   if (*mode != 'a' && *mode != 'r' && *mode != 'w')
3014ef02da0SSiva Chandra Reddy     return 0;
3024ef02da0SSiva Chandra Reddy 
3034ef02da0SSiva Chandra Reddy   // There should be exaclty one main mode ('a', 'r' or 'w') character.
3044ef02da0SSiva Chandra Reddy   // If there are more than one main mode characters listed, then
3054ef02da0SSiva Chandra Reddy   // we will consider |mode| as incorrect and return 0;
3064ef02da0SSiva Chandra Reddy   int main_mode_count = 0;
3074ef02da0SSiva Chandra Reddy 
3084ef02da0SSiva Chandra Reddy   ModeFlags flags = 0;
3094ef02da0SSiva Chandra Reddy   for (; *mode != '\0'; ++mode) {
3104ef02da0SSiva Chandra Reddy     switch (*mode) {
3114ef02da0SSiva Chandra Reddy     case 'r':
3124ef02da0SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(OpenMode::READ);
3134ef02da0SSiva Chandra Reddy       ++main_mode_count;
3144ef02da0SSiva Chandra Reddy       break;
3154ef02da0SSiva Chandra Reddy     case 'w':
3164ef02da0SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(OpenMode::WRITE);
3174ef02da0SSiva Chandra Reddy       ++main_mode_count;
3184ef02da0SSiva Chandra Reddy       break;
3194ef02da0SSiva Chandra Reddy     case '+':
32029a631a2SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(OpenMode::PLUS);
3214ef02da0SSiva Chandra Reddy       break;
3224ef02da0SSiva Chandra Reddy     case 'b':
3234ef02da0SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(ContentType::BINARY);
3244ef02da0SSiva Chandra Reddy       break;
3254ef02da0SSiva Chandra Reddy     case 'a':
3264ef02da0SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(OpenMode::APPEND);
3274ef02da0SSiva Chandra Reddy       ++main_mode_count;
3284ef02da0SSiva Chandra Reddy       break;
3294ef02da0SSiva Chandra Reddy     case 'x':
3304ef02da0SSiva Chandra Reddy       flags |= static_cast<ModeFlags>(CreateType::EXCLUSIVE);
3314ef02da0SSiva Chandra Reddy       break;
3324ef02da0SSiva Chandra Reddy     default:
3334ef02da0SSiva Chandra Reddy       return 0;
3344ef02da0SSiva Chandra Reddy     }
3354ef02da0SSiva Chandra Reddy   }
3364ef02da0SSiva Chandra Reddy 
3374ef02da0SSiva Chandra Reddy   if (main_mode_count != 1)
3384ef02da0SSiva Chandra Reddy     return 0;
3394ef02da0SSiva Chandra Reddy 
3404ef02da0SSiva Chandra Reddy   return flags;
3414ef02da0SSiva Chandra Reddy }
3424ef02da0SSiva Chandra Reddy 
3434ef02da0SSiva Chandra Reddy } // namespace __llvm_libc
344