0384d2ec74f8328506eef10d7176465d27d33439
[feisty_meow.git] / nucleus / library / filesystem / byte_filer.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : byte_filer                                                        *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2000-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "byte_filer.h"
16
17 #include <application/windoze_helper.h>
18 #include <basis/astring.h>
19 #include <basis/byte_array.h>
20 #include <basis/functions.h>
21 #include <basis/utf_conversion.h>
22
23 #include <stdio.h>
24 #include <string.h>
25 #ifdef __UNIX__
26   #include <unistd.h>
27 #endif
28 #ifdef __WIN32__
29   #include <io.h>
30 #endif
31
32 #define DEBUG_BYTE_FILER
33   // uncomment for noisy version of class.
34
35 using namespace basis;
36
37 namespace filesystem {
38
39 const size_t BTFL_FILE_TELL_LIMIT = size_t(2) * size_t(GIGABYTE);
40   // the largest a long integer can represent in the tell system call.
41
42 class file_hider
43 {
44 public:
45   FILE *fp;  // the real file pointer.
46
47   file_hider() : fp(NIL) {}
48 };
49
50 //////////////
51
52 byte_filer::byte_filer()
53 : _handle(new file_hider),
54   _filename(new filename),
55   _auto_close(true)
56 {}
57
58 byte_filer::byte_filer(const astring &fname, const astring &perms)
59 : _handle(new file_hider),
60   _filename(new filename),
61   _auto_close(true)
62 { open(fname, perms); }
63
64 byte_filer::byte_filer(const char *fname, const char *perms)
65 : _handle(new file_hider),
66   _filename(new filename),
67   _auto_close(true)
68 { open(fname, perms); }
69
70 byte_filer::byte_filer(bool auto_close, void *handle)
71 : _handle(new file_hider),
72   _filename(new filename),
73   _auto_close(auto_close)
74 {
75   if (handle) {
76     _handle->fp = (FILE *)handle;
77   }
78 }
79
80 byte_filer::~byte_filer() { close(); WHACK(_handle); WHACK(_filename); }
81
82 const astring &byte_filer::name() const { return _filename->raw(); }
83
84 size_t byte_filer::file_size_limit() { return BTFL_FILE_TELL_LIMIT; }
85
86 bool byte_filer::open(const astring &fname, const astring &perms)
87 {
88   close();
89   _auto_close = true;  // reset since we know we're opening this.
90   _filename->reset(fname);
91   _handle->fp = _filename->raw().t()? fopen(_filename->raw().s(), perms.s()) : NIL;
92   if (_handle->fp == NIL) return false;
93   return good();
94 }
95
96 void byte_filer::close()
97 {
98   _filename->reset("");
99   if (_auto_close && _handle->fp) fclose(_handle->fp);
100   _handle->fp = NIL;
101 }
102
103 bool byte_filer::good() { return !!_handle->fp; }
104
105 size_t byte_filer::tell()
106 {
107   if (!_handle->fp) return 0;
108   long to_return = ::ftell(_handle->fp);
109   if (to_return == -1) {
110     // if we couldn't get the size, either the file isn't there or the size
111     // is too big for our OS to report.
112 ///printf(a_sprintf("failed to tell size, calling it %.0f, and one plus that is %.0f\n", double(BTFL_FILE_TELL_LIMIT), double(long(long(BTFL_FILE_TELL_LIMIT) + 1))).s());
113     if (good()) return BTFL_FILE_TELL_LIMIT;
114     else return 0;
115   }
116   return size_t(to_return);
117 }
118
119 void *byte_filer::file_handle() { return _handle->fp; }
120
121 bool byte_filer::eof() { return !_handle->fp ? true : !!feof(_handle->fp); }
122
123 int byte_filer::read(abyte *buff, int size)
124 { return !_handle->fp ? 0 : int(::fread((char *)buff, 1, size, _handle->fp)); }
125
126 int byte_filer::write(const abyte *buff, int size)
127 { return !_handle->fp ? 0 : int(::fwrite((char *)buff, 1, size, _handle->fp)); }
128
129 int byte_filer::read(byte_array &buff, int desired_size)
130 {
131   buff.reset(desired_size);
132   int to_return = read(buff.access(), desired_size); 
133   buff.zap(to_return, buff.length() - 1);
134   return to_return;
135 }
136
137 int byte_filer::write(const byte_array &buff)
138 { return write(buff.observe(), buff.length()); }
139
140 size_t byte_filer::length()
141 {
142   size_t current_posn = tell();
143   seek(0, FROM_END);  // jump to end of file.
144   size_t file_size = tell();  // get position.
145   seek(int(current_posn), FROM_START);  // jump back to previous place.
146   return file_size;
147 }
148
149 int byte_filer::read(astring &s, int desired_size)
150 {
151   s.pad(desired_size + 2);
152   int found = read((abyte *)s.observe(), desired_size);
153   if (non_negative(found)) s[found] = '\0';
154   s.shrink();
155   return found;
156 }
157
158 int byte_filer::write(const astring &s, bool add_null)
159 {
160   int len = s.length();
161   if (add_null) len++;
162   return write((abyte *)s.observe(), len);
163 }
164
165 void byte_filer::flush()
166 {
167   if (!_handle->fp) return;
168   ::fflush(_handle->fp);
169 }
170
171 bool byte_filer::truncate()
172 {
173   flush();
174   int fnum = fileno(_handle->fp);
175 #ifdef __WIN32__
176   return SetEndOfFile((HANDLE)_get_osfhandle(fnum));
177 #else
178   size_t posn = tell();
179   // if we're at the highest point we can be, we no longer trust our
180   // ability to truncate properly.
181   if (posn >= file_size_limit())
182     return false;
183   return !ftruncate(fnum, posn);
184 #endif
185 }
186
187 bool byte_filer::seek(int where, origins origin)
188 {
189   if (!_handle->fp) return false;
190   int real_origin;
191   switch (origin) {
192     case FROM_START: real_origin = SEEK_SET; break;
193     case FROM_END: real_origin = SEEK_END; break;
194     case FROM_CURRENT: real_origin = SEEK_CUR; break;
195     default: return false;  // not a valid choice.
196   }
197   int ret = ::fseek(_handle->fp, where, real_origin);
198   return !ret;
199 }
200
201 int byte_filer::getline(abyte *buff, int desired_size)
202 {
203   if (!_handle->fp) return 0;
204   char *ret = ::fgets((char *)buff, desired_size, _handle->fp);
205   return !ret? 0 : int(strlen((char *)buff)) + 1;
206 }
207
208 int byte_filer::getline(byte_array &buff, int desired_size)
209 {
210   buff.reset(desired_size + 1);
211   return getline(buff.access(), desired_size);
212 }
213
214 int byte_filer::getline(astring &buff, int desired_size)
215 {
216   buff.pad(desired_size + 1);
217   int to_return = getline((abyte *)buff.access(), desired_size);
218   if (non_negative(to_return)) buff[to_return] = '\0';
219   buff.shrink();
220   return to_return;
221 }
222
223 } //namespace.
224
225