1 
2 /******************************************************************************
3 * MODULE     : data_cache.cpp
4 * DESCRIPTION: utilities for caching data
5 * COPYRIGHT  : (C) 2005  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "data_cache.hpp"
13 #include "file.hpp"
14 #include "convert.hpp"
15 #include "iterator.hpp"
16 
17 /******************************************************************************
18 * Caching routines
19 ******************************************************************************/
20 
21 static hashmap<tree,tree> cache_data ("?");
22 static hashset<string> cache_loaded;
23 static hashset<string> cache_changed;
24 static hashmap<string,bool> cache_valid (false);
25 
26 void
cache_set(string buffer,tree key,tree t)27 cache_set (string buffer, tree key, tree t) {
28   tree ckey= tuple (buffer, key);
29   if (cache_data[ckey] != t) {
30     cache_data (ckey)= t;
31     cache_changed->insert (buffer);
32   }
33 }
34 
35 void
cache_reset(string buffer,tree key)36 cache_reset (string buffer, tree key) {
37   tree ckey= tuple (buffer, key);
38   cache_data->reset (ckey);
39   cache_changed->insert (buffer);
40 }
41 
42 bool
is_cached(string buffer,tree key)43 is_cached (string buffer, tree key) {
44   tree ckey= tuple (buffer, key);
45   return cache_data->contains (ckey);
46 }
47 
48 tree
cache_get(string buffer,tree key)49 cache_get (string buffer, tree key) {
50   tree ckey= tuple (buffer, key);
51   return cache_data [ckey];
52 }
53 
54 bool
is_up_to_date(url dir)55 is_up_to_date (url dir) {
56   string name_dir= concretize (dir);
57   if (cache_valid->contains (name_dir)) return cache_valid [name_dir];
58   int l= last_modified (dir, false);
59   if (is_cached ("validate_cache.scm", name_dir)) {
60     int r= as_int (cache_get ("validate_cache.scm", name_dir) -> label);
61     if (l == r) {
62       cache_valid (name_dir)= true;
63       return true;
64     }
65     //cout << name_dir << " no longer up to date " << r << " -> " << l << "\n";
66   }
67   //else cout << name_dir << " not up to date " << l << "\n";
68   cache_set ("validate_cache.scm", name_dir, as_string (l));
69   cache_valid (name_dir)= false;
70   // FIXME: we should explicitly remove all data concerning files in 'dir'
71   // from the various caches.  Indeed, at a next run of TeXmacs, the directory
72   // will be regarded as up to date, but the other caches may still contain
73   // outdated data.  Careful: invalidating the cache lines should not
74   // give rise to a performance penaly (e.g. go through all entries of
75   // 'cache_data', or reloading unchanged files many times).
76   // See also 'declare_out_of_date'.
77   return false;
78 }
79 
80 bool
is_recursively_up_to_date(url dir)81 is_recursively_up_to_date (url dir) {
82   if (!is_up_to_date (dir)) return false;
83   bool error_flag;
84   array<string> a= read_directory (dir, error_flag);
85   for (int i=0; i<N(a); i++)
86     if (url (a[i]) != url_here () && url (a[i]) != url_parent ())
87       if (N(a[i])>0 && a[i][0] != '.')
88         if (is_directory (dir * a[i]))
89           if (!is_recursively_up_to_date (dir * a[i]))
90             return false;
91   return true;
92 }
93 
94 void
declare_out_of_date(url dir)95 declare_out_of_date (url dir) {
96   //cout << "out of date: " << dir << "\n";
97   string name_dir= concretize (dir);
98   int l= last_modified (dir, false);
99   cache_set ("validate_cache.scm", name_dir, as_string (l));
100   cache_valid (name_dir)= false;
101   // FIXME: see 'FIXME' in 'is_up_to_date'.
102 }
103 
104 /******************************************************************************
105 * Which files should be stored in the cache?
106 ******************************************************************************/
107 
108 static url texmacs_path (url_none ());
109 static url texmacs_doc_path (url_none ());
110 static url texmacs_home_path (url_none ());
111 
112 static string texmacs_path_string;
113 static string texmacs_doc_path_string;
114 static string texmacs_home_path_string;
115 static string texmacs_font_path_string;
116 
117 bool
do_cache_dir(string name)118 do_cache_dir (string name) {
119   return
120     starts (name, texmacs_path_string) ||
121     starts (name, texmacs_doc_path_string);
122 }
123 
124 bool
do_cache_stat(string name)125 do_cache_stat (string name) {
126   return
127     starts (name, texmacs_path_string) ||
128     starts (name, texmacs_font_path_string) ||
129     starts (name, texmacs_doc_path_string);
130 }
131 
132 bool
do_cache_stat_fail(string name)133 do_cache_stat_fail (string name) {
134   return
135     !ends (name, ".ts") &&
136     (starts (name, texmacs_path_string) ||
137      starts (name, texmacs_doc_path_string));
138 }
139 
140 bool
do_cache_file(string name)141 do_cache_file (string name) {
142   return
143     !ends (name, ".ts") &&
144     (starts (name, texmacs_path_string) ||
145      starts (name, texmacs_font_path_string));
146 }
147 
148 bool
do_cache_doc(string name)149 do_cache_doc (string name) {
150   return starts (name, texmacs_doc_path_string);
151 }
152 
153 /******************************************************************************
154 * Saving and loading the cache to/from disk
155 ******************************************************************************/
156 
157 void
cache_save(string buffer)158 cache_save (string buffer) {
159   if (cache_changed->contains (buffer)) {
160     url cache_file= texmacs_home_path * url ("system/cache/" * buffer);
161     string cached;
162     iterator<tree> it= iterate (cache_data);
163     if (buffer == "file_cache" || buffer == "doc_cache") {
164       while (it->busy ()) {
165 	tree ckey= it->next ();
166 	if (ckey[0] == buffer) {
167 	  cached << ckey[1]->label << "\n";
168 	  cached << cache_data [ckey]->label << "\n";
169 	  cached << "%-%-tm-cache-%-%\n";
170 	}
171       }
172     }
173     else {
174       cached << "(tuple\n";
175       while (it->busy ()) {
176 	tree ckey= it->next ();
177 	if (ckey[0] == buffer) {
178 	  cached << tree_to_scheme (ckey[1]) << " ";
179 	  cached << tree_to_scheme (cache_data [ckey]) << "\n";
180 	}
181       }
182       cached << ")";
183     }
184     (void) save_string (cache_file, cached);
185     cache_changed->remove (buffer);
186   }
187 }
188 
189 void
cache_load(string buffer)190 cache_load (string buffer) {
191   if (!cache_loaded->contains (buffer)) {
192     url cache_file = texmacs_home_path * url ("system/cache/" * buffer);
193     //cout << "cache_file "<< cache_file << LF;
194     string cached;
195     if (!load_string (cache_file, cached, false)) {
196       if (buffer == "file_cache" || buffer == "doc_cache") {
197 	int i=0, n= N(cached);
198 	while (i<n) {
199 	  int start= i;
200 	  while (i<n && cached[i] != '\n') i++;
201 	  string key= cached (start, i);
202 	  i++; start= i;
203 	  while (i<n && (cached[i] != '\n' ||
204 			 !test (cached, i+1, "%-%-tm-cache-%-%"))) i++;
205 	  string im= cached (start, i);
206 	  i++;
207 	  while (i<n && cached[i] != '\n') i++;
208 	  i++;
209 	  //cout << "key= " << key << "\n----------------------\n";
210 	  //cout << "im= " << im << "\n----------------------\n";
211 	  cache_data (tuple (buffer, key))= im;
212 	}
213       }
214       else {
215 	tree t= scheme_to_tree (cached);
216 	for (int i=0; i<N(t)-1; i+=2)
217 	  cache_data (tuple (buffer, t[i]))= t[i+1];
218       }
219     }
220     cache_loaded->insert (buffer);
221   }
222 }
223 
224 void
cache_memorize()225 cache_memorize () {
226   cache_save ("file_cache");
227   cache_save ("doc_cache");
228   cache_save ("dir_cache.scm");
229   cache_save ("stat_cache.scm");
230   cache_save ("font_cache.scm");
231   cache_save ("validate_cache.scm");
232 }
233 
234 void
cache_refresh()235 cache_refresh () {
236   cache_data   = hashmap<tree,tree> ("?");
237   cache_loaded = hashset<string> ();
238   cache_changed= hashset<string> ();
239   cache_load ("file_cache");
240   cache_load ("dir_cache.scm");
241   cache_load ("stat_cache.scm");
242   cache_load ("font_cache.scm");
243   cache_load ("validate_cache.scm");
244 }
245 
246 void
cache_initialize()247 cache_initialize () {
248   texmacs_path= url_system ("$TEXMACS_PATH");
249   if (get_env ("TEXMACS_HOME_PATH") == "")
250     texmacs_home_path= url_system ("$HOME/.TeXmacs");
251   else texmacs_home_path= url_system ("$TEXMACS_HOME_PATH");
252   if (get_env ("TEXMACS_DOC_PATH") == "")
253     texmacs_doc_path= url_system ("$TEXMACS_PATH/doc");
254   else texmacs_doc_path= url_system ("$TEXMACS_DOC_PATH");
255 
256   texmacs_path_string = concretize (texmacs_path);
257   texmacs_home_path_string = concretize (texmacs_home_path);
258   texmacs_doc_path_string = concretize (texmacs_doc_path);
259   texmacs_font_path_string = concretize (texmacs_home_path * "fonts/");
260 
261   cache_refresh ();
262   if (is_recursively_up_to_date (texmacs_path * "fonts/type1") &&
263       is_recursively_up_to_date (texmacs_path * "fonts/truetype") &&
264       is_recursively_up_to_date (texmacs_home_path * "fonts/type1") &&
265       is_recursively_up_to_date (texmacs_home_path * "fonts/truetype"));
266   else remove (texmacs_home_path * "fonts/error" * url_wildcard ("*"));
267 }
268