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 libraries 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)),
116 Boolean PosixBaseStorageObject::canSeek(int fd)
119 if (fstat(fd, &sb) < 0 || !S_ISREG(sb.st_mode)
120 || (startOffset_ = lseek(fd, off_t(0), SEEK_CUR)) < 0)
126 Boolean PosixBaseStorageObject::seekToStart(Messenger &mgr)
129 return seek(startOffset_, mgr);
132 int PosixBaseStorageObject::xclose(int fd)
137 } while (ret < 0 && errno == EINTR);
141 class PosixStorageObject : public PosixBaseStorageObject, private DescriptorUser {
143 PosixStorageObject(int fd,
145 const String<FChar> &,
147 DescriptorManager *);
148 ~PosixStorageObject();
149 Boolean read(char *buf, size_t bufSize, Messenger &mgr, size_t &nread);
151 Boolean seek(off_t, Messenger &);
152 void willNotRewind();
154 void resume(Messenger &);
156 PackedBoolean suspended_;
158 const MessageType2 *suspendFailedMessage_;
161 String<FChar> cfilename_;
163 void systemError(Messenger &, const MessageType2 &, int);
166 inline int openFile(const FChar *s) {
167 #ifdef SP_WIDE_SYSTEM
168 return _wopen(s, O_RDONLY|O_BINARY);
170 return ::open(s, O_RDONLY|O_BINARY);
174 PosixStorageManager::PosixStorageManager(const char *type,
175 const UnivCharsetDesc &filenameCharset,
176 #ifndef SP_WIDE_SYSTEM
177 const OutputCodingSystem *filenameCodingSystem,
180 : IdStorageManager(filenameCharset),
182 #ifndef SP_WIDE_SYSTEM
183 filenameCodingSystem_(filenameCodingSystem),
185 descriptorManager_(maxFDs)
187 Char newline = idCharset()->execToDesc('\n');
188 reString_.assign(&newline, 1);
191 const char *PosixStorageManager::type() const
196 void PosixStorageManager::addSearchDir(const StringC &str)
198 searchDirs_.push_back(str);
201 #ifdef SP_POSIX_FILENAMES
203 #define FILENAME_TYPE_DEFINED
205 // FIXME should use idCharset.
207 Boolean PosixStorageManager::isAbsolute(const StringC &file) const
209 return file.size() > 0 && file[0] == '/';
212 StringC PosixStorageManager::extractDir(const StringC &str) const
214 for (size_t i = str.size(); i > 0; i--)
215 if (str[i - 1] == '/')
216 return StringC(str.data(), i); // include slash for root case
220 StringC PosixStorageManager::combineDir(const StringC &dir,
221 const StringC &base) const
224 if (dir.size() > 0 && dir[dir.size() - 1] != '/')
230 Boolean PosixStorageManager::transformNeutral(StringC &str, Boolean fold,
234 for (size_t i = 0; i < str.size(); i++) {
236 if (c <= (unsigned char)-1)
237 str[i] = tolower(str[i]);
242 #endif /* SP_POSIX_FILENAMES */
244 #ifdef SP_MSDOS_FILENAMES
246 #define FILENAME_TYPE_DEFINED
248 Boolean PosixStorageManager::isAbsolute(const StringC &s) const
252 return s[0] == '/' || s[0] == '\\' || (s.size() > 1 && s[1] == ':');
255 StringC PosixStorageManager::extractDir(const StringC &str) const
257 for (size_t i = str.size(); i > 0; i--)
258 if (str[i - 1] == '/' || str[i - 1] == '\\'
259 || (i == 2 && str[i - 1] == ':'))
260 return StringC(str.data(), i); // include separator
264 StringC PosixStorageManager::combineDir(const StringC &dir,
265 const StringC &base) const
268 if (dir.size() > 0) {
269 Char lastChar = dir[dir.size() - 1];
270 if (lastChar != '/' && lastChar != '\\'
271 && !(dir.size() == 2 && lastChar == ':'))
278 Boolean PosixStorageManager::transformNeutral(StringC &str, Boolean,
281 for (size_t i = 0; i < str.size(); i++)
287 #endif /* SP_MSDOS_FILENAMES */
289 #ifndef FILENAME_TYPE_DEFINED
291 Boolean PosixStorageManager::isAbsolute(const StringC &) const
296 StringC PosixStorageManager::extractDir(const StringC &) const
301 StringC PosixStorageManager::combineDir(const StringC &,
302 const StringC &base) const
307 Boolean PosixStorageManager::transformNeutral(StringC &, Boolean,
313 #endif /* not FILENAME_TYPE_DEFINED */
315 Boolean PosixStorageManager::resolveRelative(const StringC &baseId,
317 Boolean search) const
319 if (isAbsolute(specId))
321 if (!search || searchDirs_.size() == 0) {
322 specId = combineDir(extractDir(baseId), specId);
329 PosixStorageManager::makeStorageObject(const StringC &spec,
336 if (spec.size() == 0) {
337 mgr.message(PosixStorageMessages::invalidFilename,
338 StringMessageArg(spec));
341 descriptorManager_.acquireD();
342 Boolean absolute = isAbsolute(spec);
343 SearchResultMessageArg sr;
344 for (size_t i = 0; i < searchDirs_.size() + 1; i++) {
349 filename = combineDir(extractDir(base), spec);
351 filename = combineDir(searchDirs_[i - 1], spec);
352 #ifdef SP_WIDE_SYSTEM
353 String<FChar> cfilename(filename);
354 cfilename += FChar(0);
356 String<FChar> cfilename = filenameCodingSystem_->convertOut(filename);
360 fd = openFile(cfilename.data());
361 } while (fd < 0 && errno == EINTR);
364 return new PosixStorageObject(fd,
368 &descriptorManager_);
370 int savedErrno = errno;
371 if (absolute || !search || searchDirs_.size() == 0) {
372 ParentLocationMessenger(mgr).message(PosixStorageMessages::openSystemCall,
373 StringMessageArg(filename),
374 ErrnoMessageArg(savedErrno));
375 descriptorManager_.releaseD();
378 sr.add(filename, savedErrno);
380 descriptorManager_.releaseD();
381 ParentLocationMessenger(mgr).message(PosixStorageMessages::cannotFind,
382 StringMessageArg(spec), sr);
386 PosixStorageObject::PosixStorageObject(int fd,
387 const StringC &filename,
388 const String<FChar> &cfilename,
390 DescriptorManager *manager)
391 : DescriptorUser(manager),
392 PosixBaseStorageObject(fd, mayRewind),
395 cfilename_(cfilename),
397 suspendFailedMessage_(NULL),
402 PosixStorageObject::~PosixStorageObject()
410 Boolean PosixStorageObject::seek(off_t off, Messenger &mgr)
412 if (lseek(fd_, off, SEEK_SET) < 0) {
414 systemError(mgr, PosixStorageMessages::lseekSystemCall, errno);
421 Boolean PosixStorageObject::read(char *buf, size_t bufSize, Messenger &mgr,
424 if (readSaved(buf, bufSize, nread))
432 n = ::read(fd_, buf, bufSize);
433 } while (n < 0 && errno == EINTR);
436 saveBytes(buf, nread);
440 int saveErrno = errno;
443 systemError(mgr, PosixStorageMessages::readSystemCall, saveErrno);
448 // n == 0, so end of file
452 systemError(mgr, PosixStorageMessages::closeSystemCall, errno);
460 void PosixStorageObject::willNotRewind()
462 RewindStorageObject::willNotRewind();
463 if (eof_ && fd_ >= 0) {
470 Boolean PosixStorageObject::suspend()
472 if (fd_ < 0 || suspended_)
475 if (fstat(fd_, &sb) < 0 || !S_ISREG(sb.st_mode))
477 suspendFailedMessage_ = 0;
478 suspendPos_ = lseek(fd_, 0, SEEK_CUR);
479 if (suspendPos_ == (off_t)-1) {
480 suspendFailedMessage_ = &PosixStorageMessages::lseekSystemCall;
481 suspendErrno_ = errno;
483 if (xclose(fd_) < 0 && !suspendFailedMessage_) {
484 suspendFailedMessage_ = &PosixStorageMessages::closeSystemCall;
485 suspendErrno_ = errno;
493 void PosixStorageObject::resume(Messenger &mgr)
496 if (suspendFailedMessage_) {
497 systemError(mgr, *suspendFailedMessage_, suspendErrno_);
502 // suspended_ must be 1 until after acquireD() is called,
503 // so that we don't try to suspend this one before it is resumed.
506 fd_ = openFile(cfilename_.data());
507 } while (fd_ < 0 && errno == EINTR);
510 systemError(mgr, PosixStorageMessages::openSystemCall, errno);
513 if (lseek(fd_, suspendPos_, SEEK_SET) < 0) {
514 systemError(mgr, PosixStorageMessages::lseekSystemCall, errno);
521 #ifdef SP_STAT_BLKSIZE
523 size_t PosixBaseStorageObject::getBlockSize() const
527 if (fstat(fd_, &sb) < 0)
528 return defaultBlockSize;
529 if (!S_ISREG(sb.st_mode))
530 return defaultBlockSize;
531 if ((unsigned long)sb.st_blksize > size_t(-1))
538 #else /* not SP_STAT_BLKSIZE */
540 size_t PosixBaseStorageObject::getBlockSize() const
542 return defaultBlockSize;
545 #endif /* not SP_STAT_BLKSIZE */
547 void PosixStorageObject::systemError(Messenger &mgr,
548 const MessageType2 &msg,
551 ParentLocationMessenger(mgr).message(msg,
552 StringMessageArg(filename_),
553 ErrnoMessageArg(err));
556 class PosixFdStorageObject : public PosixBaseStorageObject {
558 PosixFdStorageObject(int, Boolean mayRewind);
559 Boolean read(char *buf, size_t bufSize, Messenger &mgr, size_t &nread);
560 Boolean seek(off_t, Messenger &);
571 PosixFdStorageManager::PosixFdStorageManager(const char *type,
572 const UnivCharsetDesc &idCharset)
573 : IdStorageManager(idCharset), type_(type)
577 Boolean PosixFdStorageManager::inheritable() const
582 StorageObject *PosixFdStorageManager::makeStorageObject(const StringC &id,
591 for (i = 0; i < id.size(); i++) {
593 if (!idCharset()->descToUniv(id[i], ch))
595 if (ch < UnivCharsetDesc::zero || ch > UnivCharsetDesc::zero + 9)
597 int digit = ch - UnivCharsetDesc::zero;
598 // Allow the division to be done at compile-time.
602 if (n > INT_MAX - digit)
606 if (i < id.size() || i == 0) {
607 mgr.message(PosixStorageMessages::invalidNumber,
608 StringMessageArg(id));
612 // Could check access mode with fcntl(n, F_GETFL).
613 return new PosixFdStorageObject(n, mayRewind);
616 PosixFdStorageObject::PosixFdStorageObject(int fd, Boolean mayRewind)
617 : PosixBaseStorageObject(fd, mayRewind), origFd_(fd)
621 const char *PosixFdStorageManager::type() const
626 Boolean PosixFdStorageObject::read(char *buf, size_t bufSize, Messenger &mgr,
629 if (readSaved(buf, bufSize, nread))
635 n = ::read(fd_, buf, bufSize);
636 } while (n < 0 && errno == EINTR);
639 saveBytes(buf, nread);
643 ParentLocationMessenger(mgr).message(PosixStorageMessages::fdRead,
644 NumberMessageArg(fd_),
645 ErrnoMessageArg(errno));
653 Boolean PosixFdStorageObject::seek(off_t off, Messenger &mgr)
655 if (lseek(fd_, off, SEEK_SET) < 0) {
656 ParentLocationMessenger(mgr).message(PosixStorageMessages::fdLseek,
657 NumberMessageArg(fd_),
658 ErrnoMessageArg(errno));