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
19static const int32 kMaxSourceFileSize = 10 * 1024 * 1024;
20
21
22// #pragma mark - SourceFileOwner
23
24
25SourceFileOwner::~SourceFileOwner()
26{
27}
28
29
30// #pragma mark - SourceFile
31
32
33SourceFile::SourceFile(SourceFileOwner* owner)
34	:
35	fOwner(owner),
36	fFileContent(NULL),
37	fLineOffsets(NULL),
38	fLineCount(0)
39{
40}
41
42
43SourceFile::~SourceFile()
44{
45	free(fFileContent);
46	delete[] fLineOffsets;
47	fOwner->SourceFileDeleted(this);
48}
49
50
51status_t
52SourceFile::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
121int32
122SourceFile::CountLines() const
123{
124	return fLineCount;
125}
126
127
128const char*
129SourceFile::LineAt(int32 index) const
130{
131	return index >= 0 && index < fLineCount
132		? fFileContent + fLineOffsets[index] : NULL;
133}
134
135
136int32
137SourceFile::LineLengthAt(int32 index) const
138{
139	return index >= 0 && index < fLineCount
140		? fLineOffsets[index + 1] - fLineOffsets[index] - 1: 0;
141}
142
143void
144SourceFile::LastReferenceReleased()
145{
146	fOwner->SourceFileUnused(this);
147	delete this;
148}
149