1 #include "lib.h"
2 
3 #include <sys/stat.h>
4 #include <limits.h>
5 #ifdef HAVE_DIRENT_H
6 #include <dirent.h>
7 #endif
8 
ReadFile(FILE * fp)9 Str *ReadFile(FILE *fp) {
10   char buffer[65536];
11   if (fp == NULL)
12     return NULL;
13   StrArr *result = new StrArr();
14   for (;;) {
15     size_t nbytes = fread(buffer, 1, sizeof(buffer), fp);
16     if (nbytes == 0)
17       break;
18     result->add(new Str(buffer, nbytes));
19   }
20   return StrJoin(result, "");
21 }
22 
ReadFile(const char * filename)23 Str *ReadFile(const char *filename) {
24   FILE *fp = fopen(filename, "r");
25   Str *result = ReadFile(fp);
26   fclose(fp);
27   return result;
28 }
29 
ReadFile(Str * filename)30 Str *ReadFile(Str *filename) {
31   return ReadFile(filename->c_str());
32 }
33 
WriteFile(FILE * fp,Str * data)34 int WriteFile(FILE *fp, Str *data) {
35   char *p = data->c_str();
36   Int len = data->len();
37   while (len != 0) {
38     Int written = fwrite(p, 1, len, fp);
39     if (written == 0)
40       return 0;
41     len -= written;
42     p += written;
43   }
44   return 1;
45 }
46 
WriteFile(const char * filename,Str * data)47 int WriteFile(const char *filename, Str *data) {
48   FILE *fp = fopen(filename, "w");
49   if (!fp)
50     return 0;
51   int success = WriteFile(fp, data);
52   fclose(fp);
53   return success;
54 }
55 
WriteFile(Str * filename,Str * data)56 int WriteFile(Str *filename, Str *data) {
57   return WriteFile(filename->c_str(), data);
58 }
59 
ReadLines(const char * filename)60 StrArr *ReadLines(const char *filename) {
61   Str *contents = ReadFile(filename);
62   return contents->splitLines();
63 }
64 
ReadLines(Str * filename)65 StrArr *ReadLines(Str *filename) {
66   return ReadLines(filename->c_str());
67 }
68 
69 static char safe_chars[256];
70 
InitSafeChars()71 void InitSafeChars() {
72   for (int i = 'a'; i <= 'z'; i++)
73     safe_chars[i] = 1;
74   for (int i = 'A'; i <= 'Z'; i++)
75     safe_chars[i] = 1;
76   for (int i = '0'; i <= '9'; i++)
77     safe_chars[i] = 1;
78   const char *p = ",._+:@%/-";
79   while (*p)
80     safe_chars[(unsigned char) *p++] = 1;
81 }
82 
83 INIT(_AdLibOS, InitSafeChars(););
84 
ShellEscape(Str * arg)85 Str *ShellEscape(Str *arg) {
86   int safe = 1;
87   for (Int i = 0; i < arg->len(); i++) {
88     if (!safe_chars[arg->byte(i)]) {
89       safe = 0;
90       break;
91     }
92   }
93   if (safe)
94     return arg;
95   Str *result = new Str(arg->len());
96   result->add('\'');
97   for (Str::Each it(arg); it; ++it) {
98     if (*it == '\'')
99       result->add("'\\''");
100     else
101       result->add(*it);
102   }
103   result->add('\'');
104   return result;
105 }
106 
BuildCommand(Str * prog,StrArr * args)107 Str *BuildCommand(Str *prog, StrArr *args) {
108   Str *command = new Str(1024);
109   command->add(ShellEscape(prog));
110   for (Int i = 0; i < args->len(); i++) {
111     command->add(' ')->add(ShellEscape(args->at(i)));
112   }
113   return command;
114 }
115 
ReadProcess(Str * prog,StrArr * args)116 Str *ReadProcess(Str *prog, StrArr *args) {
117   Str *command = BuildCommand(prog, args);
118   FILE *pipe = popen(command->c_str(), "r");
119   if (!pipe)
120     return NULL;
121   Str *result = ReadFile(pipe);
122   if (pclose(pipe))
123     return NULL;
124   return result;
125 }
126 
ReadProcessLines(Str * prog,StrArr * args)127 StrArr *ReadProcessLines(Str *prog, StrArr *args) {
128   Str *output = ReadProcess(prog, args);
129   return output->splitLines();
130 }
131 
WriteProcess(Str * prog,StrArr * args,Str * data)132 int WriteProcess(Str *prog, StrArr *args, Str *data) {
133   Str *command = BuildCommand(prog, args);
134   FILE *pipe = popen(command->c_str(), "w");
135   if (!pipe)
136     return 0;
137   int success = WriteFile(pipe, data);
138   if (pclose(pipe))
139     return 0;
140   return success;
141 }
142 
System(Str * prog,StrArr * args)143 int System(Str *prog, StrArr *args) {
144   Str *command = BuildCommand(prog, args);
145   int result = system(command->c_str());
146   if (result >= 256)
147     result >>= 8;
148   return result;
149 }
150 
Print(Str * str)151 void Print(Str *str) {
152   printf("%s", str->c_str());
153 }
154 
PrintLn(Str * str)155 void PrintLn(Str *str) {
156   printf("%s\n", str->c_str());
157 }
158 
PrintErr(Str * str)159 void PrintErr(Str *str) {
160   fprintf(stderr, "%s", str->c_str());
161 }
162 
PrintErrLn(Str * str)163 void PrintErrLn(Str *str) {
164   fprintf(stderr, "%s\n", str->c_str());
165 }
166 
Print(const char * str)167 void Print(const char *str) {
168   printf("%s", str);
169 }
170 
PrintLn(const char * str)171 void PrintLn(const char *str) {
172   printf("%s\n", str);
173 }
174 
PrintErr(const char * str)175 void PrintErr(const char *str) {
176   fprintf(stderr, "%s", str);
177 }
178 
PrintErrLn(const char * str)179 void PrintErrLn(const char *str) {
180   fprintf(stderr, "%s\n", str);
181 }
182 
Print(Int i)183 void Print(Int i) {
184   printf("%" WORD_FMT "d", i);
185 }
186 
PrintLn(Int i)187 void PrintLn(Int i) {
188   printf("%" WORD_FMT "d\n", i);
189 }
190 
PrintErr(Int i)191 void PrintErr(Int i) {
192   fprintf(stderr, "%" WORD_FMT "d", i);
193 }
194 
PrintErrLn(Int i)195 void PrintErrLn(Int i) {
196   fprintf(stderr, "%" WORD_FMT "d\n", i);
197 }
198 
CurrentDir()199 Str *CurrentDir() {
200 #ifdef PATH_MAX
201   char *path = getcwd(NULL, PATH_MAX);
202 #else
203   char *path = getcwd(NULL, 8192);
204 #endif
205   Str *result = new Str(path);
206   free(path);
207   return result;
208 }
209 
ChDir(const char * path)210 bool ChDir(const char *path) {
211   return !chdir(path);
212 }
213 
ChDir(Str * path)214 bool ChDir(Str *path) {
215   return ChDir(path->c_str());
216 }
217 
FileStat(FileInfo & info,const char * path,bool follow_links)218 bool FileStat(FileInfo &info, const char *path, bool follow_links) {
219   struct stat st;
220   if (follow_links) {
221     if (stat(path, &st) < 0)
222       return false;
223   } else {
224     if (lstat(path, &st) < 0)
225       return false;
226   }
227   memset(&info, 0, sizeof(info));
228   if (S_ISDIR(st.st_mode)) {
229     info.is_dir = true;
230   } else if (S_ISREG(st.st_mode)) {
231     info.is_file = true;
232   } else if (S_ISLNK(st.st_mode)) {
233     info.is_link = true;
234   } else {
235     info.is_other = true;
236   }
237   info.atime = st.st_atime;
238   info.mtime = st.st_mtime;
239   info.ctime = st.st_ctime;
240   info.size = st.st_size;
241   return true;
242 }
243 
FileStat(FileInfo & info,Str * path,bool follow_links)244 bool FileStat(FileInfo &info, Str *path, bool follow_links) {
245   return FileStat(info, path->c_str(), follow_links);
246 }
247 
FileStat(const char * path,bool follow_links)248 FileInfo *FileStat(const char *path, bool follow_links) {
249   FileInfo info;
250   if (FileStat(info, path, follow_links))
251     return new FileInfo(info);
252   else
253     return NULL;
254 }
255 
FileStat(Str * path,bool follow_links)256 FileInfo *FileStat(Str *path, bool follow_links) {
257   return FileStat(path->c_str(), follow_links);
258 }
259 
ListFiles(const char * path)260 StrArr *ListFiles(const char *path) {
261 #ifdef HAVE_DIRENT_H
262   StrArr *result = new StrArr();
263   DIR *dir = opendir(path);
264   if (!dir)
265     return NULL;
266   for (;;) {
267     struct dirent *entry = readdir(dir);
268     if (!entry)
269       break;
270     if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
271       continue;
272     result->add(new Str(entry->d_name));
273   }
274   closedir(dir);
275   return result;
276 #else
277   return NULL;
278 #endif
279 }
280 
ListFiles(Str * path)281 StrArr *ListFiles(Str *path) {
282   return ListFiles(path->c_str());
283 }
284 
WalkDir(StrArr * acc,const char * path,int mode)285 static void WalkDir(StrArr *acc, const char *path, int mode) {
286   StrArr *files = ListFiles(path);
287   if (!files)
288     return;
289   FileInfo info;
290   for (Int i = 0; i < files->len(); i++) {
291     Str *newpath = new Str(path);
292     if (!newpath->ends_with(PathSeparator))
293       newpath->add(PathSeparator);
294     newpath->add(files->at(i));
295     if (FileStat(info, newpath)) {
296       if (info.is_dir) {
297         newpath->add(PathSeparator);
298         WalkDir(acc, newpath->c_str(), mode);
299         if (mode & ListFilesAndDirs)
300           acc->add(newpath);
301       } else {
302         acc->add(newpath);
303       }
304     }
305   }
306 }
307 
ListFileTree(const char * path,int mode)308 StrArr *ListFileTree(const char *path, int mode) {
309   StrArr *result = new StrArr();
310   FileInfo info;
311   if (FileStat(info, path, true)) {
312     if (info.is_dir) {
313       Str *dir = new Str(path);
314       if (!dir->ends_with(PathSeparator))
315         dir->add(PathSeparator);
316       Int prefixlen = dir->len();
317       result->add(dir);
318       WalkDir(result, dir->c_str(), mode);
319       if (mode & ListFilesRelative) {
320         for (Int i = 0; i < result->len(); i++) {
321           result->at(i)->remove(0, prefixlen);
322         }
323       }
324     } else {
325       if ((mode & ListFilesRelative) == 0)
326         result->add(S(path));
327     }
328   }
329   return result;
330 }
331 
ListFileTree(Str * path,int mode)332 StrArr *ListFileTree(Str *path, int mode) {
333   return ListFileTree(path->c_str(), mode);
334 }
335 
DirName(Str * path)336 Str *DirName(Str *path) {
337   Int pos = path->rfind(PathSeparator);
338   if (pos == NOWHERE) {
339     return path->clone();
340   } else {
341     return path->range_excl(0, pos);
342   }
343 }
344 
BaseName(Str * path)345 Str *BaseName(Str *path) {
346   Int pos = path->rfind(PathSeparator);
347   if (pos == NOWHERE) {
348     return path->clone();
349   } else {
350     return path->range_excl(pos+1, path->len());
351   }
352 }
353 
FileExtension(Str * path)354 Str *FileExtension(Str *path) {
355   Int slash = path->rfind(PathSeparator);
356   Int dot = path->rfind('.');
357   if (dot == NOWHERE || dot < slash) {
358     return S("");
359   } else {
360     return path->range_excl(dot, path->len());
361   }
362 }
363 
364 
MakeDirRec(Str * path)365 static bool MakeDirRec(Str *path) {
366   FileInfo info;
367   path = path->clone();
368   for (Int i = 1; i < path->len();) {
369     Int p = path->find(PathSeparator, i);
370     if (p == NOWHERE)
371       break;
372     path->at(p) = '\0';
373     if (FileStat(info, path, true)) {
374       if (!info.is_dir)
375         return false;
376     } else if (mkdir(path->c_str(), 0777) < 0) {
377       return false;
378     }
379     path->at(p) = PathSeparator[0];
380     i = p+1;
381   }
382   if (FileStat(info, path, true)) {
383     if (!info.is_dir)
384       return false;
385   } else if (mkdir(path->c_str(), 0777) < 0) {
386     return false;
387   }
388   return true;
389 }
390 
AbsolutePath(Str * path)391 Str *AbsolutePath(Str *path) {
392   if (path->starts_with(PathSeparator)) {
393     return path->clone();
394   }
395   Str *result = CurrentDir();
396   return result->add(PathSeparator)->add(path);
397 }
398 
NormalizePath(Str * path)399 Str *NormalizePath(Str *path) {
400   bool abs = path->starts_with(PathSeparator);
401   StrArr *parts = path->split(PathSeparator);
402   StrArr *result = new StrArr();
403   for (Int i = 0; i < parts->len(); i++) {
404     Str *part = parts->at(i);
405     if (part->eq("") || part->eq(".")) {
406       // do nothing
407     } else if (part->eq("..")) {
408       if (result->len() > 0)
409         result->pop();
410       else if (!abs)
411         result->add(part);
412     } else {
413       result->add(part);
414     }
415   }
416   if (abs)
417     return S(PathSeparator)->add(StrJoin(result, PathSeparator));
418   else
419     return StrJoin(result, PathSeparator);
420 }
421 
GetEnv(const char * name)422 Str *GetEnv(const char *name) {
423   char *result = getenv(name);
424   if (result)
425     return new Str(result);
426   else
427     return NULL;
428 }
429 
GetEnv(Str * name)430 Str *GetEnv(Str *name) {
431   return GetEnv(name->c_str());
432 }
433 
ProgramPath()434 Str *ProgramPath() {
435   Str *arg0 = new Str(ArgV[0]);
436   Str *result = arg0;
437   if (arg0) {
438     if (FileStat(result, true)) {
439       return NormalizePath(AbsolutePath(result));
440     }
441     if (!arg0->starts_with(PathSeparator)) {
442       Str *path = GetEnv("PATH");
443       StrArr *paths = path ? path->split(':') : A();
444       for (Int i = 0; i < paths->len(); i++) {
445         Str *p = paths->at(i)->clone();
446         p->add(PathSeparator);
447         p->add(arg0);
448         if (FileStat(p, true)) {
449           result = p;
450           break;
451         }
452       }
453     }
454   }
455   return result ? NormalizePath(AbsolutePath(result)) : result;
456 }
457 
MakeDir(const char * path,bool recursive)458 bool MakeDir(const char *path, bool recursive) {
459   if (!recursive)
460     return mkdir(path, 0777) >= 0;
461   return MakeDirRec(S(path));
462 }
463 
MakeDir(Str * path,bool recursive)464 bool MakeDir(Str *path, bool recursive) {
465   if (!recursive)
466     return mkdir(path->c_str(), 0777) >= 0;
467   return MakeDirRec(path);
468 }
469 
RemoveDir(const char * path)470 bool RemoveDir(const char *path) {
471   return rmdir(path) >= 0;
472 }
473 
RemoveDir(Str * path)474 bool RemoveDir(Str *path) {
475   return RemoveDir(path->c_str());
476 }
477 
RemoveFile(const char * path)478 bool RemoveFile(const char *path) {
479   return unlink(path) >= 0;
480 }
481 
RemoveFile(Str * path)482 bool RemoveFile(Str *path) {
483   return RemoveFile(path->c_str());
484 }
485 
486 
Rename(const char * path,const char * path2)487 bool Rename(const char *path, const char *path2) {
488   return rename(path, path2) >= 0;
489 }
490 
Rename(Str * path,Str * path2)491 bool Rename(Str *path, Str *path2) {
492   return Rename(path->c_str(), path2->c_str());
493 }
494