/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | | Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.zend.com/license/2_00.txt. | | If you did not receive a copy of the Zend license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@zend.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "hphp/runtime/base/plain_file.h" #include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/base/request_local.h" #include namespace HPHP { IMPLEMENT_OBJECT_ALLOCATION(PlainFile) /////////////////////////////////////////////////////////////////////////////// StaticString PlainFile::s_class_name("PlainFile"); /////////////////////////////////////////////////////////////////////////////// // constructor and destructor PlainFile::PlainFile(FILE *stream, bool nonblocking) : File(nonblocking), m_stream(stream), m_eof(false), m_buffer(nullptr) { if (stream) { m_fd = fileno(stream); m_buffer = (char *)malloc(BUFSIZ); if (m_buffer) setbuffer(stream, m_buffer, BUFSIZ); } } PlainFile::PlainFile(int fd, bool nonblocking) : File(nonblocking), m_stream(nullptr), m_eof(false), m_buffer(nullptr) { m_fd = fd; } PlainFile::~PlainFile() { closeImpl(); } bool PlainFile::open(CStrRef filename, CStrRef mode) { int fd; FILE *f; assert(m_stream == nullptr); assert(m_fd == -1); // For these definded in php fopen but C stream have different modes switch (mode[0]) { case 'x': if (mode.find('+') == -1) { fd = ::open(filename.data(), O_WRONLY|O_CREAT|O_EXCL, 0666); if (fd < 0) return false; f = fdopen(fd, "w"); } else { fd = ::open(filename.data(), O_RDWR|O_CREAT|O_EXCL, 0666); if (fd < 0) return false; f = fdopen(fd, "w+"); } break; case 'c': if (mode.find('+') == -1) { fd = ::open(filename.data(), O_WRONLY|O_CREAT, 0666); if (fd < 0) return false; f = fdopen(fd, "w"); } else { fd = ::open(filename.data(), O_RDWR|O_CREAT, 0666); if (fd < 0) return false; f = fdopen(fd, "w+"); } break; default: f = fopen(filename.data(), mode.data()); } if (!f) { return false; } m_stream = f; m_fd = fileno(f); m_buffer = (char *)malloc(BUFSIZ); if (m_buffer) setbuffer(f, m_buffer, BUFSIZ); return true; } bool PlainFile::close() { return closeImpl(); } bool PlainFile::closeImpl() { bool ret = true; s_file_data->m_pcloseRet = 0; if (!m_closed) { if (m_stream) { s_file_data->m_pcloseRet = fclose(m_stream); m_stream = nullptr; } else if (m_fd >= 0) { s_file_data->m_pcloseRet = ::close(m_fd); } if (m_buffer) { free(m_buffer); m_buffer = nullptr; } ret = (s_file_data->m_pcloseRet == 0); m_closed = true; m_fd = -1; } File::closeImpl(); return ret; } /////////////////////////////////////////////////////////////////////////////// // virtual functions int64_t PlainFile::readImpl(char *buffer, int64_t length) { assert(valid()); assert(length > 0); // use read instead of fread to handle EOL in stdin size_t ret = ::read(m_fd, buffer, length); if (ret == 0 || (ret == (size_t)-1 && errno != EWOULDBLOCK && errno != EINTR && errno != EBADF)) { m_eof = true; } return ret == (size_t)-1 ? 0 : ret; } int PlainFile::getc() { assert(valid()); return File::getc(); } String PlainFile::read(int64_t length) { if (length) m_eof = false; return File::read(length); } int64_t PlainFile::writeImpl(const char *buffer, int64_t length) { assert(valid()); assert(length > 0); // use write instead of fwrite to be consistent with read // o.w., read-and-write files would not work int64_t written = ::write(m_fd, buffer, length); return written < 0 ? 0 : written; } bool PlainFile::seek(int64_t offset, int whence /* = SEEK_SET */) { assert(valid()); if (whence == SEEK_CUR) { off_t result = lseek(m_fd, 0, SEEK_CUR); if (result != (off_t)-1) { offset += result - (m_writepos - m_readpos + m_position); } if (offset > 0 && offset < m_writepos - m_readpos) { m_readpos += offset; m_position += offset; return true; } offset += m_position; whence = SEEK_SET; } // invalidate the current buffer m_writepos = 0; m_readpos = 0; // clear the eof flag m_eof = false; flush(); // lseek instead of seek to be consistent with read off_t result = lseek(m_fd, offset, whence); m_position = result; return result != (off_t)-1; } int64_t PlainFile::tell() { assert(valid()); return m_position; } bool PlainFile::eof() { assert(valid()); int64_t avail = m_writepos - m_readpos; if (avail > 0) { return false; } return m_eof; } bool PlainFile::rewind() { assert(valid()); seek(0); m_writepos = 0; m_readpos = 0; m_position = 0; return true; } bool PlainFile::flush() { if (m_stream) { return fflush(m_stream) == 0; } assert(valid()); // No need to flush a file descriptor. return true; } bool PlainFile::truncate(int64_t size) { assert(valid()); return ftruncate(m_fd, size) == 0; } /////////////////////////////////////////////////////////////////////////////// // BuiltinFiles BuiltinFile::~BuiltinFile() { m_closed = true; m_stream = nullptr; m_fd = -1; } bool BuiltinFile::close() { ::fclose(m_stream); m_closed = true; m_stream = nullptr; m_fd = -1; File::closeImpl(); return true; } IMPLEMENT_REQUEST_LOCAL(BuiltinFiles, g_builtin_files); void BuiltinFiles::requestInit() { GetSTDIN(); GetSTDOUT(); GetSTDERR(); } void BuiltinFiles::requestShutdown() { m_stdin.reset(); m_stdout.reset(); m_stderr.reset(); } CVarRef BuiltinFiles::GetSTDIN() { if (g_builtin_files->m_stdin.isNull()) { BuiltinFile *f = NEWOBJ(BuiltinFile)(stdin); g_builtin_files->m_stdin = f; f->o_setId(1); assert(f->o_getId() == 1); } return g_builtin_files->m_stdin; } CVarRef BuiltinFiles::GetSTDOUT() { if (g_builtin_files->m_stdout.isNull()) { BuiltinFile *f = NEWOBJ(BuiltinFile)(stdout); g_builtin_files->m_stdout = f; f->o_setId(2); assert(f->o_getId() == 2); } return g_builtin_files->m_stdout; } CVarRef BuiltinFiles::GetSTDERR() { if (g_builtin_files->m_stderr.isNull()) { BuiltinFile *f = NEWOBJ(BuiltinFile)(stderr); g_builtin_files->m_stderr = f; f->o_setId(3); assert(f->o_getId() == 3); } return g_builtin_files->m_stderr; } /////////////////////////////////////////////////////////////////////////////// }