Back to index

texmacs  1.0.7.15
file.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : file.cpp
00004 * DESCRIPTION: file handling
00005 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "file.hpp"
00013 #include "sys_utils.hpp"
00014 #include "analyze.hpp"
00015 #include "hashmap.hpp"
00016 #include "timer.hpp"
00017 #include "merge_sort.hpp"
00018 #include "data_cache.hpp"
00019 #include "web_files.hpp"
00020 #include "scheme.hpp"
00021 
00022 #include <stddef.h>
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <errno.h>
00026 #include <dirent.h>
00027 #ifdef OS_WIN32
00028 #include <sys/misc.h>
00029 #include <sys/_stat.h>
00030 #include <X11/Xlib.h>
00031 #else
00032 #include <sys/stat.h>
00033 #endif
00034 #include <sys/types.h>
00035 
00036 #ifdef MACOSX_EXTENSIONS
00037 #include "MacOS/mac_images.h"
00038 #endif
00039 
00040 /******************************************************************************
00041 * New style loading and saving
00042 ******************************************************************************/
00043 
00044 bool
00045 load_string (url u, string& s, bool fatal) {
00046   // cout << "Load " << u << LF;
00047   url r= u;
00048   if (!is_rooted_name (r)) r= resolve (r);
00049   // cout << "Resolved " << r << LF;
00050   bool err= !is_rooted_name (r);
00051   if (!err) {
00052     string name= concretize (r);
00053     // cout << "Concrete :" << name << LF;
00054     // File contents in cache?
00055     bool file_flag= do_cache_file (name);
00056     bool doc_flag= do_cache_doc (name);
00057     string cache_type= doc_flag? string ("doc_cache"): string ("file_cache");
00058     if (doc_flag) cache_load ("doc_cache");
00059     if (is_cached (cache_type, name) && is_up_to_date (url_parent (r))) {
00060       s= cache_get (cache_type, name) -> label;
00061       return false;
00062     }
00063     // End caching
00064 
00065     bench_start ("load file");
00066     char* _name= as_charp (name);
00067     // cout << "OPEN :" << _name << LF;
00068 #if defined (OS_WIN32)
00069     FILE* fin= _fopen (_name, "rb");
00070 #elif defined (__MINGW__) || defined (__MINGW32__)
00071     FILE* fin= fopen (_name, "rb");
00072 #else
00073     FILE* fin= fopen (_name, "r");
00074 #endif
00075     if (fin == NULL) {
00076       err= true;
00077       if (!occurs ("system", name))
00078         cerr << "TeXmacs] warning, load error for " << name << ", "
00079              << sys_errlist[errno] << "\n";
00080     }
00081     int size= 0;
00082     if (!err) {
00083       if (fseek (fin, 0L, SEEK_END) < 0) err= true;
00084       else {
00085        size= ftell (fin);
00086        if (size<0) err= true;
00087       }
00088       if (err) {
00089         cerr << "TeXmacs] warning, seek failed for " << as_string (u) << "\n";
00090         fclose (fin);
00091       }
00092     }
00093     if (!err) {
00094       rewind (fin);
00095       s->resize (size);
00096       int read= fread (&(s[0]), 1, size, fin);
00097       if (read < size) s->resize (read);
00098       fclose (fin);
00099     }
00100     tm_delete_array (_name);
00101     bench_cumul ("load file");
00102 
00103     // Cache file contents
00104     if (!err && N(s) <= 10000)
00105       if (file_flag || doc_flag)
00106        cache_set (cache_type, name, s);
00107     // End caching
00108   }
00109   if (err && fatal) {
00110     cerr << "File name= " << as_string (u) << "\n";
00111     FAILED ("file not readable");
00112   }
00113   return err;
00114 }
00115 
00116 bool
00117 save_string (url u, string s, bool fatal) {
00118   if (is_rooted_tmfs (u)) {
00119     bool err= save_to_server (u, s);
00120     if (err && fatal) {
00121       cerr << "File name= " << as_string (u) << "\n";
00122       FAILED ("file not writeable");
00123     }
00124     return err;
00125   }
00126 
00127   // cout << "Save " << u << LF;
00128   url r= u;
00129   if (!is_rooted_name (r)) r= resolve (r, "");
00130   bool err= !is_rooted_name (r);
00131   if (!err) {
00132     string name= concretize (r);
00133     char* _name= as_charp (name);
00134 #if defined (OS_WIN32)
00135     FILE* fout= _fopen (_name, "wb");
00136 #elif defined (__MINGW__) || defined (__MINGW32__)
00137     FILE* fout= fopen (_name, "wb");
00138 #else
00139     FILE* fout= fopen (_name, "w");
00140 #endif
00141     if (fout == NULL) {
00142       err= true;
00143       cerr << "TeXmacs] warning, save error for " << name << ", "
00144            << sys_errlist[errno] << "\n";
00145     }
00146     if (!err) {
00147       int i, n= N(s);
00148       for (i=0; i<n; i++)
00149        fputc (s[i], fout);
00150       fclose (fout);
00151     }
00152     tm_delete_array (_name);
00153 
00154     // Cache file contents
00155     bool file_flag= do_cache_file (name);
00156     bool doc_flag= do_cache_doc (name);
00157     string cache_type= doc_flag? string ("doc_cache"): string ("file_cache");
00158     if (!err && N(s) <= 10000)
00159       if (file_flag || doc_flag)
00160        cache_set (cache_type, name, s);
00161     (bool) is_up_to_date (url_parent (r), true);
00162     // End caching
00163   }
00164 
00165   if (err && fatal) {
00166     cerr << "File name= " << as_string (u) << "\n";
00167     FAILED ("file not writeable");
00168   }
00169   return err;
00170 }
00171 
00172 /******************************************************************************
00173 * Getting attributes of a file
00174 ******************************************************************************/
00175 
00176 static bool
00177 get_attributes (url name, struct stat* buf,
00178               bool link_flag=false, bool cache_flag= true)
00179 {
00180   // cout << "Stat " << name << LF;
00181   string name_s= concretize (name);
00182 
00183   // Stat result in cache?
00184   if (cache_flag &&
00185       is_cached ("stat_cache.scm", name_s) &&
00186       is_up_to_date (url_parent (name)))
00187     {
00188       string r= cache_get ("stat_cache.scm", name_s) -> label;
00189       if (r == "#f") return true;
00190       buf->st_mode= ((unsigned int) as_int (r));
00191       return false;
00192     }
00193   // End caching
00194 
00195   bench_start ("stat");
00196   bool flag;
00197   char* temp= as_charp (name_s);
00198 #ifdef OS_WIN32
00199   flag= _stat (temp, buf);
00200 #else
00201   flag= stat (temp, buf);
00202 #endif
00203   (void) link_flag;
00204   // FIXME: configure should test whether lstat works
00205   // flag= (link_flag? lstat (temp, buf): stat (temp, buf));
00206   tm_delete_array (temp);
00207   bench_cumul ("stat");
00208 
00209   // Cache stat results
00210   if (cache_flag) {
00211     if (flag) {
00212       if (do_cache_stat_fail (name_s))
00213        cache_set ("stat_cache.scm", name_s, "#f");
00214     }
00215     else {
00216       if (do_cache_stat (name_s))
00217        cache_set ("stat_cache.scm", name_s, as_string ((int) buf->st_mode));
00218     }
00219   }
00220   // End caching
00221 
00222   return flag;
00223 }
00224 
00225 bool
00226 is_of_type (url name, string filter) {
00227   if (filter == "") return true;
00228   int i, n= N(filter);
00229 
00230   // Files from the web
00231   if (is_rooted_web (name)) {
00232     // cout << "  try " << name << "\n";
00233     url from_web= get_from_web (name);
00234     // cout << "  --> " << from_web << "\n";
00235     if (is_none (from_web)) return false;
00236     for (i=0; i<n; i++)
00237       switch (filter[i]) {
00238       case 'd': return false;
00239       case 'l': return false;
00240       case 'w': return false;
00241       case 'x': return false;
00242       }
00243     return true;
00244   }
00245 
00246   // Files from a remote server
00247   if (is_rooted_tmfs (name)) {
00248     for (i=0; i<n; i++)
00249       switch (filter[i]) {
00250       case 'd': return false;
00251       case 'l': return false;
00252       case 'r':
00253        if (!as_bool (call ("tmfs-permission?", name, "read")))
00254          return false;
00255        break;
00256       case 'w':
00257        if (!as_bool (call ("tmfs-permission?", name, "write")))
00258          return false;
00259        break;
00260       case 'x': return false;
00261       }
00262     return true;
00263   }
00264 
00265   // Files from the ramdisk
00266   if (is_ramdisc (name))
00267     return true;
00268 
00269   // Normal files
00270 #if defined (OS_WIN32) || defined (__MINGW__) || defined (__MINGW32__)
00271   if ((filter == "x") && (suffix(name) != "exe") && (suffix(name) != "bat"))
00272     name = glue (name, ".exe");
00273 #endif
00274   bool preserve_links= false;
00275   for (i=0; i<n; i++)
00276     preserve_links= preserve_links || (filter[i] == 'l');
00277   struct stat buf;
00278   if (get_attributes (name, &buf, preserve_links)) return false;
00279   for (i=0; i<n; i++)
00280     switch (filter[i]) {
00281       // FIXME: should check user id and group id for r, w and x
00282     case 'f':
00283       if (!S_ISREG (buf.st_mode)) return false;
00284       break;
00285     case 'd':
00286       if (!S_ISDIR (buf.st_mode)) return false;
00287       break;
00288     case 'l':
00289 #ifdef __MINGW32__
00290       return false;
00291 #else
00292       if (!S_ISLNK (buf.st_mode)) return false;
00293 #endif
00294       break;
00295     case 'r':
00296 #ifndef __MINGW32__
00297       if ((buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) return false;
00298 #else
00299       if ((buf.st_mode & 292) == 0) return false;
00300 #endif
00301       break;
00302     case 'w':
00303 #ifndef __MINGW32__
00304       if ((buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) return false;
00305 #else
00306       if ((buf.st_mode & 146) == 0) return false;
00307 #endif
00308       break;
00309     case 'x':
00310 #if defined (OS_WIN32) || defined (__MINGW__) || defined (__MINGW32__)
00311       if (suffix(name) == "bat") break;
00312 #endif
00313 #ifndef __MINGW32__
00314       if ((buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return false;
00315 #else
00316       if ((buf.st_mode & 73) == 0) return false;
00317 #endif
00318       break;
00319     }
00320   return true;
00321 }
00322 
00323 bool is_regular (url name) { return is_of_type (name, "f"); }
00324 bool is_directory (url name) { return is_of_type (name, "d"); }
00325 bool is_symbolic_link (url name) { return is_of_type (name, "l"); }
00326 
00327 int
00328 last_modified (url u, bool cache_flag) {
00329   struct stat u_stat;
00330   if (get_attributes (u, &u_stat, true, cache_flag)) return 0;
00331   return u_stat.st_mtime;
00332 }
00333 
00334 bool
00335 is_newer (url which, url than) {
00336   struct stat which_stat;
00337   struct stat than_stat;
00338   // FIXME: why was this? 
00339   if (is_cached ("stat_cache.scm", concretize (which))) return false;
00340   if (is_cached ("stat_cache.scm", concretize (than))) return false;
00341   // end FIXME
00342   if (get_attributes (which, &which_stat, true)) return false;
00343   if (get_attributes (than , &than_stat , true)) return false;
00344   return which_stat.st_mtime > than_stat.st_mtime;
00345 }
00346 
00347 url
00348 url_temp (string suffix) {
00349 #ifdef __MINGW32__
00350   int rnd= raw_time ();
00351 #else
00352   static bool initialized= false;
00353   if (!initialized) {
00354     srandom ((int) raw_time ());
00355     initialized= true;
00356   }
00357   int rnd= random ();
00358 #endif
00359   string name= "tmp_" * as_string (rnd) * suffix;
00360   url u ("$TEXMACS_HOME_PATH/system/tmp", name);
00361   if (exists (u)) return url_temp (suffix);
00362   return u;
00363 }
00364 
00365 url
00366 url_numbered (url dir, string prefix, string postfix, int i) {
00367   if (!exists (dir)) mkdir (dir);
00368   for (; true; i++) {
00369     url name= dir * (prefix * as_string (i) * postfix);
00370     if (!exists (name)) return name;
00371   }
00372   return dir * (prefix * "x" * postfix);
00373 }
00374 
00375 url
00376 url_scratch (string prefix, string postfix, int i) {
00377   url dir ("$TEXMACS_HOME_PATH/texts/scratch");
00378   return url_numbered (dir, prefix, postfix, i);
00379 }
00380 
00381 bool
00382 is_scratch (url u) {
00383   return head (u) == url ("$TEXMACS_HOME_PATH/texts/scratch");
00384 }
00385 
00386 /******************************************************************************
00387 * Reading directories
00388 ******************************************************************************/
00389 
00390 static array<string>
00391 cache_dir_get (string dir) {
00392   tree t= cache_get ("dir_cache.scm", dir);
00393   array<string> a (N(t));
00394   for (int i=0; i<N(t); i++) a[i]= t[i]->label;
00395   return a;
00396 }
00397 
00398 static void
00399 cache_dir_set (string dir, array<string> a) {
00400   tree t (TUPLE, N(a));
00401   for (int i=0; i<N(a); i++) t[i]= a[i];
00402   cache_set ("dir_cache.scm", dir, t);
00403 }
00404 
00405 array<string>
00406 read_directory (url u, bool& error_flag) {
00407  //  cout << "Directory " << u << LF;
00408   u= resolve (u, "dr");
00409   if (is_none (u)) return array<string> ();
00410   string name= concretize (u);
00411 
00412   // Directory contents in cache?
00413   if (is_cached ("dir_cache.scm", name) && is_up_to_date (u))
00414     return cache_dir_get (name);
00415   bench_start ("read directory");
00416   // End caching
00417 
00418   DIR* dp;
00419   char* temp= as_charp (name);
00420   dp= opendir (temp);
00421   tm_delete_array (temp);
00422   error_flag= (dp==NULL);
00423   if (error_flag) return array<string> ();
00424 
00425   array<string> dir;
00426   struct dirent* ep;
00427   while (true) {
00428     ep= readdir (dp);
00429     if (ep==NULL) break;
00430     dir << string (ep->d_name);
00431   }
00432   (void) closedir (dp);
00433   merge_sort (dir);
00434 
00435   // Caching of directory contents
00436   bench_cumul ("read directory");
00437   if (do_cache_dir (name))
00438     cache_dir_set (name, dir);
00439   // End caching
00440 
00441   return dir;
00442 }
00443 
00444 /******************************************************************************
00445 * Searching text in the documentation
00446 ******************************************************************************/
00447 
00448 static array<int>
00449 search (string what, string in) {
00450   int i= 0, n= N(what);
00451   array<int> matches;
00452   if (n == 0) return matches;
00453   while (true) {
00454     int pos= search_forwards (what, i, in);
00455     if (pos == -1) return matches;
00456     matches << pos;
00457     i= pos+1;
00458   }
00459 }
00460 
00461 static bool
00462 precedes (string in, int pos, string what) {
00463   return pos >= N(what) && in (pos-N(what), pos) == what;
00464 }
00465 
00466 static int
00467 compute_score (string what, string in, int pos, string suf) {
00468   int score= 1;
00469   if (pos > 0 && !is_iso_alpha (in [pos-1]))
00470     if (pos + N(what) + 1 < N(in) && !is_iso_alpha (in [pos+N(what)]))
00471       score *= 10;
00472   if (suf == "tm") {
00473     if (precedes (in, pos, "<")) score= 0;
00474     else if (precedes (in, pos, "<\\")) score= 0;
00475     else if (precedes (in, pos, "<|")) score= 0;
00476     else if (precedes (in, pos, "</")) score= 0;
00477     else if (precedes (in, pos, "compound|")) score= 0;
00478     else if (precedes (in, pos, "<name|")) score *= 10;
00479     else if (precedes (in, pos, "<tmstyle|")) score *= 10;
00480     else if (precedes (in, pos, "<tmdtd|")) score *= 10;
00481     else if (precedes (in, pos, "<explain-macro|")) score *= 10;
00482     else if (precedes (in, pos, "<var-val|")) score *= 10;
00483   }
00484   else if (suf == "scm") {
00485     if (precedes (in, pos, "define ")) score *= 10;
00486     else if (precedes (in, pos, "define-public ")) score *= 10;
00487     else if (precedes (in, pos, "define (")) score *= 10;
00488     else if (precedes (in, pos, "define-public (")) score *= 10;
00489     else if (precedes (in, pos, "define-macro ")) score *= 10;
00490     else if (precedes (in, pos, "define-public-macro ")) score *= 10;
00491     else if (precedes (in, pos, "define-macro (")) score *= 10;
00492     else if (precedes (in, pos, "define-public-macro (")) score *= 10;
00493   }
00494   return score;
00495 }
00496 
00497 static int
00498 compute_score (string what, string in, array<int> pos, string suf) {
00499   int score= 0, i= 0, n= N(pos);
00500   for (i=0; i<n; i++)
00501     score += compute_score (what, in, pos[i], suf);
00502   return score;
00503 }
00504 
00505 int
00506 search_score (url u, array<string> a) {
00507   string in, suf= suffix (u);
00508   if (load_string (u, in, false)) return 0;
00509   in= locase_all (in);
00510   int i, score= 1, n= N(a);
00511   for (i=0; i<n; i++) {
00512     string what= locase_all (a[i]);
00513     array<int> pos= search (what, in);
00514     score *= compute_score (what, in, pos, suf);
00515     if (score == 0) return 0;
00516     if (score > 1000000) score= 1000000;
00517   }
00518   return score;
00519 }
00520 
00521 /******************************************************************************
00522 * Commands on files
00523 ******************************************************************************/
00524 
00525 void
00526 move (url u1, url u2) {
00527   char *_u1, *_u2;
00528   _u1 = as_charp (concretize (u1));
00529   _u2 = as_charp (concretize (u2));
00530   (void) rename (_u1, _u2);
00531   tm_delete_array (_u1);
00532   tm_delete_array (_u2);
00533 }
00534 
00535 void
00536 copy (url u1, url u2) {
00537   string s;
00538   if (!load_string (u1, s, false))
00539     (void) save_string (u2, s, false);
00540 }
00541 
00542 void
00543 remove (url u) {
00544   u= expand (complete (u));
00545   if (is_none (u));
00546   else if (is_or (u)) {
00547     remove (u[1]);
00548     remove (u[2]);
00549   }
00550   else {
00551     char *_u= as_charp (concretize (u));
00552     (void) ::remove (_u);
00553     tm_delete_array (_u);
00554   }
00555 }
00556 
00557 void
00558 mkdir (url u) {
00559 #if defined (HAVE_SYS_TYPES_H) && defined (HAVE_SYS_STAT_H)
00560   if (exists (u)) return;
00561   char *_u= as_charp (concretize (u));
00562 #if defined(__MINGW__) || defined(__MINGW32__)
00563   (void) ::mkdir (_u);
00564 #else
00565   (void) ::mkdir (_u, S_IRWXU + S_IRGRP + S_IROTH);
00566 #endif
00567   tm_delete_array (_u);
00568 #else
00569 #if defined(__MINGW__) || defined(__MINGW32__)
00570   system ("mkdir", u);
00571 #else
00572   system ("mkdir -p", u);
00573 #endif
00574 #endif
00575 }
00576 
00577 void
00578 change_mode (url u, int mode) {
00579 #if defined (HAVE_SYS_TYPES_H) && defined (HAVE_SYS_STAT_H)
00580   char *_u= as_charp (concretize (u));
00581   (void) ::chmod (_u, mode);
00582   tm_delete_array (_u);
00583 #else
00584   string m0= as_string ((mode >> 9) & 7);
00585   string m1= as_string ((mode >> 6) & 7);
00586   string m2= as_string ((mode >> 3) & 7);
00587   string m3= as_string (mode & 7);
00588   system ("chmod -f " * m0 * m1 * m2 * m3, u);
00589 #endif
00590 }
00591 
00592 void
00593 ps2pdf (url u1, url u2) {
00594 #ifdef OS_WIN32
00595   char *_u1, *_u2;
00596   _u1 = as_charp (concretize (u1));
00597   _u2 = as_charp (concretize (u2));
00598   XPs2Pdf (_u1, _u2);
00599   tm_delete_array (_u1);
00600   tm_delete_array (_u2);
00601 #else
00602 #ifdef MACOSX_EXTENSIONS
00603   mac_ps_to_pdf (u1, u2);
00604 #else
00605   system ("ps2pdf", u1, u2);
00606 #endif
00607 #endif
00608 }
00609 
00610 
00611 /******************************************************************************
00612  * Tab-completion for file names
00613  ******************************************************************************/
00614 
00615 #ifdef OS_WIN32
00616 #define URL_CONCATER  '\\'
00617 #else
00618 #define URL_CONCATER  '/'
00619 #endif
00620 
00621 static void
00622 file_completions_file (array<string>& a, url search, url u) {
00623   if (is_or (u)) {
00624     file_completions_file (a, search, u[1]);
00625     file_completions_file (a, search, u[2]);
00626   }
00627   else {
00628     url v= delta (search * url ("dummy"), u);
00629     if (is_none (v)) return;
00630     string s= as_string (v);
00631     if (is_directory (u)) s= s * string (URL_CONCATER);
00632     a << s;
00633   }
00634 }
00635 
00636 static void
00637 file_completions_dir (array<string>& a, url search, url dir) {
00638   if (is_or (search)) {
00639     file_completions_dir (a, search[1], dir);
00640     file_completions_dir (a, search[2], dir);
00641   }
00642   else if (is_or (dir)) {
00643     file_completions_dir (a, search, dir[1]);
00644     file_completions_dir (a, search, dir[2]);
00645   }
00646   else {
00647     url u= search * dir * url_wildcard ("*");
00648     u= complete (u, "r");
00649     u= expand (u);
00650     file_completions_file (a, search, u);
00651   }
00652 }
00653 
00654 array<string>
00655 file_completions (url search, url dir) {
00656   array<string> a;
00657   file_completions_dir (a, search, dir);
00658   return a;
00659 }
00660