Link with C++ linker
[oweals/cde.git] / cde / programs / nsgmls / PosixStorage.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
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.
26
27 #ifdef __GNUG__
28 #pragma implementation
29 #endif
30
31 #include "splib.h"
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"
39 #include "Message.h"
40 #include "StringC.h"
41 #include "StringOf.h"
42 #include "CharsetInfo.h"
43 #include "CodingSystem.h"
44 #include "macros.h"
45 #include "PosixStorageMessages.h"
46
47 #include <sys/types.h>
48 #include <stdio.h>
49 #include <ctype.h>
50
51 #ifdef SP_INCLUDE_IO_H
52 #include <io.h>         // for open, fstat, lseek, read prototypes
53 #endif
54
55 #ifdef SP_INCLUDE_UNISTD_H
56 #include <unistd.h>
57 #endif
58
59 #ifdef SP_INCLUDE_OSFCN_H
60 #include <osfcn.h>
61 #endif
62
63 #include <fcntl.h>
64 #include <sys/stat.h>
65 #include <errno.h>
66 #include <stddef.h>
67
68 #ifndef S_ISREG
69 #ifndef S_IFREG
70 #define S_IFREG _S_IFREG
71 #endif
72 #ifndef S_IFMT
73 #define S_IFMT _S_IFMT
74 #endif
75 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
76 #endif /* not S_ISREG */
77
78 #ifndef O_BINARY
79 #define O_BINARY 0
80 #endif
81
82 #ifdef SP_NAMESPACE
83 namespace SP_NAMESPACE {
84 #endif
85
86 #ifdef SP_WIDE_SYSTEM
87 typedef wchar_t FChar;
88 #else
89 typedef char FChar;
90 #endif
91
92 class PosixBaseStorageObject : public RewindStorageObject {
93 public:
94   PosixBaseStorageObject(int fd, Boolean mayRewind);
95   size_t getBlockSize() const;
96 protected:
97   enum { defaultBlockSize = 8192 };
98   int fd_;
99   PackedBoolean eof_;
100
101   Boolean seekToStart(Messenger &);
102   virtual Boolean seek(off_t, Messenger &) = 0;
103   static int xclose(int fd);
104 private:
105   Boolean canSeek(int fd);
106   off_t startOffset_;
107 };
108
109 PosixBaseStorageObject::PosixBaseStorageObject(int fd, Boolean mayRewind)
110 : fd_(fd), eof_(0),
111   RewindStorageObject(mayRewind, mayRewind && canSeek(fd))
112 {
113 }
114
115 Boolean PosixBaseStorageObject::canSeek(int fd)
116 {
117   struct stat sb;
118   if (fstat(fd, &sb) < 0 || !S_ISREG(sb.st_mode)
119       || (startOffset_ = lseek(fd, off_t(0), SEEK_CUR)) < 0)
120     return 0;
121   else
122     return 1;
123 }
124
125 Boolean PosixBaseStorageObject::seekToStart(Messenger &mgr)
126 {
127   eof_ = 0;
128   return seek(startOffset_, mgr);
129 }
130
131 int PosixBaseStorageObject::xclose(int fd)
132 {
133   int ret;
134   do {
135     ret = ::close(fd);
136   } while (ret < 0 && errno == EINTR);
137   return ret;
138 }
139
140 class PosixStorageObject : public PosixBaseStorageObject, private DescriptorUser {
141 public:
142   PosixStorageObject(int fd,
143                      const StringC &,
144                      const String<FChar> &,
145                      Boolean mayRewind,
146                      DescriptorManager *);
147   ~PosixStorageObject();
148   Boolean read(char *buf, size_t bufSize, Messenger &mgr, size_t &nread);
149   Boolean suspend();
150   Boolean seek(off_t, Messenger &);
151   void willNotRewind();
152 private:
153   void resume(Messenger &);
154
155   PackedBoolean suspended_;
156   off_t suspendPos_;
157   const MessageType2 *suspendFailedMessage_;
158   int suspendErrno_;
159   StringC filename_;
160   String<FChar> cfilename_;
161
162   void systemError(Messenger &, const MessageType2 &, int);
163 };
164
165 inline int openFile(const FChar *s) {
166 #ifdef SP_WIDE_SYSTEM
167     return _wopen(s, O_RDONLY|O_BINARY);
168 #else
169     return ::open(s, O_RDONLY|O_BINARY);
170 #endif
171   }
172
173 PosixStorageManager::PosixStorageManager(const char *type,
174                                          const UnivCharsetDesc &filenameCharset,
175 #ifndef SP_WIDE_SYSTEM
176                                          const OutputCodingSystem *filenameCodingSystem,
177 #endif
178                                          int maxFDs)
179 : IdStorageManager(filenameCharset),
180   type_(type),
181 #ifndef SP_WIDE_SYSTEM
182   filenameCodingSystem_(filenameCodingSystem),
183 #endif
184   descriptorManager_(maxFDs)
185 {
186   Char newline = idCharset()->execToDesc('\n');
187   reString_.assign(&newline, 1);
188 }
189
190 const char *PosixStorageManager::type() const
191 {
192   return type_;
193 }
194
195 void PosixStorageManager::addSearchDir(const StringC &str)
196 {
197   searchDirs_.push_back(str);
198 }
199
200 #ifdef SP_POSIX_FILENAMES
201
202 #define FILENAME_TYPE_DEFINED
203
204 // FIXME should use idCharset.
205
206 Boolean PosixStorageManager::isAbsolute(const StringC &file) const
207 {
208   return file.size() > 0 && file[0] == '/';
209 }
210
211 StringC PosixStorageManager::extractDir(const StringC &str) const
212 {
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
216   return StringC();
217 }
218
219 StringC PosixStorageManager::combineDir(const StringC &dir,
220                                         const StringC &base) const
221 {
222   StringC result(dir);
223   if (dir.size() > 0 && dir[dir.size() - 1] != '/')
224     result += '/';
225   result += base;
226   return result;
227 }
228
229 Boolean PosixStorageManager::transformNeutral(StringC &str, Boolean fold,
230                                               Messenger &) const
231 {
232   if (fold)
233     for (size_t i = 0; i < str.size(); i++) {
234       Char c = str[i];
235       if (c <= (unsigned char)-1)
236         str[i] = tolower(str[i]);
237     }
238   return 1;
239 }
240
241 #endif /* SP_POSIX_FILENAMES */
242
243 #ifdef SP_MSDOS_FILENAMES
244
245 #define FILENAME_TYPE_DEFINED
246
247 Boolean PosixStorageManager::isAbsolute(const StringC &s) const
248 {
249   if (s.size() == 0)
250     return 0;
251   return s[0] == '/' || s[0] == '\\' || (s.size() > 1 && s[1] == ':');
252 }
253
254 StringC PosixStorageManager::extractDir(const StringC &str) const
255 {
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
260   return StringC();
261 }
262
263 StringC PosixStorageManager::combineDir(const StringC &dir,
264                                         const StringC &base) const
265 {
266   StringC result(dir);
267   if (dir.size() > 0) {
268     Char lastChar = dir[dir.size() - 1];
269     if (lastChar != '/' && lastChar != '\\'
270         && !(dir.size() == 2 && lastChar == ':'))
271     result += '\\';
272   }
273   result += base;
274   return result;
275 }
276
277 Boolean PosixStorageManager::transformNeutral(StringC &str, Boolean,
278                                               Messenger &) const
279 {
280   for (size_t i = 0; i < str.size(); i++)
281     if (str[i] == '/')
282       str[i] = '\\';
283   return 1;
284 }
285
286 #endif /* SP_MSDOS_FILENAMES */
287
288 #ifndef FILENAME_TYPE_DEFINED
289
290 Boolean PosixStorageManager::isAbsolute(const StringC &) const
291 {
292   return 1;
293 }
294
295 StringC PosixStorageManager::extractDir(const StringC &) const
296 {
297   return StringC();
298 }
299
300 StringC PosixStorageManager::combineDir(const StringC &,
301                                         const StringC &base) const
302 {
303   return base;
304 }
305
306 Boolean PosixStorageManager::transformNeutral(StringC &, Boolean,
307                                               Messenger &) const
308 {
309   return 1;
310 }
311
312 #endif /* not FILENAME_TYPE_DEFINED */
313
314 Boolean PosixStorageManager::resolveRelative(const StringC &baseId,
315                                              StringC &specId,
316                                              Boolean search) const
317 {
318   if (isAbsolute(specId))
319     return 1;
320   if (!search || searchDirs_.size() == 0) {
321     specId = combineDir(extractDir(baseId), specId);
322     return 1;
323   }
324   return 0;
325 }
326
327 StorageObject *
328 PosixStorageManager::makeStorageObject(const StringC &spec,
329                                        const StringC &base,
330                                        Boolean search,
331                                        Boolean mayRewind,
332                                        Messenger &mgr,
333                                        StringC &found)
334 {
335   if (spec.size() == 0) {
336     mgr.message(PosixStorageMessages::invalidFilename,
337                 StringMessageArg(spec));
338     return 0;
339   }
340   descriptorManager_.acquireD();
341   Boolean absolute = isAbsolute(spec);
342   SearchResultMessageArg sr;
343   for (size_t i = 0; i < searchDirs_.size() + 1; i++) {
344     StringC filename;
345     if (absolute)
346       filename = spec;
347     else if (i == 0)
348         filename = combineDir(extractDir(base), spec);
349     else
350       filename = combineDir(searchDirs_[i - 1], spec);
351 #ifdef SP_WIDE_SYSTEM
352     String<FChar> cfilename(filename);
353     cfilename += FChar(0);
354 #else
355     String<FChar> cfilename = filenameCodingSystem_->convertOut(filename);
356 #endif
357     int fd;
358     do {
359       fd = openFile(cfilename.data());
360     } while (fd < 0 && errno == EINTR);
361     if (fd >= 0) {
362       found = filename;
363       return new PosixStorageObject(fd,
364                                     filename,
365                                     cfilename,
366                                     mayRewind,
367                                     &descriptorManager_);
368     }
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();
375       return 0;
376     }
377     sr.add(filename, savedErrno);
378   }
379   descriptorManager_.releaseD();
380   ParentLocationMessenger(mgr).message(PosixStorageMessages::cannotFind,
381                                        StringMessageArg(spec), sr);
382   return 0;
383 }
384
385 PosixStorageObject::PosixStorageObject(int fd,
386                                        const StringC &filename,
387                                        const String<FChar> &cfilename,
388                                        Boolean mayRewind,
389                                        DescriptorManager *manager)
390 : DescriptorUser(manager),
391   PosixBaseStorageObject(fd, mayRewind),
392   suspended_(0),
393   filename_(filename),
394   cfilename_(cfilename)
395 {
396 }
397
398 PosixStorageObject::~PosixStorageObject()
399 {
400   if (fd_ >= 0) {
401     (void)xclose(fd_);
402     releaseD();
403   }
404 }
405
406 Boolean PosixStorageObject::seek(off_t off, Messenger &mgr)
407 {
408   if (lseek(fd_, off, SEEK_SET) < 0) {
409     fd_ = -1;
410     systemError(mgr, PosixStorageMessages::lseekSystemCall, errno);
411     return 0;
412   }
413   else
414     return 1;
415 }
416
417 Boolean PosixStorageObject::read(char *buf, size_t bufSize, Messenger &mgr,
418                                  size_t &nread)
419 {
420   if (readSaved(buf, bufSize, nread))
421     return 1;
422   if (suspended_)
423     resume(mgr);
424   if (fd_ < 0 || eof_)
425     return 0;
426   long n;
427   do {
428     n = ::read(fd_, buf, bufSize);
429   } while (n < 0 && errno == EINTR);
430   if (n > 0) {
431     nread = size_t(n);
432     saveBytes(buf, nread);
433     return 1;
434   }
435   if (n < 0) {
436     int saveErrno = errno;
437     releaseD();
438     (void)xclose(fd_);
439     systemError(mgr, PosixStorageMessages::readSystemCall, saveErrno);
440     fd_ = -1;
441   }
442   else {
443     eof_ = 1;
444     // n == 0, so end of file
445     if (!mayRewind_) {
446       releaseD();
447       if (xclose(fd_) < 0)
448         systemError(mgr, PosixStorageMessages::closeSystemCall, errno);
449       fd_ = -1;
450     }
451     
452   }
453   return 0;
454 }
455
456 void PosixStorageObject::willNotRewind()
457 {
458   RewindStorageObject::willNotRewind();
459   if (eof_ && fd_ >= 0) {
460     releaseD();
461     (void)xclose(fd_);
462     fd_ = -1;
463   }
464 }
465
466 Boolean PosixStorageObject::suspend()
467 {
468   if (fd_ < 0 || suspended_)
469     return 0;
470   struct stat sb;
471   if (fstat(fd_, &sb) < 0 || !S_ISREG(sb.st_mode))
472     return 0;
473   suspendFailedMessage_ = 0;
474   suspendPos_ = lseek(fd_, 0, SEEK_CUR);
475   if (suspendPos_ == (off_t)-1) {
476     suspendFailedMessage_ = &PosixStorageMessages::lseekSystemCall;
477     suspendErrno_ = errno;
478   }
479   if (xclose(fd_) < 0 && !suspendFailedMessage_) {
480     suspendFailedMessage_ = &PosixStorageMessages::closeSystemCall;
481     suspendErrno_ = errno;
482   }
483   fd_ = -1;
484   suspended_ = 1;
485   releaseD();
486   return 1;
487 }
488
489 void PosixStorageObject::resume(Messenger &mgr)
490 {
491   ASSERT(suspended_);
492   if (suspendFailedMessage_) {
493     systemError(mgr, *suspendFailedMessage_, suspendErrno_);
494     suspended_ = 0;
495     return;
496   }
497   acquireD();
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.
500   suspended_ = 0;
501   do {
502     fd_ = openFile(cfilename_.data());
503   } while (fd_ < 0 && errno == EINTR);
504   if (fd_ < 0) {
505     releaseD();
506     systemError(mgr, PosixStorageMessages::openSystemCall, errno);
507     return;
508   }
509   if (lseek(fd_, suspendPos_, SEEK_SET) < 0) {
510     systemError(mgr, PosixStorageMessages::lseekSystemCall, errno);
511     (void)xclose(fd_);
512     fd_ = -1;
513     releaseD();
514   }
515 }
516
517 #ifdef SP_STAT_BLKSIZE
518
519 size_t PosixBaseStorageObject::getBlockSize() const
520 {
521   struct stat sb;
522   long sz;
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))
528     sz = size_t(-1);
529   else
530     sz = sb.st_blksize;
531   return sz;
532 }
533
534 #else /* not SP_STAT_BLKSIZE */
535
536 size_t PosixBaseStorageObject::getBlockSize() const
537 {
538   return defaultBlockSize;
539 }
540
541 #endif /* not SP_STAT_BLKSIZE */
542
543 void PosixStorageObject::systemError(Messenger &mgr,
544                                      const MessageType2 &msg,
545                                      int err)
546 {
547   ParentLocationMessenger(mgr).message(msg,
548                                        StringMessageArg(filename_),
549                                        ErrnoMessageArg(err));
550 }
551
552 class PosixFdStorageObject : public PosixBaseStorageObject {
553 public:
554   PosixFdStorageObject(int, Boolean mayRewind);
555   Boolean read(char *buf, size_t bufSize, Messenger &mgr, size_t &nread);
556   Boolean seek(off_t, Messenger &);
557   enum {
558     noError,
559     readError,
560     invalidNumberError,
561     lseekError
562   };
563 private:
564   int origFd_;
565 };
566
567 PosixFdStorageManager::PosixFdStorageManager(const char *type,
568                                              const UnivCharsetDesc &idCharset)
569 : IdStorageManager(idCharset), type_(type)
570 {
571 }
572
573 Boolean PosixFdStorageManager::inheritable() const
574 {
575   return 0;
576 }
577
578 StorageObject *PosixFdStorageManager::makeStorageObject(const StringC &id,
579                                                         const StringC &,
580                                                         Boolean,
581                                                         Boolean mayRewind,
582                                                         Messenger &mgr,
583                                                         StringC &foundId)
584 {
585   int n = 0;
586   size_t i;
587   for (i = 0; i < id.size(); i++) {
588     UnivChar ch;
589     if (!idCharset()->descToUniv(id[i], ch))
590       break;
591     if (ch < UnivCharsetDesc::zero || ch > UnivCharsetDesc::zero + 9)
592       break;
593     int digit = ch - UnivCharsetDesc::zero;
594     // Allow the division to be done at compile-time.
595     if (n > INT_MAX/10)
596       break;
597     n *= 10;
598     if (n > INT_MAX - digit)
599       break;
600     n += digit;
601   }
602   if (i < id.size() || i == 0) {
603     mgr.message(PosixStorageMessages::invalidNumber,
604                 StringMessageArg(id));
605     return 0;
606   }
607   foundId = id;
608   // Could check access mode with fcntl(n, F_GETFL).
609   return new PosixFdStorageObject(n, mayRewind);
610 }
611
612 PosixFdStorageObject::PosixFdStorageObject(int fd, Boolean mayRewind)
613 : PosixBaseStorageObject(fd, mayRewind), origFd_(fd)
614 {
615 }
616
617 const char *PosixFdStorageManager::type() const
618 {
619   return type_;
620 }
621
622 Boolean PosixFdStorageObject::read(char *buf, size_t bufSize, Messenger &mgr,
623                                    size_t &nread)
624 {
625   if (readSaved(buf, bufSize, nread))
626     return 1;
627   if (fd_ < 0 || eof_)
628     return 0;
629   long n;
630   do {
631     n = ::read(fd_, buf, bufSize);
632   } while (n < 0 && errno == EINTR);
633   if (n > 0) {
634     nread = size_t(n);
635     saveBytes(buf, nread);
636     return 1;
637   }
638   if (n < 0) {
639     ParentLocationMessenger(mgr).message(PosixStorageMessages::fdRead,
640                                          NumberMessageArg(fd_),
641                                          ErrnoMessageArg(errno));
642     fd_ = -1;
643   }
644   else
645     eof_ = 1;
646   return 0;
647 }
648
649 Boolean PosixFdStorageObject::seek(off_t off, Messenger &mgr)
650 {
651   if (lseek(fd_, off, SEEK_SET) < 0) {
652     ParentLocationMessenger(mgr).message(PosixStorageMessages::fdLseek,
653                                          NumberMessageArg(fd_),
654                                          ErrnoMessageArg(errno));
655     return 0;
656   }
657   else
658     return 1;
659 }
660
661 #ifdef SP_NAMESPACE
662 }
663 #endif