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