1//----------------------------------------------------------------------
2//  This software is part of the OpenBeOS distribution and is covered
3//  by the MIT License.
4//----------------------------------------------------------------------
5/*!
6	\file storage_support.cpp
7	Implementations of miscellaneous internal Storage Kit support functions.
8*/
9
10#include <new>
11#include <ctype.h>
12#include <string.h>
13
14#include <StorageDefs.h>
15#include <SupportDefs.h>
16
17#include <syscalls.h>
18
19#include "storage_support.h"
20
21using std::nothrow;
22
23namespace BPrivate {
24namespace Storage {
25
26/*!	\param path the path
27	\return \c true, if \a path is not \c NULL and absolute, \c false otherwise
28*/
29bool
30is_absolute_path(const char *path)
31{
32	return (path && path[0] == '/');
33}
34
35// parse_path
36/*!	\brief Parses the supplied path and returns the position of the leaf name
37		   part of the path and the length of its directory path part.
38
39	The value returned in \a fullPath is guaranteed to be > 0, i.e. the
40	function always returns a non-empty directory path part. The leaf name
41	part may be empty though (i.e. \code leafStart == leafEnd \endcode), which
42	will happen, if the supplied path consists only of one component.
43
44	\param fullPath The path to be parsed.
45	\param dirEnd Reference to a variable into which the end index of the
46		   directory part shall be written. The index is exclusive.
47	\param leafStart Reference to a variable into which the start index of
48		   the leaf name part shall be written. The index is inclusive.
49	\param leafEnd Reference to a variable into which the end index of
50		   the leaf name part shall be written. The index is exclusive.
51	\return \c B_OK, if everything went fine, B_BAD_VALUE, if the supplied
52		   path is invalid.
53*/
54status_t
55parse_path(const char *fullPath, int &dirEnd, int &leafStart, int &leafEnd)
56{
57	// check path and get length
58	if (!fullPath)
59		return B_BAD_VALUE;
60	int pathLen = strlen(fullPath);
61	if (pathLen == 0)
62		return B_BAD_VALUE;
63	// find then end of the leaf name (skip trailing '/')
64	int i = pathLen - 1;
65	while (i >= 0 && fullPath[i] == '/')
66		i--;
67	leafEnd = i + 1;
68	if (leafEnd == 0) {
69		// fullPath consists of slashes only
70		dirEnd = leafStart = leafEnd = 1;
71		return B_OK;
72	}
73	// find the start of the leaf name
74	while (i >= 0 && fullPath[i] != '/')
75		i--;
76	leafStart = i + 1;
77	if (leafStart == 0) {
78		// fullPath contains only one component
79		dirEnd = leafStart = leafEnd;
80		return B_OK;
81	}
82	// find the end of the dir path
83	while (i >= 0 && fullPath[i] == '/')
84		i--;
85	dirEnd = i + 1;
86	if (dirEnd == 0)	// => fullPath[0] == '/' (an absolute path)
87		dirEnd = 1;
88	return B_OK;
89}
90
91// parse_path
92/*!	\brief Parses the supplied path and returns the leaf name part of the path
93		   and its directory path part.
94
95	The value returned in \a fullPath is guaranteed to be > 0, i.e. the
96	function always returns a non-empty directory path part. The leaf name
97	part may be empty though (i.e. \code leafStart == leafEnd \endcode), which
98	will happen, if the supplied path consists only of one component.
99
100	\param fullPath The path to be parsed.
101	\param dirPath Pointer to a character array of size \c B_PATH_NAME_LENGTH
102		   or greater, into which the directory part shall be written.
103		   May be \c NULL.
104	\param leaf Pointer to a character array of size \c B_FILE_NAME_LENGTH
105		   or greater, into which the leaf name part shall be written.
106		   May be \c NULL.
107	\return \c B_OK, if everything went fine, B_BAD_VALUE, if the supplied
108		   path is invalid.
109*/
110status_t
111parse_path(const char *fullPath, char *dirPath, char *leaf)
112{
113	// parse the path and check the lengths
114	int leafStart, leafEnd, dirEnd;
115	status_t error = parse_path(fullPath, dirEnd, leafStart, leafEnd);
116	if (error != B_OK)
117		return error;
118	if (dirEnd >= B_PATH_NAME_LENGTH
119		|| leafEnd - leafStart >= B_FILE_NAME_LENGTH) {
120		return B_NAME_TOO_LONG;
121	}
122	// copy the result strings
123	if (dirPath)
124		strlcpy(dirPath, fullPath, dirEnd + 1);
125	if (leaf)
126		strlcpy(leaf, fullPath + leafStart, leafEnd - leafStart + 1);
127	return B_OK;
128}
129
130// internal_parse_path
131static
132void
133internal_parse_path(const char *fullPath, int &leafStart, int &leafEnd,
134	int &pathEnd)
135{
136	if (fullPath == NULL)
137		return;
138
139	enum PathParserState { PPS_START, PPS_LEAF } state = PPS_START;
140
141	int len = strlen(fullPath);
142
143	leafStart = -1;
144	leafEnd = -1;
145	pathEnd = -2;
146
147	bool loop = true;
148	for (int pos = len-1; ; pos--) {
149		if (pos < 0)
150			break;
151
152		switch (state) {
153			case PPS_START:
154				// Skip all trailing '/' chars, then move on to
155				// reading the leaf name
156				if (fullPath[pos] != '/') {
157					leafEnd = pos;
158					state = PPS_LEAF;
159				}
160				break;
161
162			case PPS_LEAF:
163				// Read leaf name chars until we hit a '/' char
164				if (fullPath[pos] == '/') {
165					leafStart = pos+1;
166					pathEnd = pos-1;
167					loop = false;
168				}
169				break;
170		}
171
172		if (!loop)
173			break;
174	}
175}
176
177/*!	The caller is responsible for deleting the returned directory path name
178	and the leaf name.
179	\param fullPath the path name to be split
180	\param path a variable the directory path name pointer shall
181		   be written into, may be NULL
182	\param leaf a variable the leaf name pointer shall be
183		   written into, may be NULL
184*/
185status_t
186split_path(const char *fullPath, char *&path, char *&leaf)
187{
188	return split_path(fullPath, &path, &leaf);
189}
190
191/*!	The caller is responsible for deleting the returned directory path name
192	and the leaf name.
193	\param fullPath the path name to be split
194	\param path a pointer to a variable the directory path name pointer shall
195		   be written into, may be NULL
196	\param leaf a pointer to a variable the leaf name pointer shall be
197		   written into, may be NULL
198*/
199status_t
200split_path(const char *fullPath, char **path, char **leaf)
201{
202	if (path)
203		*path = NULL;
204	if (leaf)
205		*leaf = NULL;
206
207	if (fullPath == NULL)
208		return B_BAD_VALUE;
209
210	int leafStart, leafEnd, pathEnd, len;
211	internal_parse_path(fullPath, leafStart, leafEnd, pathEnd);
212
213	try {
214		// Tidy up/handle special cases
215		if (leafEnd == -1) {
216
217			// Handle special cases
218			if (fullPath[0] == '/') {
219				// Handle "/"
220				if (path) {
221					*path = new char[2];
222					(*path)[0] = '/';
223					(*path)[1] = 0;
224				}
225				if (leaf) {
226					*leaf = new char[2];
227					(*leaf)[0] = '.';
228					(*leaf)[1] = 0;
229				}
230				return B_OK;
231			} else if (fullPath[0] == 0) {
232				// Handle "", which we'll treat as "./"
233				if (path) {
234					*path = new char[1];
235					(*path)[0] = 0;
236				}
237				if (leaf) {
238					*leaf = new char[2];
239					(*leaf)[0] = '.';
240					(*leaf)[1] = 0;
241				}
242				return B_OK;
243			}
244
245		} else if (leafStart == -1) {
246			// fullPath is just an entry name, no parent directories specified
247			leafStart = 0;
248		} else if (pathEnd == -1) {
249			// The path is '/' (since pathEnd would be -2 if we had
250			// run out of characters before hitting a '/')
251			pathEnd = 0;
252		}
253
254		// Alloc new strings and copy the path and leaf over
255		if (path) {
256			if (pathEnd == -2) {
257				// empty path
258				*path = new char[2];
259				(*path)[0] = '.';
260				(*path)[1] = 0;
261			} else {
262				// non-empty path
263				len = pathEnd + 1;
264				*path = new char[len+1];
265				memcpy(*path, fullPath, len);
266				(*path)[len] = 0;
267			}
268		}
269		if (leaf) {
270			len = leafEnd - leafStart + 1;
271			*leaf = new char[len+1];
272			memcpy(*leaf, fullPath + leafStart, len);
273			(*leaf)[len] = 0;
274		}
275	} catch (std::bad_alloc& exception) {
276		if (path)
277			delete[] *path;
278		if (leaf)
279			delete[] *leaf;
280		return B_NO_MEMORY;
281	}
282	return B_OK;
283}
284
285/*! The length of the first component is returned as well as the index at
286	which the next one starts. These values are only valid, if the function
287	returns \c B_OK.
288	\param path the path to be parsed
289	\param length the variable the length of the first component is written
290		   into
291	\param nextComponent the variable the index of the next component is
292		   written into. \c 0 is returned, if there is no next component.
293	\return \c B_OK, if \a path is not \c NULL, \c B_BAD_VALUE otherwise
294*/
295status_t
296parse_first_path_component(const char *path, int32& length,
297						   int32& nextComponent)
298{
299	status_t error = (path ? B_OK : B_BAD_VALUE);
300	if (error == B_OK) {
301		int32 i = 0;
302		// find first '/' or end of name
303		for (; path[i] != '/' && path[i] != '\0'; i++);
304		// handle special case "/..." (absolute path)
305		if (i == 0 && path[i] != '\0')
306			i = 1;
307		length = i;
308		// find last '/' or end of name
309		for (; path[i] == '/' && path[i] != '\0'; i++);
310		if (path[i] == '\0')	// this covers "" as well
311			nextComponent = 0;
312		else
313			nextComponent = i;
314	}
315	return error;
316}
317
318/*! A string containing the first component is returned and the index, at
319	which the next one starts. These values are only valid, if the function
320	returns \c B_OK.
321	\param path the path to be parsed
322	\param component the variable the pointer to the newly allocated string
323		   containing the first path component is written into. The caller
324		   is responsible for delete[]'ing the string.
325	\param nextComponent the variable the index of the next component is
326		   written into. \c 0 is returned, if there is no next component.
327	\return \c B_OK, if \a path is not \c NULL, \c B_BAD_VALUE otherwise
328*/
329status_t
330parse_first_path_component(const char *path, char *&component,
331						   int32& nextComponent)
332{
333	int32 length;
334	status_t error = parse_first_path_component(path, length, nextComponent);
335	if (error == B_OK) {
336		component = new(nothrow) char[length + 1];
337		if (component) {
338			strncpy(component, path, length);
339			component[length] = '\0';
340		} else
341			error = B_NO_MEMORY;
342	}
343	return error;
344}
345
346/*!	An entry name is considered valid, if its length doesn't exceed
347	\c B_FILE_NAME_LENGTH (including the terminating null) and it doesn't
348	contain any \c "/".
349	\param entry the entry name
350	\return
351	- \c B_OK, if \a entry is valid,
352	- \c B_BAD_VALUE, if \a entry is \c NULL or contains a "/",
353	- \c B_NAME_TOO_LONG, if \a entry is too long
354	\note \c "" is considered a valid entry name.
355*/
356status_t
357check_entry_name(const char *entry)
358{
359	status_t error = (entry ? B_OK : B_BAD_VALUE);
360	if (error == B_OK) {
361		if (strlen(entry) >= B_FILE_NAME_LENGTH)
362			error = B_NAME_TOO_LONG;
363	}
364	if (error == B_OK) {
365		for (int32 i = 0; error == B_OK && entry[i] != '\0'; i++) {
366			if (entry[i] == '/')
367				error = B_BAD_VALUE;
368		}
369	}
370	return error;
371}
372
373/*!	An path name is considered valid, if its length doesn't exceed
374	\c B_PATH_NAME_LENGTH (including the terminating null) and each of
375	its components is a valid entry name.
376	\param entry the entry name
377	\return
378	- \c B_OK, if \a path is valid,
379	- \c B_BAD_VALUE, if \a path is \c NULL,
380	- \c B_NAME_TOO_LONG, if \a path, or any of its components is too long
381	\note \c "" is considered a valid path name.
382*/
383status_t
384check_path_name(const char *path)
385{
386	// check the path is not NULL
387	status_t error = (path ? B_OK : B_BAD_VALUE);
388	if (error == B_BAD_VALUE)
389		return error;
390	// check the path components
391	const char *remainder = path;
392	int32 length, nextComponent;
393	do {
394		error = parse_first_path_component(remainder, length, nextComponent);
395		if (error == B_OK) {
396			if (length >= B_FILE_NAME_LENGTH)
397				error = B_NAME_TOO_LONG;
398			remainder += nextComponent;
399		}
400	} while (error == B_OK && nextComponent != 0);
401	// check the length of the path
402	if (error == B_OK && strlen(path) >= B_PATH_NAME_LENGTH)
403		error = B_NAME_TOO_LONG;
404	return error;
405}
406
407std::string
408to_lower(const char *str)
409{
410	std::string result;
411	to_lower(str, result);
412	return result;
413}
414
415void
416to_lower(const char *str, std::string &result)
417{
418	if (str) {
419		result = "";
420		for (int i = 0; i < (int)strlen(str); i++)
421			result += tolower(str[i]);
422	} else
423		result = "(null)";
424}
425
426void
427to_lower(const char *str, char *result)
428{
429	if (str && result) {
430		int i;
431		for (i = 0; i < (int)strlen(str); i++)
432			result[i] = tolower(str[i]);
433		result[i] = 0;
434	}
435}
436
437void
438to_lower(char *str)
439{
440	to_lower(str, str);
441}
442
443void escape_path(const char *str, char *result)
444{
445	if (str && result) {
446		int32 len = strlen(str);
447
448		for (int32 i = 0; i < len; i++) {
449			char ch = str[i];
450			char escapeChar = 0;
451
452			switch (ch) {
453				case ' ':
454				case '\'':
455				case '"':
456				case '?':
457				case '\\':
458				case '(':
459				case ')':
460				case '[':
461				case ']':
462				case '*':
463				case '^':
464					escapeChar = ch;
465					break;
466			}
467
468			if (escapeChar) {
469				*(result++) = '\\';
470				*(result++) = escapeChar;
471			} else {
472				*(result++) = ch;
473			}
474		}
475
476		*result = 0;
477	}
478}
479
480void escape_path(char *str)
481{
482	if (str) {
483		char *copy = new(nothrow) char[strlen(str)+1];
484		if (copy) {
485			strcpy(copy, str);
486			escape_path(copy, str);
487		}
488		delete [] copy;
489	}
490}
491
492// device_is_root_device
493bool
494device_is_root_device(dev_t device)
495{
496	return device == 1;
497}
498
499// Close
500void
501FDCloser::Close()
502{
503	if (fFD >= 0)
504		_kern_close(fFD);
505	fFD = -1;
506}
507
508};	// namespace Storage
509};	// namespace BPrivate
510
511