2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: PosixStorage.C /main/1 1996/07/29 17:01:49 cde-hp $ */
24 // Copyright (c) 1994, 1995 James Clark
25 // See the file COPYING for copying permission.
28 #pragma implementation
32 #include "PosixStorage.h"
33 #include "RewindStorageObject.h"
34 #include "StorageManager.h"
35 #include "DescriptorManager.h"
36 #include "MessageArg.h"
37 #include "ErrnoMessageArg.h"
38 #include "SearchResultMessageArg.h"
42 #include "CharsetInfo.h"
43 #include "CodingSystem.h"
45 #include "PosixStorageMessages.h"
47 #include <sys/types.h>
51 #ifdef SP_INCLUDE_IO_H
52 #include <io.h> // for open, fstat, lseek, read prototypes
55 #ifdef SP_INCLUDE_UNISTD_H
59 #ifdef SP_INCLUDE_OSFCN_H
70 #define S_IFREG _S_IFREG
73 #define S_IFMT _S_IFMT
75 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
76 #endif /* not S_ISREG */
83 namespace SP_NAMESPACE {
87 typedef wchar_t FChar;
92 class PosixBaseStorageObject : public RewindStorageObject {
94 PosixBaseStorageObject(int fd, Boolean mayRewind);
95 size_t getBlockSize() const;
97 enum { defaultBlockSize = 8192 };
101 Boolean seekToStart(Messenger &);
102 virtual Boolean seek(off_t, Messenger &) = 0;
103 static int xclose(int fd);
105 Boolean canSeek(int fd);
109 PosixBaseStorageObject::PosixBaseStorageObject(int fd, Boolean mayRewind)
111 RewindStorageObject(mayRewind, mayRewind && canSeek(fd))
115 Boolean PosixBaseStorageObject::canSeek(int fd)
118 if (fstat(fd, &sb) < 0 || !S_ISREG(sb.st_mode)
119 || (startOffset_ = lseek(fd, off_t(0), SEEK_CUR)) < 0)
125 Boolean PosixBaseStorageObject::seekToStart(Messenger &mgr)
128 return seek(startOffset_, mgr);
131 int PosixBaseStorageObject::xclose(int fd)
136 } while (ret < 0 && errno == EINTR);
140 class PosixStorageObject : public PosixBaseStorageObject, private DescriptorUser {
142 PosixStorageObject(int fd,
144 const String<FChar> &,
146 DescriptorManager *);
147 ~PosixStorageObject();
148 Boolean read(char *buf, size_t bufSize, Messenger &mgr, size_t &nread);
150 Boolean seek(off_t, Messenger &);
151 void willNotRewind();
153 void resume(Messenger &);
155 PackedBoolean suspended_;
157 const MessageType2 *suspendFailedMessage_;
160 String<FChar> cfilename_;
162 void systemError(Messenger &, const MessageType2 &, int);
165 inline int openFile(const FChar *s) {
166 #ifdef SP_WIDE_SYSTEM
167 return _wopen(s, O_RDONLY|O_BINARY);
169 return ::open(s, O_RDONLY|O_BINARY);
173 PosixStorageManager::PosixStorageManager(const char *type,
174 const UnivCharsetDesc &filenameCharset,
175 #ifndef SP_WIDE_SYSTEM
176 const OutputCodingSystem *filenameCodingSystem,
179 : IdStorageManager(filenameCharset),
181 #ifndef SP_WIDE_SYSTEM
182 filenameCodingSystem_(filenameCodingSystem),
184 descriptorManager_(maxFDs)
186 Char newline = idCharset()->execToDesc('\n');
187 reString_.assign(&newline, 1);
190 const char *PosixStorageManager::type() const
195 void PosixStorageManager::addSearchDir(const StringC &str)
197 searchDirs_.push_back(str);
200 #ifdef SP_POSIX_FILENAMES
202 #define FILENAME_TYPE_DEFINED
204 // FIXME should use idCharset.
206 Boolean PosixStorageManager::isAbsolute(const StringC &file) const
208 return file.size() > 0 && file[0] == '/';
211 StringC PosixStorageManager::extractDir(const StringC &str) const
213 for (size_t i = str.size(); i > 0; i--)
214 if (str[i - 1] == '/')
215 return StringC(str.data(), i); // include slash for root case
219 StringC PosixStorageManager::combineDir(const StringC &dir,
220 const StringC &base) const
223 if (dir.size() > 0 && dir[dir.size() - 1] != '/')
229 Boolean PosixStorageManager::transformNeutral(StringC &str, Boolean fold,
233 for (size_t i = 0; i < str.size(); i++) {
235 if (c <= (unsigned char)-1)
236 str[i] = tolower(str[i]);
241 #endif /* SP_POSIX_FILENAMES */
243 #ifdef SP_MSDOS_FILENAMES
245 #define FILENAME_TYPE_DEFINED
247 Boolean PosixStorageManager::isAbsolute(const StringC &s) const
251 return s[0] == '/' || s[0] == '\\' || (s.size() > 1 && s[1] == ':');
254 StringC PosixStorageManager::extractDir(const StringC &str) const
256 for (size_t i = str.size(); i > 0; i--)
257 if (str[i - 1] == '/' || str[i - 1] == '\\'
258 || (i == 2 && str[i - 1] == ':'))
259 return StringC(str.data(), i); // include separator
263 StringC PosixStorageManager::combineDir(const StringC &dir,
264 const StringC &base) const
267 if (dir.size() > 0) {
268 Char lastChar = dir[dir.size() - 1];
269 if (lastChar != '/' && lastChar != '\\'
270 && !(dir.size() == 2 && lastChar == ':'))
277 Boolean PosixStorageManager::transformNeutral(StringC &str, Boolean,
280 for (size_t i = 0; i < str.size(); i++)
286 #endif /* SP_MSDOS_FILENAMES */
288 #ifndef FILENAME_TYPE_DEFINED
290 Boolean PosixStorageManager::isAbsolute(const StringC &) const
295 StringC PosixStorageManager::extractDir(const StringC &) const
300 StringC PosixStorageManager::combineDir(const StringC &,
301 const StringC &base) const
306 Boolean PosixStorageManager::transformNeutral(StringC &, Boolean,
312 #endif /* not FILENAME_TYPE_DEFINED */
314 Boolean PosixStorageManager::resolveRelative(const StringC &baseId,
316 Boolean search) const
318 if (isAbsolute(specId))
320 if (!search || searchDirs_.size() == 0) {
321 specId = combineDir(extractDir(baseId), specId);
328 PosixStorageManager::makeStorageObject(const StringC &spec,
335 if (spec.size() == 0) {
336 mgr.message(PosixStorageMessages::invalidFilename,
337 StringMessageArg(spec));
340 descriptorManager_.acquireD();
341 Boolean absolute = isAbsolute(spec);
342 SearchResultMessageArg sr;
343 for (size_t i = 0; i < searchDirs_.size() + 1; i++) {
348 filename = combineDir(extractDir(base), spec);
350 filename = combineDir(searchDirs_[i - 1], spec);
351 #ifdef SP_WIDE_SYSTEM
352 String<FChar> cfilename(filename);
353 cfilename += FChar(0);
355 String<FChar> cfilename = filenameCodingSystem_->convertOut(filename);
359 fd = openFile(cfilename.data());
360 } while (fd < 0 && errno == EINTR);
363 return new PosixStorageObject(fd,
367 &descriptorManager_);
369 int savedErrno = errno;
370 if (absolute || !search || searchDirs_.size() == 0) {
371 ParentLocationMessenger(mgr).message(PosixStorageMessages::openSystemCall,
372 StringMessageArg(filename),
373 ErrnoMessageArg(savedErrno));
374 descriptorManager_.releaseD();
377 sr.add(filename, savedErrno);
379 descriptorManager_.releaseD();
380 ParentLocationMessenger(mgr).message(PosixStorageMessages::cannotFind,
381 StringMessageArg(spec), sr);
385 PosixStorageObject::PosixStorageObject(int fd,
386 const StringC &filename,
387 const String<FChar> &cfilename,
389 DescriptorManager *manager)
390 : DescriptorUser(manager),
391 PosixBaseStorageObject(fd, mayRewind),
394 cfilename_(cfilename)
398 PosixStorageObject::~PosixStorageObject()
406 Boolean PosixStorageObject::seek(off_t off, Messenger &mgr)
408 if (lseek(fd_, off, SEEK_SET) < 0) {
410 systemError(mgr, PosixStorageMessages::lseekSystemCall, errno);
417 Boolean PosixStorageObject::read(char *buf, size_t bufSize, Messenger &mgr,
420 if (readSaved(buf, bufSize, nread))
428 n = ::read(fd_, buf, bufSize);
429 } while (n < 0 && errno == EINTR);
432 saveBytes(buf, nread);
436 int saveErrno = errno;
439 systemError(mgr, PosixStorageMessages::readSystemCall, saveErrno);
444 // n == 0, so end of file
448 systemError(mgr, PosixStorageMessages::closeSystemCall, errno);
456 void PosixStorageObject::willNotRewind()
458 RewindStorageObject::willNotRewind();
459 if (eof_ && fd_ >= 0) {
466 Boolean PosixStorageObject::suspend()
468 if (fd_ < 0 || suspended_)
471 if (fstat(fd_, &sb) < 0 || !S_ISREG(sb.st_mode))
473 suspendFailedMessage_ = 0;
474 suspendPos_ = lseek(fd_, 0, SEEK_CUR);
475 if (suspendPos_ == (off_t)-1) {
476 suspendFailedMessage_ = &PosixStorageMessages::lseekSystemCall;
477 suspendErrno_ = errno;
479 if (xclose(fd_) < 0 && !suspendFailedMessage_) {
480 suspendFailedMessage_ = &PosixStorageMessages::closeSystemCall;
481 suspendErrno_ = errno;
489 void PosixStorageObject::resume(Messenger &mgr)
492 if (suspendFailedMessage_) {
493 systemError(mgr, *suspendFailedMessage_, suspendErrno_);
498 // suspended_ must be 1 until after acquireD() is called,
499 // so that we don't try to suspend this one before it is resumed.
502 fd_ = openFile(cfilename_.data());
503 } while (fd_ < 0 && errno == EINTR);
506 systemError(mgr, PosixStorageMessages::openSystemCall, errno);
509 if (lseek(fd_, suspendPos_, SEEK_SET) < 0) {
510 systemError(mgr, PosixStorageMessages::lseekSystemCall, errno);
517 #ifdef SP_STAT_BLKSIZE
519 size_t PosixBaseStorageObject::getBlockSize() const
523 if (fstat(fd_, &sb) < 0)
524 return defaultBlockSize;
525 if (!S_ISREG(sb.st_mode))
526 return defaultBlockSize;
527 if ((unsigned long)sb.st_blksize > size_t(-1))
534 #else /* not SP_STAT_BLKSIZE */
536 size_t PosixBaseStorageObject::getBlockSize() const
538 return defaultBlockSize;
541 #endif /* not SP_STAT_BLKSIZE */
543 void PosixStorageObject::systemError(Messenger &mgr,
544 const MessageType2 &msg,
547 ParentLocationMessenger(mgr).message(msg,
548 StringMessageArg(filename_),
549 ErrnoMessageArg(err));
552 class PosixFdStorageObject : public PosixBaseStorageObject {
554 PosixFdStorageObject(int, Boolean mayRewind);
555 Boolean read(char *buf, size_t bufSize, Messenger &mgr, size_t &nread);
556 Boolean seek(off_t, Messenger &);
567 PosixFdStorageManager::PosixFdStorageManager(const char *type,
568 const UnivCharsetDesc &idCharset)
569 : IdStorageManager(idCharset), type_(type)
573 Boolean PosixFdStorageManager::inheritable() const
578 StorageObject *PosixFdStorageManager::makeStorageObject(const StringC &id,
587 for (i = 0; i < id.size(); i++) {
589 if (!idCharset()->descToUniv(id[i], ch))
591 if (ch < UnivCharsetDesc::zero || ch > UnivCharsetDesc::zero + 9)
593 int digit = ch - UnivCharsetDesc::zero;
594 // Allow the division to be done at compile-time.
598 if (n > INT_MAX - digit)
602 if (i < id.size() || i == 0) {
603 mgr.message(PosixStorageMessages::invalidNumber,
604 StringMessageArg(id));
608 // Could check access mode with fcntl(n, F_GETFL).
609 return new PosixFdStorageObject(n, mayRewind);
612 PosixFdStorageObject::PosixFdStorageObject(int fd, Boolean mayRewind)
613 : PosixBaseStorageObject(fd, mayRewind), origFd_(fd)
617 const char *PosixFdStorageManager::type() const
622 Boolean PosixFdStorageObject::read(char *buf, size_t bufSize, Messenger &mgr,
625 if (readSaved(buf, bufSize, nread))
631 n = ::read(fd_, buf, bufSize);
632 } while (n < 0 && errno == EINTR);
635 saveBytes(buf, nread);
639 ParentLocationMessenger(mgr).message(PosixStorageMessages::fdRead,
640 NumberMessageArg(fd_),
641 ErrnoMessageArg(errno));
649 Boolean PosixFdStorageObject::seek(off_t off, Messenger &mgr)
651 if (lseek(fd_, off, SEEK_SET) < 0) {
652 ParentLocationMessenger(mgr).message(PosixStorageMessages::fdLseek,
653 NumberMessageArg(fd_),
654 ErrnoMessageArg(errno));