1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "SourceFile.h" 7 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <sys/stat.h> 14 #include <unistd.h> 15 16 #include <new> 17 18 19 static const int32 kMaxSourceFileSize = 10 * 1024 * 1024; 20 21 22 // #pragma mark - SourceFileOwner 23 24 25 SourceFileOwner::~SourceFileOwner() 26 { 27 } 28 29 30 // #pragma mark - SourceFile 31 32 33 SourceFile::SourceFile(SourceFileOwner* owner) 34 : 35 fOwner(owner), 36 fFileContent(NULL), 37 fLineOffsets(NULL), 38 fLineCount(0) 39 { 40 } 41 42 43 SourceFile::~SourceFile() 44 { 45 free(fFileContent); 46 delete[] fLineOffsets; 47 fOwner->SourceFileDeleted(this); 48 } 49 50 51 status_t 52 SourceFile::Init(const char* path) 53 { 54 // open the file 55 int fd = open(path, O_RDONLY); 56 if (fd < 0) 57 return errno; 58 59 // stat the file to get its size 60 struct stat st; 61 if (fstat(fd, &st) < 0) { 62 close(fd); 63 return errno; 64 } 65 66 if (st.st_size > kMaxSourceFileSize) { 67 close(fd); 68 return B_FILE_TOO_LARGE; 69 } 70 size_t fileSize = st.st_size; 71 72 if (fileSize == 0) { 73 close(fd); 74 return B_BAD_VALUE; 75 } 76 77 // allocate the content buffer 78 fFileContent = (char*)malloc(fileSize + 1); 79 // one more byte for a terminating null 80 if (fFileContent == NULL) { 81 close(fd); 82 return B_NO_MEMORY; 83 } 84 85 // read the file 86 ssize_t bytesRead = read(fd, fFileContent, fileSize); 87 close(fd); 88 if (bytesRead < 0 || (size_t)bytesRead != fileSize) 89 return bytesRead < 0 ? errno : B_FILE_ERROR; 90 91 // null-terminate 92 fFileContent[fileSize] = '\0'; 93 94 // count lines 95 fLineCount = 1; 96 for (size_t i = 0; i < fileSize; i++) { 97 if (fFileContent[i] == '\n') 98 fLineCount++; 99 } 100 101 // allocate line offset array 102 fLineOffsets = new(std::nothrow) int32[fLineCount + 1]; 103 if (fLineOffsets == NULL) 104 return B_NO_MEMORY; 105 106 // get the line offsets and null-terminate the lines 107 int32 lineIndex = 0; 108 fLineOffsets[lineIndex++] = 0; 109 for (size_t i = 0; i < fileSize; i++) { 110 if (fFileContent[i] == '\n') { 111 fFileContent[i] = '\0'; 112 fLineOffsets[lineIndex++] = i + 1; 113 } 114 } 115 fLineOffsets[fLineCount] = fileSize + 1; 116 117 return B_OK; 118 } 119 120 121 int32 122 SourceFile::CountLines() const 123 { 124 return fLineCount; 125 } 126 127 128 const char* 129 SourceFile::LineAt(int32 index) const 130 { 131 return index >= 0 && index < fLineCount 132 ? fFileContent + fLineOffsets[index] : NULL; 133 } 134 135 136 int32 137 SourceFile::LineLengthAt(int32 index) const 138 { 139 return index >= 0 && index < fLineCount 140 ? fLineOffsets[index + 1] - fLineOffsets[index] - 1: 0; 141 } 142 143 void 144 SourceFile::LastReferenceReleased() 145 { 146 fOwner->SourceFileUnused(this); 147 delete this; 148 } 149