1 //  Construo - A wire-frame construction game
2 //  Copyright (C) 2002 Ingo Ruhnke <grumbel@gmx.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 
17 #include <stdio.h>
18 #include <assert.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/time.h>
22 #include <time.h>
23 #include <string>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <dirent.h>
27 #include <pwd.h>
28 #include <errno.h>
29 #include <iostream>
30 #include <algorithm>
31 #include "construo_error.hpp"
32 #include "string_utils.hpp"
33 #include "construo.hpp"
34 #include "path_manager.hpp"
35 #include "unix_system.hpp"
36 
37 using namespace StringUtils;
38 
UnixSystem()39 UnixSystem::UnixSystem ()
40 { // riped out of ClanLib-0.7
41   timeval tv;
42   gettimeofday(&tv, NULL);
43   start_time = (long) tv.tv_sec*(long) 1000+(long) tv.tv_usec/(long) 1000;
44 
45   char* home = getenv("HOME");
46   if (home)
47     {
48       construo_rc_path = std::string(home) + std::string("/.construo/");
49     }
50   else
51     {
52       std::cout << "UnixSystem: FATAL ERROR: couldn't find env variable $HOME" << std::endl;
53       throw ConstruoError ("UnixSystem: Couldn't find $HOME!");
54     }
55 
56   // create $HOME directory if not already there
57   struct stat buf;
58 
59   if (stat(construo_rc_path.c_str(), &buf) != 0) // Couldn't find directory, create it
60     {
61       if (mkdir(construo_rc_path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP) != 0)
62         {
63           throw ConstruoError(std::string("UnixSystem: ") + construo_rc_path + ": "
64                               + strerror(errno));
65         }
66     }
67   else
68     {
69       if (S_ISDIR(buf.st_rdev)) // Is not a directory
70         {
71           throw ConstruoError("Error: " + construo_rc_path + " is not a directory!");
72         }
73 
74       if (access(construo_rc_path.c_str (), R_OK | W_OK | X_OK) != 0) // not readable/writeable
75         {
76           throw ConstruoError("Error: " + construo_rc_path + " is not read or writeable!");
77         }
78     }
79 }
80 
~UnixSystem()81 UnixSystem::~UnixSystem ()
82 {
83 }
84 
85 unsigned int
get_time()86 UnixSystem::get_time ()
87 { // riped out of ClanLib-0.7
88   timeval tv;
89   gettimeofday(&tv, NULL);
90 
91   long tid = (long) tv.tv_sec*(long) 1000 + (long) tv.tv_usec/(long) 1000 - start_time;
92 
93   return tid;
94 }
95 
96 void
sleep(unsigned long t)97 UnixSystem::sleep (unsigned long t)
98 {
99   usleep (t);
100 }
101 
102 std::string
get_construo_rc_path()103 UnixSystem::get_construo_rc_path()
104 {
105   return construo_rc_path;
106 }
107 
108 std::string
get_user_realname()109 UnixSystem::get_user_realname()
110 {
111   struct passwd* pw;
112 
113   pw = getpwuid(getuid());
114   if (pw)
115     {
116       return pw->pw_gecos;
117     }
118   else
119     {
120       return "";
121     }
122 }
123 
124 std::string
get_user_email()125 UnixSystem::get_user_email()
126 {
127   const char* s_email = getenv("EMAIL");
128   if (s_email)
129     {
130       return s_email;
131     }
132   else
133     return "";
134 }
135 
136 unsigned int
get_mtime(const std::string & filename)137 UnixSystem::get_mtime (const std::string& filename)
138 {
139   std::string sys_name = translate_filename(filename);
140 
141   struct stat buf;
142   if (stat(sys_name.c_str(), &buf) != 0)
143     {
144       return 0;
145     }
146   else
147     {
148       return buf.st_mtime;
149     }
150 }
151 
152 FileType
get_file_type(const std::string & filename)153 UnixSystem::get_file_type(const std::string& filename)
154 {
155   if (filename == "/examples/"
156       || filename == "/user/")
157     return FT_DIRECTORY;
158 
159   std::string sys_name = translate_filename(filename);
160 
161   struct stat buf;
162   if (stat(sys_name.c_str(), &buf) != 0)
163     {
164       std::cout << "UnixSystem: ERROR: Couldn't stat: '" << sys_name << "'" << std::endl;
165       return FT_UNKNOWN_FILE;
166     }
167   else
168     {
169       if (S_ISDIR(buf.st_mode))
170         {
171           return FT_DIRECTORY;
172         }
173       else if (S_ISREG(buf.st_mode))
174         {
175           if (has_suffix(filename, ".construo") || has_suffix(filename, ".construo.gz"))
176             return FT_CONSTRUO_FILE;
177           else
178             {
179               return FT_UNKNOWN_FILE;
180             }
181         }
182       else
183         {
184           return FT_UNKNOWN_FILE;
185         }
186     }
187 }
188 
189 std::string
translate_filename(const std::string & filename)190 UnixSystem::translate_filename (const std::string& filename)
191 {
192   if (filename == "/")
193     {
194       assert("root directory is not translatable");
195       return "";
196     }
197   else if (has_prefix(filename, "/user/"))
198     {
199       return construo_rc_path + filename.substr(6);
200     }
201   else if (has_prefix(filename, "/examples/"))
202     {
203       return path_manager.complete("examples/") + filename.substr(10);
204     }
205   else
206     return filename;
207 }
208 
209 FILE*
open_input_file(const std::string & filename)210 UnixSystem::open_input_file(const std::string& filename)
211 {
212   //std::cout << "UnixSystem: open_input_file: " << translate_filename (filename) << std::endl;
213   return fopen(translate_filename (filename).c_str(), "r");
214 }
215 
216 FILE*
open_output_file(const std::string & filename)217 UnixSystem::open_output_file(const std::string& filename)
218 {
219   //std::cout << "UnixSystem: open_output_file: " << translate_filename (filename) << std::endl;
220   return fopen(translate_filename (filename).c_str(), "w");
221 }
222 
223 
224 /** Sort directories before files and sort them all
225     alphabetically */
226 struct DirectorySorter
227 {
228   std::string pathname;
229 
DirectorySorterDirectorySorter230   DirectorySorter(const std::string& p)
231     : pathname(p)
232   {
233   }
234 
operator ()DirectorySorter235   bool operator()(const std::string& lhs, const std::string& rhs)
236   {
237     FileType lhs_type = system_context->get_file_type(pathname + "/" + lhs);
238     FileType rhs_type = system_context->get_file_type(pathname + "/" + rhs);
239 
240     if (lhs_type == rhs_type)
241       return (lhs < rhs);
242     else if (lhs_type == FT_DIRECTORY)
243       {
244         return true;
245       }
246     else if (rhs_type == FT_DIRECTORY)
247       {
248         return false;
249       }
250     else
251       {
252         return (lhs < rhs);
253       }
254   }
255 };
256 
257 std::vector<std::string>
read_directory(const std::string & arg_pathname)258 UnixSystem::read_directory(const std::string& arg_pathname)
259 {
260   if (arg_pathname == "/")
261     {
262       std::vector<std::string> ret;
263       ret.push_back("examples/");
264       ret.push_back("user/");
265       return ret;
266     }
267   else
268     {
269       std::vector<std::string> dir_lst;
270       std::string pathname = translate_filename (arg_pathname);
271 
272       DIR* dir = ::opendir (pathname.c_str());
273 
274       if (!dir)
275         {
276           std::cout << "UnixSystem: Error couldn't open: '" << pathname << "', ignoring\n"
277                     << "            error and continuing with an empty directory" << std::endl;
278         }
279       else
280         {
281           struct dirent* entry;
282 
283           while ((entry = readdir(dir)) != 0)
284             {
285               if (strcmp(entry->d_name, ".") != 0
286                   && strcmp(entry->d_name, "..") != 0
287                   && strcmp(entry->d_name, "CVS") != 0)
288                 { // We ignore unusefull directories
289                   dir_lst.push_back(entry->d_name);
290                 }
291             }
292 
293           closedir (dir);
294         }
295 
296       std::sort(dir_lst.begin(), dir_lst.end(), DirectorySorter(pathname));
297 
298       return dir_lst;
299     }
300 }
301 
302 /* EOF */
303