first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / core / 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 astring),
55   _auto_close(true)
56 {}
57
58 byte_filer::byte_filer(const astring &filename, const astring &perms)
59 : _handle(new file_hider),
60   _filename(new astring),
61   _auto_close(true)
62 { open(filename, perms); }
63
64 byte_filer::byte_filer(const char *filename, const char *perms)
65 : _handle(new file_hider),
66   _filename(new astring),
67   _auto_close(true)
68 { open(filename, perms); }
69
70 byte_filer::byte_filer(bool auto_close, void *handle)
71 : _handle(new file_hider),
72   _filename(new astring),
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 astring byte_filer::filename() const { return *_filename; }
83
84 size_t byte_filer::file_size_limit() { return BTFL_FILE_TELL_LIMIT; }
85
86 bool byte_filer::open(const astring &filename, const astring &perms)
87 {
88   close();
89   _auto_close = true;  // reset since we know we're opening this.
90   *_filename = filename;
91 #ifndef __WIN32__
92   _handle->fp = filename.t()? fopen(filename.s(), perms.s()) : NIL;
93 #else
94   _handle->fp = filename.t()? _wfopen((wchar_t *)(UTF16 *)transcode_to_utf16(filename),
95       (wchar_t *)(UTF16 *)transcode_to_utf16(perms)) : NIL;
96
97 #ifdef DEBUG_BYTE_FILER
98   if (!_handle->fp)
99     wprintf((wchar_t *)(UTF16 *)transcode_to_utf16("could not open: %ls\n"),
100         (wchar_t *)(UTF16 *)transcode_to_utf16(filename));
101 #endif
102
103 #endif
104   return good();
105 }
106
107 void byte_filer::close()
108 {
109   *_filename = "";
110   if (_auto_close && _handle->fp) fclose(_handle->fp);
111   _handle->fp = NIL;
112 }
113
114 bool byte_filer::good() { return !!_handle->fp; }
115
116 size_t byte_filer::tell()
117 {
118   if (!_handle->fp) return 0;
119   long to_return = ::ftell(_handle->fp);
120   if (to_return == -1) {
121     // if we couldn't get the size, either the file isn't there or the size
122     // is too big for our OS to report.
123 ///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());
124     if (good()) return BTFL_FILE_TELL_LIMIT;
125     else return 0;
126   }
127   return size_t(to_return);
128 }
129
130 void *byte_filer::file_handle() { return _handle->fp; }
131
132 bool byte_filer::eof() { return !_handle->fp ? true : !!feof(_handle->fp); }
133
134 int byte_filer::read(abyte *buff, int size)
135 { return !_handle->fp ? 0 : int(::fread((char *)buff, 1, size, _handle->fp)); }
136
137 int byte_filer::write(const abyte *buff, int size)
138 { return !_handle->fp ? 0 : int(::fwrite((char *)buff, 1, size, _handle->fp)); }
139
140 int byte_filer::read(byte_array &buff, int desired_size)
141 {
142   buff.reset(desired_size);
143   int to_return = read(buff.access(), desired_size); 
144   buff.zap(to_return, buff.length() - 1);
145   return to_return;
146 }
147
148 int byte_filer::write(const byte_array &buff)
149 { return write(buff.observe(), buff.length()); }
150
151 size_t byte_filer::length()
152 {
153   size_t current_posn = tell();
154   seek(0, FROM_END);  // jump to end of file.
155   size_t file_size = tell();  // get position.
156   seek(int(current_posn), FROM_START);  // jump back to previous place.
157   return file_size;
158 }
159
160 int byte_filer::read(astring &s, int desired_size)
161 {
162   s.pad(desired_size + 2);
163   int found = read((abyte *)s.observe(), desired_size);
164   if (non_negative(found)) s[found] = '\0';
165   s.shrink();
166   return found;
167 }
168
169 int byte_filer::write(const astring &s, bool add_null)
170 {
171   int len = s.length();
172   if (add_null) len++;
173   return write((abyte *)s.observe(), len);
174 }
175
176 void byte_filer::flush()
177 {
178   if (!_handle->fp) return;
179   ::fflush(_handle->fp);
180 }
181
182 bool byte_filer::truncate()
183 {
184   flush();
185   int fnum = fileno(_handle->fp);
186 #ifdef __WIN32__
187   return SetEndOfFile((HANDLE)_get_osfhandle(fnum));
188 #else
189   size_t posn = tell();
190   // if we're at the highest point we can be, we no longer trust our
191   // ability to truncate properly.
192   if (posn >= file_size_limit())
193     return false;
194   return !ftruncate(fnum, posn);
195 #endif
196 }
197
198 bool byte_filer::seek(int where, origins origin)
199 {
200   if (!_handle->fp) return false;
201   int real_origin;
202   switch (origin) {
203     case FROM_START: real_origin = SEEK_SET; break;
204     case FROM_END: real_origin = SEEK_END; break;
205     case FROM_CURRENT: real_origin = SEEK_CUR; break;
206     default: return false;  // not a valid choice.
207   }
208   int ret = ::fseek(_handle->fp, where, real_origin);
209   return !ret;
210 }
211
212 int byte_filer::getline(abyte *buff, int desired_size)
213 {
214   if (!_handle->fp) return 0;
215   char *ret = ::fgets((char *)buff, desired_size, _handle->fp);
216   return !ret? 0 : int(strlen((char *)buff)) + 1;
217 }
218
219 int byte_filer::getline(byte_array &buff, int desired_size)
220 {
221   buff.reset(desired_size + 1);
222   return getline(buff.access(), desired_size);
223 }
224
225 int byte_filer::getline(astring &buff, int desired_size)
226 {
227   buff.pad(desired_size + 1);
228   int to_return = getline((abyte *)buff.access(), desired_size);
229   if (non_negative(to_return)) buff[to_return] = '\0';
230   buff.shrink();
231   return to_return;
232 }
233
234 } //namespace.
235
236