// balst_stacktraceresolver_filehelper.h                              -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BALST_STACKTRACERESOLVER_FILEHELPER
#define INCLUDED_BALST_STACKTRACERESOLVER_FILEHELPER

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide platform-independent file input for stack trace resolvers.
//
//@CLASSES:
//   balst::StackTraceResolver_FileHelper: file input for stack trace resolvers
//
//@SEE_ALSO: balst_stacktraceresolverimpl_elf,
//           balst_stacktraceresolverimpl_xcoff
//
//@DESCRIPTION: The one class in this component opens a file in readonly mode
// and then owns the file descriptor, and provides 3 utility functions for
// reading from the file: 'readBytes', which attempts to read a number of bytes
// into a buffer, and does a partial read if it can't read that many;
// 'readExact', which either reads an exact number of bytes or fails, and
// 'loadString', which reads a 0 terminated string from the file, copies it to
// a buffer it allocates, and returns a pointer to the copy.
//
///Usage
///-----
//..
//  bslma::TestAllocator ta;
//
//  char fileNameBuffer[100];
//  sprintf(fileNameBuffer,
//          "/tmp/balst_StackTraceResolver_FileHelper.usage.%d.txt",
//          getProcessId());
//..
// Make sure file does not already exist.
//..
//  bdls::FilesystemUtil::remove(fileNameBuffer);
//..
// Next, Create the file and open a file descriptor to it.  The boolean
// flags indicate that the file is writable, and not previously existing
// (and therefore must be created).
//..
//  FdType fd = FilesystemUtil::open(fileNameBuffer,
//                             true,          // writable
//                             false);        // doesn't already exist
//  assert(FilesystemUtil::k_INVALID_FD != fd);
//..
// 64 char long string
//..
//  const char *testString64 =
//              "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
//                                                      "0123456789+-";
//..
// Populate the file with known data, with a zero byte at a known offset.
//..
//  int rc;
//  for (int i = 0; i < 20; ++i) {
//      rc = FilesystemUtil::write(fd, testString64, 64);
//      assert(64 == rc);
//  }
//
//  enum { OFFSET_OF_ZERO_BYTE = 7 * 64 };
//
//  rc = (int) FilesystemUtil::seek(fd,
//                            OFFSET_OF_ZERO_BYTE,
//                            FilesystemUtil::e_SEEK_FROM_BEGINNING);
//  assert(OFFSET_OF_ZERO_BYTE == rc);
//
//  rc = FilesystemUtil::write(fd, "", 1);        // write the zero byte
//  assert(1 == rc);
//
//  rc = FilesystemUtil::close(fd);
//  assert(0 == rc);
//
//  {
//      balst::StackTraceResolver_FileHelper helper(fileNameBuffer);
//
//      char buf[100];
//      memset(buf, 0, sizeof(buf));
//      rc = helper.readExact(buf,
//                            6,                    // # chars to read
//                            128);                 // offset
//      assert(0 == rc);
//      assert(!strcmp(buf, "abcdef"));
//..
// 'readExact' past EOF fails
//..
//      rc = helper.readExact(buf,
//                            6,                    // # chars to read
//                            64 * 40);             // offset
//      assert(0 != rc);
//..
// 'loadString' will read a zero terminated string at a given offset,
// using a buffer passed in, and allocating memory for a new copy of
// the string.
//..
//      memset(buf, 'a', sizeof(buf));
//      char *result = helper.loadString(OFFSET_OF_ZERO_BYTE - 12,
//                                       buf,
//                                       sizeof(buf),
//                                       &ta);
//
//      assert(12 == bsl::strlen(result));
//      assert(!bsl::strcmp("0123456789+-", result));
//..
// clean up
//..
//      ta.deallocate(result);
//  }
//  bdls::FilesystemUtil::remove(fileNameBuffer);
//..

#include <balscm_version.h>

#include <balst_objectfileformat.h>

#if defined(BALST_OBJECTFILEFORMAT_RESOLVER_ELF) || \
    defined(BALST_OBJECTFILEFORMAT_RESOLVER_XCOFF)
#include <bdls_filesystemutil.h>

#include <bslma_allocator.h>

#include <bsls_assert.h>
#include <bsls_review.h>
#include <bsls_types.h>

namespace BloombergLP {

namespace balst {
                    // ===================================
                    // class StackTraceResolver_FileHelper
                    // ===================================

class StackTraceResolver_FileHelper {
    // This class provides a low-level file utility functions for
    // 'StackTraceResolver<Elf>' and 'StackTraceResolver<Xcoff>'.
    // This class contains the file descriptor of current object file.  Note
    // that the file is opened readonly, and all reads specify the offset, so
    // the offset of the file descriptor is not considered part of the state of
    // this object.

    // PRIVATE TYPES
    typedef bdls::FilesystemUtil    FilesystemUtil;
    typedef FilesystemUtil::Offset  Offset;
    typedef bsls::Types::UintPtr    UintPtr;
    typedef bsls::Types::IntPtr     IntPtr;

    // DATA
    FilesystemUtil::FileDescriptor d_fd;  // file descriptor

  private:
    // NOT IMPLEMENTED
    StackTraceResolver_FileHelper(const StackTraceResolver_FileHelper&);
    StackTraceResolver_FileHelper& operator=(
                                         const StackTraceResolver_FileHelper&);
  public:
    // CREATORS
    explicit
    StackTraceResolver_FileHelper();
        // Create a file helper object in an invalid state.

    ~StackTraceResolver_FileHelper();
        // Close any file currently opened by this object and destroy it.

    // MANIPULATOR
    int initialize(const char *fileName);
        // Open the file referred to by the specified 'fileName' for read-only
        // access and set 'd_fd' to the file descriptor.  Return 0 on success
        // and a non-zero value otherwise.  If this object already had a file
        // descriptor open, close it before opening 'fileName'.  If the 'open'
        // call fails, 'd_fd' will be set to 'FilesystemUtil::k_INVALID_FD'.

    // ACCESSORS
    char *loadString(Offset            offset,
                     char             *scratchBuf,
                     UintPtr           scratchBufLength,
                     bslma::Allocator *basicAllocator) const;
        // Load into memory newly allocated from the specified 'basicAllocator'
        // a zero-terminated string from the specified absolute file 'offset',
        // using the specified 'scratchBuf' of specified length
        // 'scratchBufLength' as temporary storage before the new string is
        // allocated.  Return the pointer to the newly allocated string.  The
        // behavior is undefined unless 'scratchBuf != 0' and
        // 'scratchBufLength >= 1'.  Note that if the string is longer than
        // 'scratchBufLength - 1', it is truncated.

    UintPtr readBytes(void *buf, UintPtr numBytes, Offset offset) const;
        // Read into the specified 'buf' up to the specified 'numBytes' of data
        // starting at the specified 'offset' in the current ELF or XCOFF file.
        // Return the number of bytes read, which can be zero.  The behavior is
        // undefined unless 'buf != 0' and 'offset >= 0'.

    int readExact(void *buf, UintPtr numBytes, Offset offset) const;
        // Read into the specified 'buf' exactly the specified 'numBytes' of
        // data starting at the specified 'offset' in the current ELF or XCOFF
        // file.  Return 0 on success, or a negative value otherwise.  The
        // behavior is undefined unless 'buf != 0' and 'offset >= 0'.
};

// ============================================================================
//                        INLINE FUNCTION DEFINITIONS
// ============================================================================

                     // -----------------------------------
                     // StackTraceResolver_FileHelper
                     // -----------------------------------

// ACCESSORS
inline
int StackTraceResolver_FileHelper::readExact(void    *buf,
                                             UintPtr  numBytes,
                                             Offset   offset) const
{
    BSLS_ASSERT(buf);
    BSLS_ASSERT(offset >= 0);

    UintPtr res = readBytes(buf, numBytes, offset);
    return res != numBytes ? -1 : 0;
}

}  // close package namespace
}  // close enterprise namespace

#endif

#endif

// ----------------------------------------------------------------------------
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed 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.
// ----------------------------- END-OF-FILE ----------------------------------