1 /*
2 * Copyright (C) 2002-2012 Edscott Wilson Garcia
3 * EMail: edscott@users.sf.net
4 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program;
18 */
19
20 #define RFM_PRIMARY_C
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include "rfm.h"
27 #include "rfm_modules.h"
28
29 // static functions:
30 #include "primary.i"
31 #include "primary-ls.i"
32
33 gchar *
rfm_default_url_mount_point(const gchar * url)34 rfm_default_url_mount_point(const gchar *url){
35 gchar *computer=NULL;
36 gchar *remote_path=NULL;
37 gchar *u = NULL;
38 if (url){
39 u = g_strdup(url);
40 gchar *p = strstr(u, "://");
41 if (p) {
42 p = p+strlen("://");
43 if (strchr(p,'/')){
44 *strchr(p,'/') = 0;
45 computer = g_strdup(p);
46 remote_path = g_strdup_printf("/%s",p + strlen(p) + 1);
47 }
48 }
49 g_free(u);
50 }
51
52 gchar *user = g_path_get_basename(g_get_home_dir ());
53 gchar *dir;
54 gchar *device = NULL;
55 if (remote_path) {
56 device = g_path_is_absolute(remote_path)? remote_path+1: remote_path;
57 }
58 if (computer && remote_path) {
59 dir = g_strdup_printf("%s-%s", computer, device);
60 } else {
61 dir = g_strdup((computer)? computer : device);
62 }
63 gchar *default_value =
64 g_build_filename (g_get_tmp_dir (), user, "mnt", dir, NULL);
65 g_free(user);
66 g_free(dir);
67 g_free(computer);
68 g_free(remote_path);
69 return default_value;
70
71 }
72
73 static GSList *valid_view_list = NULL;
74 static pthread_rwlock_t valid_view_rwlock;
75 static pthread_once_t valid_view_once_control = PTHREAD_ONCE_INIT;
76 static pthread_mutex_t view_list_mutex = PTHREAD_MUTEX_INITIALIZER;
77
78 static
valid_view_init(void)79 void valid_view_init(void){
80 pthread_rwlockattr_t Attr;
81 pthread_rwlockattr_init(&Attr);
82 pthread_rwlock_init(&valid_view_rwlock, &Attr);
83 }
84
85
86 #if defined DEBUG_TRACE || defined DEBUG
87 static gint mutex_count = 0;
88 #endif
89
90 void
rfm_view_list_unlock(const gchar * source)91 rfm_view_list_unlock(const gchar *source){
92 NOOP("- %s -- unlocking view_list now... %d\n",source, --mutex_count);
93 #if defined DEBUG || defined DEBUG_TRACE
94 if (mutex_count < 0) g_error("unlocking non locked RWlock\n");
95 #endif
96 pthread_rwlock_unlock(&valid_view_rwlock);
97
98 }
99
100 static GSList **
view_list_lock(view_t * view_p,gboolean w,const gchar * source)101 view_list_lock(view_t *view_p, gboolean w, const gchar *source){
102 NOOP("+++ attempting %s lock for view_list... (%d)\n",
103 (w)?"write":"read", mutex_count);
104
105 pthread_once(&valid_view_once_control, valid_view_init);
106 if (w) {
107 pthread_rwlock_wrlock(&valid_view_rwlock);
108 } else {
109 pthread_rwlock_rdlock(&valid_view_rwlock);
110 }
111 NOOP("+ %s ++ locking view_list now (%s)... %d\n", source,
112 (w)?"write":"read", ++mutex_count);
113 if (!view_p) return &valid_view_list;
114
115 pthread_mutex_lock(&view_list_mutex);
116 void *data = g_slist_find(valid_view_list, view_p);
117 pthread_mutex_unlock(&view_list_mutex);
118
119 if (data){
120 return &valid_view_list;
121 }
122 NOOP("view %p not found in list\n", view_p);
123 rfm_view_list_unlock("view_list_lock");
124 return NULL;
125 }
126
127 GSList **
rfm_view_list_lock(view_t * view_p,const gchar * source)128 rfm_view_list_lock(view_t *view_p, const gchar *source){
129 GSList **p=view_list_lock(view_p, FALSE, source);
130 return p;
131 }
132
133
134 void
rfm_add_view(view_t * view_p)135 rfm_add_view(view_t *view_p){
136 TRACE("rfm_add_view()...\n");
137 // write lock on valid views.
138 view_list_lock(NULL, FALSE, "rfm_add_view");
139 pthread_mutex_lock(&view_list_mutex);
140 void *data = g_slist_find(valid_view_list, view_p);
141 pthread_mutex_unlock(&view_list_mutex);
142 if (data){
143 DBG("view %p already in viewlist\n", view_p);
144 rfm_view_list_unlock("rfm_add_view");
145 return;
146 }
147 TRACE("adding view %p to list\n", view_p);
148 pthread_mutex_lock(&view_list_mutex);
149 valid_view_list = g_slist_prepend(valid_view_list, view_p);
150 pthread_mutex_unlock(&view_list_mutex);
151 rfm_view_list_unlock("rfm_add_view");
152 return;
153 }
154
155 void
rfm_rm_view(view_t * view_p)156 rfm_rm_view(view_t *view_p){
157 TRACE("rfm_rm_view()...\n");
158 // write lock on valid views.
159 view_list_lock(NULL, TRUE, "rfm_rm_view");
160
161 pthread_mutex_lock(&view_list_mutex);
162 void *data = g_slist_find(valid_view_list, view_p);
163 pthread_mutex_unlock(&view_list_mutex);
164
165 if (data){
166 pthread_mutex_lock(&view_list_mutex);
167 valid_view_list = g_slist_remove(valid_view_list, view_p);
168 pthread_mutex_unlock(&view_list_mutex);
169 rfm_view_list_unlock("rfm_rm_view");
170 return;
171 }
172 rfm_view_list_unlock("rfm_rm_view");
173 DBG("view %p not in viewlist\n", view_p);
174 return;
175 }
176
177
178
179 extern GThread *main_loop_thread;
180
181
182 static gboolean
main_context_function_f(gpointer data)183 main_context_function_f(gpointer data){
184 void **arg = data;
185 void * (*function)(gpointer) = arg[0];
186 gpointer function_data = arg[1];
187 GMutex *mutex = arg[2];
188 GCond *signal = arg[3];
189 void **result_p = arg[4];
190 void *result = (*function)(function_data);
191 g_mutex_lock(mutex);
192 *result_p = result;
193 g_cond_signal(signal);
194 g_mutex_unlock(mutex);
195 return FALSE;
196 }
197
198 void *
rfm_context_function(void * (* function)(gpointer),void * function_data)199 rfm_context_function(void * (*function)(gpointer), void * function_data){
200 void *arg[5];
201 GMutex *mutex = NULL;
202 rfm_mutex_init(mutex);
203 GCond *signal;
204 rfm_cond_init(signal);
205 void *result=GINT_TO_POINTER(-1);
206 arg[0] = function;
207 arg[1] = function_data;
208 arg[2] = mutex;
209 arg[3] = signal;
210 arg[4] = &result;
211
212 if (!rfm_get_gtk_thread()){
213 g_warning("gtk_main_thread not set. You can expect problems.\n");
214 }
215 if (rfm_get_gtk_thread()== g_thread_self()){
216 NOOP("main call to context function\n");
217 main_context_function_f(arg);
218 } else {
219 NOOP("thread call to context function\n");
220 g_main_context_invoke(NULL, main_context_function_f, arg);
221 g_mutex_lock(mutex);
222 if (result == GINT_TO_POINTER(-1)) g_cond_wait(signal, mutex);
223 g_mutex_unlock(mutex);
224 }
225 rfm_mutex_free(mutex);
226 rfm_cond_free(signal);
227 return result;
228 }
229
230 gboolean
rfm_main_context_check(view_t * view_p,gboolean view_entry)231 rfm_main_context_check(view_t *view_p, gboolean view_entry){
232 // Check if window is not in exit mode
233 rfm_global_t *rfm_global_p = rfm_global();
234 g_mutex_lock(rfm_global_p->status_mutex);
235 gint status = rfm_global_p->status;
236 g_mutex_unlock(rfm_global_p->status_mutex);
237 if(status == STATUS_EXIT) return FALSE;
238 if (!view_p) return TRUE;
239
240 // Check if view is still listed
241 TRACE("rfm_main_context_check()...\n");
242 if (!rfm_view_list_lock(view_p, "rfm_main_context_check")) return FALSE;
243
244 // Check if view is not in exit mode
245 status = view_p->flags.status;
246 if(status == STATUS_EXIT) goto done;
247
248 // Check if view->en is still valid (move callbacks.i function to rfm lib)
249 if (view_entry &&
250 !rfm_entry_available(&(view_p->widgets), view_p->en)){
251 goto done;
252 }
253 rfm_view_list_unlock("rfm_main_context_check1");
254 return TRUE;
255 done:
256 rfm_view_list_unlock("rfm_main_context_check2");
257 return FALSE;
258 }
259
260 RFM_RW_LOCK_INIT(drag_info_lock);
261
262 //#define LOCK_TRACE
263
264 #ifdef LOCK_TRACE
265 /*static GHashTable *thread_hash = NULL;
266 static GHashTable *lock_ref_hash = NULL;*/
267 static gint pop_lock_count = 0;
268
269 /*const gchar *
270 get_thread_text(GThread *thread){
271 if (rfm_get_gtk_thread() == thread) return "main thread";
272 gchar *text = g_hash_table_lookup(thread_hash, thread);
273 return (const gchar *)text;
274 }*/
275
276
277 /*gint thread_has_lock(GThread *thread){
278 return GPOINTER_TO_INT(g_hash_table_lookup(lock_ref_hash, thread));
279 }*/
280
inc_RW(gboolean increment,const gchar * source)281 static void inc_RW(gboolean increment, const gchar *source){
282 static pthread_mutex_t lock_ref_mutex=PTHREAD_MUTEX_INITIALIZER;
283 pthread_mutex_lock(&lock_ref_mutex);
284 /*if (!lock_ref_hash){
285 lock_ref_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
286 }*/
287 //gint count = GPOINTER_TO_INT(g_hash_table_lookup(lock_ref_hash, g_thread_self()));
288 // These two commented out TRACE instructions will really vomit output
289 // and turn application into a snail. Use with care.
290 if (increment) {
291 //count++;
292 pop_lock_count++;
293 //if (get_thread_text(g_thread_self()))
294 DBG("lock...%s: %d\n", source, pop_lock_count);
295 //DBG("lock...%s\n", get_thread_text(g_thread_self()));
296 } else {
297 //count--;
298 pop_lock_count--;
299 //if (get_thread_text(g_thread_self()))
300 DBG("unlock...%s: %d\n", source, pop_lock_count);
301 //DBG("unlock...%s\n", get_thread_text(g_thread_self()));
302 }
303 //g_hash_table_replace(lock_ref_hash, g_thread_self(), GINT_TO_POINTER(count));
304 pthread_mutex_unlock(&lock_ref_mutex);
305 }
306 #endif
307
308 //#define FILTER if (!strstr(source, "rect"))
309 //#define FILTER if (0)
310 #define FILTER if (0)
311 void
rfm_population_read_unlock(view_t * view_p,const gchar * source)312 rfm_population_read_unlock (view_t * view_p, const gchar *source) {
313 FILTER DBG("--- rfm_population_read_unlock() from %s\n", source);
314 rfm_rw_lock_reader_unlock(&(view_p->mutexes.population_rwlock));
315 #ifdef LOCK_TRACE
316 inc_RW(FALSE, source);
317 #endif
318 }
319 void
rfm_population_write_unlock(view_t * view_p,const gchar * source)320 rfm_population_write_unlock (view_t * view_p, const gchar *source) {
321 FILTER DBG("--- rfm_population_write_unlock() from %s\n", source);
322 rfm_rw_lock_writer_unlock(&(view_p->mutexes.population_rwlock));
323 #ifdef LOCK_TRACE
324 inc_RW(FALSE, source);
325 #endif
326 }
327
328
329 gboolean
rfm_population_write_lock(view_t * view_p,const gchar * source)330 rfm_population_write_lock (view_t * view_p, const gchar *source) {
331
332 // This should only return false on serial mismatch.
333 // Write lock on exit condition is necessary for view cleanup
334 gint population_serial = view_p->flags.population_serial;
335
336
337 FILTER DBG("... rfm_population_write_lock() request from %s\n", source);
338 rfm_rw_lock_writer_lock(&(view_p->mutexes.population_rwlock));
339
340 FILTER DBG("+++ rfm_population_write_lock() obtained for %s\n", source);
341 gint obtained_serial = view_p->flags.population_serial;
342 if (obtained_serial != population_serial) {
343 rfm_rw_lock_writer_unlock(&(view_p->mutexes.population_rwlock));
344 return FALSE;
345 }
346 #ifdef LOCK_TRACE
347 inc_RW(TRUE, source);
348 #endif
349 return TRUE;
350 }
351
352
353 gboolean
rfm_population_read_lock(view_t * view_p,const gchar * source)354 rfm_population_read_lock (view_t * view_p, const gchar *source) {
355 gboolean status = view_p->flags.status;
356 if (status == STATUS_EXIT){
357 TRACE( "rfm_population_read_lock STATUS_EXIT\n");
358 return FALSE;
359 }
360 gint population_serial = view_p->flags.population_serial;
361
362 NOOP("... rfm_population_read_lock() request from %s\n", source);
363 rfm_rw_lock_reader_lock(&(view_p->mutexes.population_rwlock));
364 FILTER DBG("ooo rfm_population_reader_lock() obtained for %s\n", source);
365 gint obtained_serial = view_p->flags.population_serial;
366 if (obtained_serial != population_serial) {
367 DBG( "population serial mismatch: %d != %d read lock cancelled.\n",
368 obtained_serial, population_serial);
369 rfm_rw_lock_reader_unlock(&(view_p->mutexes.population_rwlock));
370 return FALSE;
371 }
372 #ifdef LOCK_TRACE
373 inc_RW(TRUE, source);
374 #endif
375
376 return TRUE;
377 }
378
379 gboolean
rfm_population_try_read_lock(view_t * view_p,const gchar * source)380 rfm_population_try_read_lock (view_t * view_p, const gchar *source) {
381 gboolean status = view_p->flags.status;
382 if (status == STATUS_EXIT) return FALSE;
383 gint population_serial = view_p->flags.population_serial;
384
385 NOOP("... rfm_population_try_read_lock() request from %s\n", source);
386 gboolean result = rfm_rw_lock_reader_trylock(&(view_p->mutexes.population_rwlock));
387
388 if (!result){
389 FILTER DBG("~~~ rfm_population_try_reader_lock() NOT obtained for %s\n",
390 source);
391 return FALSE;
392 }
393
394 FILTER DBG("ooo rfm_population_try_reader_lock() obtained for %s\n", source);
395
396 gint obtained_serial = view_p->flags.population_serial;
397 if (obtained_serial != population_serial) {
398 rfm_rw_lock_reader_unlock(&(view_p->mutexes.population_rwlock));
399 return FALSE;
400 }
401 #ifdef LOCK_TRACE
402 inc_RW(TRUE, source);
403 #endif
404
405 return TRUE;
406 }
407 //#undef LOCK_TRACE
408
409 static gint thread_count = 0;
410
411 static void
inc_dec_view_ref(view_t * view_p,gboolean increment)412 inc_dec_view_ref(view_t *view_p, gboolean increment){
413 RfmRWLock *lock;
414 rfm_global_t *rfm_global_p = rfm_global();
415 if (view_p) {
416 lock = &(view_p->mutexes.view_lock);
417 } else {
418 if (!rfm_global_p) {
419 TRACE("increment/decrement view_ref(): invalid view_p (rfm_global_p==NULL)\n");
420 return;
421 }
422 NOOP( "*+*+ global window lock\n");
423 lock = &(rfm_global_p->window_lock);
424 }
425 // XXX since this is main thread business (as well
426 // as child thread), reader lock may block...
427 // but this is very rare condition.
428 if (increment){
429 if (rfm_get_gtk_thread() == g_thread_self()){
430 while (!rfm_rw_lock_reader_trylock(lock)) gtk_main_iteration();
431 } else rfm_rw_lock_reader_lock(lock);
432 }
433 else rfm_rw_lock_reader_unlock(lock);
434 static pthread_mutex_t inc_dec_mutex=PTHREAD_MUTEX_INITIALIZER;
435
436 pthread_mutex_lock(&inc_dec_mutex);
437 if (increment) {
438 if (view_p) view_p->flags.thread_count++;
439 thread_count++;
440 } else {
441 if (view_p) view_p->flags.thread_count--;
442 thread_count--;
443 }
444 gint view_count = 1;
445 if (view_p) view_count = view_p->flags.thread_count;
446 if (thread_count == 0 || view_count == 0){
447 // Janitor signal (This wakes up the janitor).
448 TRACE("thread unreference sending janitor the signal. (%d,%d)\n",
449 thread_count, view_count);
450 g_cond_signal(rfm_global_p->janitor_signal);
451 }
452 pthread_mutex_unlock(&inc_dec_mutex);
453 }
454
455 static void *
wait_f(gpointer data)456 wait_f(gpointer data){
457 void **argv =data;
458 view_t *view_p = argv[0];
459 GThread *thread = argv[1];
460 g_free(argv);
461 g_thread_join(thread);
462 rfm_thread_unreference(view_p, thread);
463 return NULL;
464 }
465
466 #ifdef DEBUG_TRACE
467 #define DEBUG_THREADS
468 #endif
469
470 #ifdef DEBUG_THREADS
471 static GSList *view_ref_list = NULL;
472 static pthread_mutex_t *
get_view_ref_mutex(void)473 get_view_ref_mutex(void){
474 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
475 return &mutex;
476 }
477
478 void
rfm_thread_unreference_quiet(view_t * view_p,GThread * thread)479 rfm_thread_unreference_quiet(view_t *view_p, GThread *thread){
480 if (thread) inc_dec_view_ref(view_p, FALSE);
481 }
482 #else
483 void
rfm_thread_unreference_quiet(view_t * view_p,GThread * thread)484 rfm_thread_unreference_quiet(view_t *view_p, GThread *thread){
485 return rfm_thread_unreference(view_p, thread);
486 }
487 #endif
488
489
490 void
rfm_thread_unreference(view_t * view_p,GThread * thread)491 rfm_thread_unreference(view_t *view_p, GThread *thread){
492 if (thread) inc_dec_view_ref(view_p, FALSE);
493 #ifdef DEBUG_THREADS
494 pthread_mutex_t *view_ref_mutex=get_view_ref_mutex();
495 pthread_mutex_lock(view_ref_mutex);
496 const gchar *removed_text = get_thread_text(thread);
497
498 TRACE( "- view decrement: 0x%x (%s) view ref = %d\n",
499 GPOINTER_TO_INT(thread), removed_text,
500 g_slist_length(view_ref_list)-1);
501
502 view_ref_list = g_slist_remove(view_ref_list, thread);
503 g_hash_table_remove(thread_hash, thread);
504
505 #ifdef DEBUG_TRACE
506 gchar *trace_text = NULL;
507
508 GSList *t = view_ref_list;
509 if (g_slist_length(view_ref_list)) for(;t && t->data; t=t->next){
510 gchar *dbg_text = g_hash_table_lookup(thread_hash, t->data);
511 if (dbg_text) {
512 gchar *g =
513 g_strdup_printf("%s 0x%x (%s)",
514 trace_text?trace_text:"- decrement pending:",
515 GPOINTER_TO_INT(t->data), dbg_text);
516 g_free(trace_text);
517 trace_text = g;
518 }
519 } else {
520 trace_text = g_strdup("- view decrement: no threads pending");
521 }
522 if (trace_text) {
523 TRACE( "%s (%d)\n", trace_text, g_slist_length(view_ref_list));
524 g_free(trace_text);
525 }
526 #endif
527 pthread_mutex_unlock(view_ref_mutex);
528
529 #endif
530 }
531
532 void
rfm_thread_reference(view_t * view_p,GThread * thread,const gchar * dbg_text)533 rfm_thread_reference(view_t *view_p, GThread *thread, const gchar *dbg_text){
534 if (thread) inc_dec_view_ref(view_p, TRUE);
535 #ifdef DEBUG_THREADS
536 if (dbg_text){
537 TRACE( "- view increment: 0x%x:0x%x (%s) view ref = %d\n",
538 GPOINTER_TO_INT(view_p), GPOINTER_TO_INT(thread), dbg_text, g_slist_length(view_ref_list)+1);
539 pthread_mutex_t *view_ref_mutex=get_view_ref_mutex();
540 pthread_mutex_lock(view_ref_mutex);
541 if (!thread_hash){
542 thread_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
543 }
544 view_ref_list = g_slist_append(view_ref_list, thread);
545 g_hash_table_replace(thread_hash, thread, (void *)dbg_text);
546 pthread_mutex_unlock(view_ref_mutex);
547 }
548 #endif
549 }
550
551 // If view is NULL, the lock is on the window, not any particular view.
552 GThread *
rfm_view_thread_create(view_t * view_p,gpointer (* thread_f)(gpointer),gpointer data,const gchar * dbg_text)553 rfm_view_thread_create(
554 view_t *view_p,
555 gpointer(*thread_f)(gpointer), gpointer data,
556 const gchar *dbg_text){
557 rfm_global_t *rfm_global_p = rfm_global();
558
559 if (rfm_global_p){
560 g_mutex_lock(rfm_global_p->status_mutex);
561 gint status = rfm_global_p->status;
562 g_mutex_unlock(rfm_global_p->status_mutex);
563 if (status == STATUS_EXIT) {
564 DBG("rfm_view_thread_create: rfm_global_p->status == STATUS_EXIT\n");
565 return NULL;
566 }
567 }
568
569 if (view_p){
570 TRACE("rfm_view_thread_create()...\n");
571 if (!rfm_view_list_lock(view_p, "rfm_view_thread_create")) {
572 return NULL;
573 }
574 gint status = view_p->flags.status;
575 if (status == STATUS_EXIT) {
576 rfm_view_list_unlock("rfm_view_thread_create");
577 return NULL;
578 }
579 }
580
581 #ifdef DEBUG_TRACE
582 if (dbg_text){
583 TRACE("Thread: %s\n", dbg_text);
584 }
585 #endif
586 GThread *thread = rfm_thread_create (dbg_text, thread_f, data, TRUE);
587 if (thread){
588 rfm_thread_reference(view_p, thread, dbg_text);
589 void **arg = (void **)malloc(2*sizeof(void *));
590 arg[0] = view_p;
591 arg[1] = thread;
592 rfm_thread_create ("wait_f", wait_f, arg, FALSE);
593 }
594 if (view_p) rfm_view_list_unlock("rfm_view_thread_create");
595 return thread;
596 }
597
598 ///////////////////////////////////////////////////////////////////
599 // Main thread threadpool queue creation
600
601 GThreadPool *
rfm_thread_queue_new(void (* signal_pool_f)(void *,void *),void * pool_data,gint max_threads)602 rfm_thread_queue_new(void (*signal_pool_f)(void *, void *), void *pool_data, gint max_threads){
603 GThreadPool *thread_pool;
604 GError *error = NULL;
605 thread_pool = g_thread_pool_new (signal_pool_f,
606 pool_data, max_threads, TRUE, &error);
607 if (error){
608 g_error("g_thread_pool_new: %s\n", error->message);
609 }
610 // g_thread_pool_set_max_idle_time (miliseconds);
611 return thread_pool;
612 }
613
614 gboolean
rfm_threadqueue_push(GThreadPool * thread_pool,gint signal_enum,view_t * view_p,void * data)615 rfm_threadqueue_push(GThreadPool *thread_pool, gint signal_enum, view_t *view_p, void *data){
616 rfm_global_t *rfm_global_p = rfm_global();
617 if (!thread_pool) {
618 DBG("rfm_global_p->thread_queue not yet initialized.\n");
619 return FALSE;
620 }
621 g_mutex_lock(rfm_global_p->status_mutex);
622 gint status = rfm_global_p->status;
623 g_mutex_unlock(rfm_global_p->status_mutex);
624 if (status == STATUS_EXIT) return FALSE;
625
626 void **arg = (void **)malloc(3*sizeof(void *));
627 if (!arg) g_error("malloc: %s\n", strerror(errno));
628 arg[0] = GINT_TO_POINTER(signal_enum);
629 arg[1] = view_p;
630 arg[2] = data;
631
632 NOOP(stderr, "rfm_threadqueue_push: g_thread_pool_push, signal %d\n", signal_enum);
633 g_thread_pool_push(thread_pool, arg, NULL);
634 return TRUE;
635 }
636
637 #define PASTE_SHM_NAME paste_shm_name()
638
639 static const gchar*
paste_shm_name(void)640 paste_shm_name(void){
641 static const gchar *name=NULL;
642 if (!name){
643 name = g_strdup_printf("/%d-rfm-pasteboard", (gint)geteuid());
644 }
645 return name;
646 }
647
648 gchar *
rfm_get_hash_key(const gchar * key,gint size)649 rfm_get_hash_key (const gchar * key, gint size) {
650 gchar *hash_key = NULL;
651 GString *gs = g_string_new (key);
652 if (size <=0) {
653 hash_key = g_strdup_printf ("%010u", g_string_hash (gs));
654 } else {
655 gint usize = 999;
656 if (size <= 999) usize = size;
657 hash_key = g_strdup_printf ("%010u-%d", g_string_hash (gs), usize);
658 }
659 g_string_free (gs, TRUE);
660 NOOP("%s: hashkey=%s\n", key, hash_key);
661 return hash_key;
662 }
663
664
665 void
rfm_set_widget(void * widget,const gchar * name)666 rfm_set_widget (void *widget, const gchar * name){
667 if(!name) g_error ("rfm_set_widget invalid call");
668 rfm_global_t *rfm_global_p = rfm_global();
669 if (!rfm_global_p || !rfm_global_p->window){
670 NOOP("rfm_set_widget invalid parameters\n");
671 return;
672 }
673 g_object_set_data (G_OBJECT (rfm_global_p->window), name, widget);
674 NOOP(stderr, "set %s: 0x%x\n", name, GPOINTER_TO_INT(widget));
675 return;
676 }
677
678 void *
rfm_get_widget(const gchar * name)679 rfm_get_widget (const gchar * name) {
680 if(!name) {
681 DBG ("rfm_get_widget: !name\n");
682 return NULL;
683 }
684 rfm_global_t *rfm_global_p = rfm_global();
685 if (!rfm_global_p || !rfm_global_p->window){
686 g_error("invalid call to rfm_get_widget: %s\n", name);
687 }
688 void *pointer = g_object_get_data (G_OBJECT (rfm_global_p->window), name);
689 if(!pointer) {
690 // This may be not set yet, and may not indicate any error.
691 NOOP("Cannot find widget (or pointer) associated to \"%s\"\n", name);
692 TRACE("Cannot find widget (or pointer) associated to \"%s\"\n", name);
693 }
694 return pointer;
695
696 }
697
698 gchar *
rfm_esc_string(const gchar * string)699 rfm_esc_string (const gchar * string) {
700 int i,
701 j,
702 k;
703 char *charset = "\\\"\' ()|<>";
704
705 for(j = 0, i = 0; i < strlen (string); i++) {
706 for(k = 0; k < strlen (charset); k++) {
707 if(string[i] == charset[k])
708 j++;
709 }
710 }
711 gchar *estring = (gchar *) malloc (strlen (string) + j + 1);
712 memset (estring, 0, strlen (string) + j + 1);
713 for(j = 0, i = 0; i < strlen (string); i++, j++) {
714 for(k = 0; k < strlen (charset); k++) {
715 if(string[i] == charset[k])
716 estring[j++] = '\\';
717 }
718 estring[j] = string[i];
719 }
720 NOOP ("ESC:estring=%s\n", estring);
721 return estring;
722 }
723
724 #define MAX_PATH_LABEL 40
725 #define MAX_PATH_START_LABEL 18
726 const gchar *
rfm_chop_excess(gchar * b)727 rfm_chop_excess (gchar * b) {
728 // chop excess length...
729
730 const gchar *home = g_get_home_dir();
731 gchar *bb;
732 if (strncmp(home, b, strlen(home))==0){
733 if (strlen(home) == strlen(b)) return b;
734 bb = g_strconcat("~/", b + strlen(home)+1, NULL);
735 } else {
736 bb = g_strdup(b);
737 }
738
739 int len = strlen (bb);
740
741 if(len < MAX_PATH_LABEL) {
742 strcpy(b, bb);
743 g_free(bb);
744 return (b);
745 }
746
747 bb[MAX_PATH_START_LABEL - 3] = 0;
748
749 gchar *g = g_strconcat(bb, "...", b + (len - MAX_PATH_LABEL + MAX_PATH_START_LABEL), NULL);
750 strcpy (b, g);
751 g_free(bb);
752 g_free(g);
753
754 return b;
755 }
756
757 /*
758 * This function converts a time value specified in seconds since 1970-01-01
759 * to a string representation. It shows the date and the time if the point
760 * if less then six month in the past and only the date otherwise.
761 * The exact format must be provided by a translation string.
762 *
763 * The function should be thread-save since there are not used static
764 * (or even global) variables if the system provided a localtime_r() function.
765 *
766 * Arguments: when: time in seconds since 1970-01-01
767 * string: the result char-array in which the string is placed
768 * length: the length of the string-array
769 *
770 * Return value: string on success, NULL on failure
771 */
772 gchar *
rfm_time_to_string(time_t when)773 rfm_time_to_string (time_t when) {
774 time_t now = time (NULL);
775 #ifdef HAVE_LOCALTIME_R
776 struct tm timestruct;
777 #endif
778 struct tm *timestruct_ptr;
779 char *formatstring;
780 gchar *s = NULL;
781 gchar string[64];
782
783 #ifdef HAVE_MEMSET
784 memset (string, 0, 64);
785 #else
786 string[0] = 0;
787 #endif
788
789 formatstring = difftime (now, when) > 24 * 60 * 60 * 30 * 6
790 /* strftime format for non-recent files (older than 6 months) */
791 ? _("%b %e %Y")
792 /* strftime format for recent files */
793 : _("%b %e %H:%M");
794
795 #ifdef HAVE_LOCALTIME_R
796 timestruct_ptr = ×truct;
797 localtime_r (&when, timestruct_ptr);
798 #else
799 timestruct_ptr = localtime (&when);
800 #endif
801
802 if(strftime (string, 64, formatstring, localtime (&when)) == 0) {
803 return NULL;
804 }
805 s = rfm_utf_string (string);
806 return s;
807 }
808
809 gchar *
rfm_mode_string(mode_t mode)810 rfm_mode_string (mode_t mode) {
811 return mode_string (mode);
812 }
813
814 static pthread_mutex_t user_string_mutex=PTHREAD_MUTEX_INITIALIZER;
815
816 gchar *
rfm_user_string(struct stat * st)817 rfm_user_string (struct stat *st) {
818 pthread_mutex_lock(&user_string_mutex);
819 struct passwd *p;
820 gchar *user_string;
821 if((p = getpwuid (st->st_uid)) != NULL)
822 user_string = g_strdup(p->pw_name);
823 else if((gint)st->st_uid < 0)
824 user_string = g_strdup("");
825 else
826 user_string = g_strdup_printf("%d", (gint)st->st_uid);
827 pthread_mutex_unlock(&user_string_mutex);
828 return user_string;
829 }
830
831 static pthread_mutex_t group_string_mutex=PTHREAD_MUTEX_INITIALIZER;
832
833 gchar *
rfm_group_string(struct stat * st)834 rfm_group_string (struct stat *st) {
835 pthread_mutex_lock(&group_string_mutex);
836 struct group *g;
837 gchar *group_string;
838 if((g = getgrgid(st->st_gid)) != NULL)
839 group_string = g_strdup(g->gr_name);
840 else
841 group_string = g_strdup_printf("%d", (gint)st->st_gid);
842 pthread_mutex_unlock(&group_string_mutex);
843 return group_string;
844 }
845
846 static pthread_mutex_t date_string_mutex=PTHREAD_MUTEX_INITIALIZER;
847 gchar *
rfm_date_string(time_t the_time)848 rfm_date_string (time_t the_time) {
849 pthread_mutex_lock(&date_string_mutex);
850
851 #ifdef HAVE_LOCALTIME_R
852 struct tm t_r;
853 #endif
854 struct tm *t;
855
856 #ifdef HAVE_LOCALTIME_R
857 t = localtime_r (&the_time, &t_r);
858 #else
859 t = localtime (&the_time);
860 #endif
861 gchar *date_string=
862 g_strdup_printf ("%04d/%02d/%02d %02d:%02d", t->tm_year + 1900,
863 t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min);
864 pthread_mutex_unlock(&date_string_mutex);
865
866 return date_string;
867 }
868
869 gchar *
rfm_utf_string(const gchar * t)870 rfm_utf_string (const gchar * t) {
871 if(!t) {
872 NOOP("rfm_utf_string(): string == NULL!\n");
873 return g_strdup ("");
874 }
875
876 if(g_utf8_validate (t, -1, NULL)) {
877 return g_strdup (t);
878 }
879 /* so we got a non-UTF-8 */
880 /* but is it a valid locale string? */
881 gchar *actual_tag;
882 actual_tag = g_locale_to_utf8 (t, -1, NULL, NULL, NULL);
883 if(actual_tag)
884 return actual_tag;
885 /* So it is not even a valid locale string...
886 * Let us get valid utf-8 caracters then: */
887 const gchar *p;
888 actual_tag = g_strdup ("");
889 for(p = t; *p; p++) {
890 // short circuit end of string:
891 gchar *r = g_locale_to_utf8 (p, -1, NULL, NULL, NULL);
892 if(g_utf8_validate (p, -1, NULL)) {
893 gchar *qq = g_strconcat (actual_tag, p, NULL);
894 g_free (actual_tag);
895 actual_tag = qq;
896 break;
897 } else if(r) {
898 gchar *qq = g_strconcat (actual_tag, r, NULL);
899 g_free (r);
900 g_free (actual_tag);
901 actual_tag = qq;
902 break;
903 }
904 // convert caracter to utf-8 valid.
905 gunichar gu = g_utf8_get_char_validated (p, 2);
906 if(gu == (gunichar) - 1) {
907 //DBG("gu=%d\n",(int)gu);
908 gu = g_utf8_get_char_validated ("?", -1);
909 }
910 gchar outbuf[8];
911 memset (outbuf, 0, 8);
912 gint outbuf_len = g_unichar_to_utf8 (gu, outbuf);
913 if(outbuf_len < 0) {
914 DBG ("unichar=%d char =%c outbuf_len=%d\n", gu, p[0], outbuf_len);
915 }
916 gchar *qq = g_strconcat (actual_tag, outbuf, NULL);
917 g_free (actual_tag);
918 actual_tag = qq;
919 }
920 return actual_tag;
921 }
922
923 gchar *
rfm_sizetag(off_t tama,gint count)924 rfm_sizetag (off_t tama, gint count) {
925 gchar *tag = _("bytes");
926 gchar *buf = NULL;
927 double utama = tama;
928
929 buf = NULL;
930 if(utama > 0) {
931 if(utama >= (off_t)1000 * 1000 * 1000) {
932 utama /= ((off_t)1000 * 1000 * 1000);
933 tag = _("Gigabytes");
934 } else if(utama >= 1000 * 1000) {
935 utama /= (1000 * 1000);
936 tag = _("Megabytes");
937 } else if(utama >= 1000) {
938 utama /= 1000;
939 tag = _("Kilobytes");
940 }
941 if(count <= 0) {
942 /* format for size column of regular files */
943 buf = g_strdup_printf ("%.2lf %s", utama, tag);
944 } else {
945 gchar *plural_text=
946 g_strdup_printf (ngettext ("%'u item", "%'u items",
947 count),count);
948 if (tama < 1000) {
949 buf = g_strdup_printf ("%s: %.0lf %s.", plural_text,
950 utama, tag);
951 } else {
952 buf = g_strdup_printf ("%s: %.2lf %s.", plural_text,
953 utama, tag);
954 }
955 g_free(plural_text);
956
957 }
958 } else {
959 if(count <=0) {
960 buf = g_strdup_printf (_("The location is empty."));
961 } else {
962 buf=
963 g_strdup_printf (ngettext ("%'u item", "%'u items", count),
964 count);
965 }
966 }
967 return buf;
968 }
969
970 /**
971 * rfm_host_name:
972 * @xid: Window X id
973 *
974 * This gets the hostname of the window client. This is different from
975 * #g_get_host_name() which gets the name of the window server. This may be
976 * different when the window is displayed on a remote server.
977 *
978 **/
979 gchar *
rfm_host_name(Window xid)980 rfm_host_name (Window xid) {
981 char *name = NULL;
982 unsigned char *property_data;
983 unsigned long items,
984 remaining;
985 int actual_format;
986 Atom atomo,
987 actual_atom;
988 if (rfm_get_gtk_thread() != g_thread_self())
989 g_error("rfm_host_name(): only to be called by gtk thread\n");
990 rfm_global_t *rfm_global_p = rfm_global();
991 if (!rfm_global_p) return "localhost";
992 atomo = XInternAtom (rfm_global_p->Xdisplay, "WM_CLIENT_MACHINE", FALSE);
993 if(XGetWindowProperty (rfm_global_p->Xdisplay,
994 xid,
995 atomo, 0, 255, FALSE, XA_STRING,
996 &actual_atom, &actual_format, &items,
997 &remaining, &property_data) == Success) {
998 NOOP ("property_data=%s", ((property_data) ? property_data : (unsigned char *)"null"));
999 if(!property_data)
1000 name = g_strdup (g_get_host_name ());
1001 else {
1002 name = g_strdup ((const gchar *)property_data);
1003 XFree (property_data);
1004 }
1005 } else
1006 name = g_strdup (g_get_host_name ());
1007 return name;
1008 }
1009
1010 gint
rfm_mkdir(const gchar * dir)1011 rfm_mkdir (const gchar * dir) {
1012 if(rfm_g_file_test (dir, G_FILE_TEST_EXISTS)) {
1013 gchar *message;
1014 if(!rfm_g_file_test (dir, G_FILE_TEST_IS_DIR)) {
1015 message = g_strdup_printf ("%s: %s (ENOTDIR: %s)", dir, strerror (EEXIST), strerror (ENOTDIR));
1016 } else {
1017 message = g_strdup_printf ("%s: %s", dir, strerror (EEXIST));
1018 }
1019 rfm_confirm (NULL, GTK_MESSAGE_ERROR, message, NULL, NULL);
1020 g_free (message);
1021 return 0;
1022 }
1023 if(g_mkdir_with_parents (dir, 0700) < 0) {
1024 //DBG("cannot create %s: %s\n", dir, strerror(errno));
1025 }
1026 if(rfm_g_file_test (dir, G_FILE_TEST_EXISTS)) {
1027 if(rfm_g_file_test (dir, G_FILE_TEST_IS_DIR)) {
1028 return 0;
1029 }
1030 }
1031 NOOP ("!rfm_g_file_test(%s, G_FILE_TEST_IS_DIR\n", dir);
1032 return -1;
1033 }
1034
1035 void
rfm_save_to_go_history(char * p)1036 rfm_save_to_go_history (char *p) {
1037 gchar *f = g_build_filename (GOTO_DBH_FILE, NULL);
1038 COMBOBOX_save_to_history (f, p);
1039 g_free (f);
1040 }
1041
1042 void
rfm_init_env(void)1043 rfm_init_env (void) {
1044 gint i;
1045 static gsize initialized = 0;
1046 if (g_once_init_enter (&initialized)){
1047 environ_t *environ_v = rfm_get_environ();
1048 // XXX: This initialization may now be obsolete:
1049 // must check.
1050 for(i = 0; environ_v[i].env_var; i++) {
1051 // copy default values from static memory to heap
1052 if(strcmp (environ_v[i].env_var, "SUDO_ASKPASS") == 0 ||
1053 strcmp(environ_v[i].env_var, "SSH_ASKPASS") == 0 ) {
1054 environ_v[i].env_string = g_find_program_in_path ("rodent-getpass");
1055 }
1056 else if(environ_v[i].env_string) {
1057 environ_v[i].env_string = g_strdup (environ_v[i].env_string);
1058 NOOP ("ENV: %s %s\n", environ_v[i].env_var, environ_v[i].env_string);
1059 }
1060 }
1061 g_once_init_leave (&initialized, 1);
1062 }
1063 return;
1064 }
1065
1066
1067 void
rfm_setenv(const gchar * name,gchar * value,gboolean verbose)1068 rfm_setenv (const gchar *name, gchar *value, gboolean verbose) {
1069 rfm_init_env();
1070 NOOP(stderr, "setting %s to %s\n", name, value);
1071 gint which;
1072 gboolean go_ahead = FALSE;
1073 environ_t *environ_v = rfm_get_environ();
1074 for(which = 0; environ_v[which].env_var; which++){
1075 if(strcmp (name, environ_v[which].env_var) == 0){
1076 break;
1077 }
1078 }
1079 if(!environ_v[which].env_var) return;
1080 if(!value || !strlen (value)) {
1081 g_free (environ_v[which].env_string);
1082 #ifdef HAVE_UNSETENV
1083 environ_v[which].env_string = NULL;
1084 unsetenv (name);
1085 #else
1086 environ_v[which].env_string = g_strconcat (name, "=", NULL);
1087 putenv (environ_v[which].env_string);
1088 #endif
1089 /*if(verbose) {
1090 if(strcmp (name, "SMB_USER") == 0) {
1091 TRACE("Mcs manager changed rfm environment: %s\n", name);
1092 } else {
1093 TRACE("Mcs manager changed rfm environment: %s=%s\n", name, ((value) ? value : " "));
1094 }
1095 }*/
1096 return;
1097 }
1098 if(strcmp (name, "RFM_MAX_PREVIEW_SIZE") == 0) {
1099 if(is_number (value))
1100 go_ahead = TRUE;
1101 } else if(strcmp (name, "TERMCMD") == 0) {
1102 if(value && strlen (value)) {
1103 gchar *c;
1104 gchar *t = g_strdup (value);
1105 t = g_strstrip (t);
1106 if(strchr (t, ' '))
1107 t = strtok (t, " ");
1108 c = g_find_program_in_path (t);
1109 if(c && access (c, X_OK) == 0) {
1110 go_ahead = TRUE;
1111 }
1112 g_free (c);
1113 c = NULL;
1114 g_free (t);
1115 t = NULL;
1116 }
1117 } else
1118 go_ahead = TRUE;
1119 if(go_ahead) {
1120 g_free (environ_v[which].env_string);
1121 gchar *getpass = NULL;
1122 gchar *editor = NULL;
1123 if (strcmp (name, "EDITOR") == 0){
1124 editor = rfm_get_text_editor_envar(value);
1125 NOOP(stderr, "Setting text editor to %s\n", editor);
1126 }
1127 if (editor){
1128 value = editor;
1129 } else {
1130 if (strcmp (name, "SUDO_ASKPASS") == 0 ||
1131 strcmp(name, "SSH_ASKPASS") == 0 ){
1132 if (!g_file_test(value, G_FILE_TEST_EXISTS)){
1133 getpass = g_find_program_in_path ("rodent-getpass");
1134 }
1135 }
1136 }
1137 if (getpass) value = getpass;
1138
1139
1140 NOOP(stderr, "rfm_setenv(): setting %s -> %s \n", name, value);
1141 #ifdef HAVE_SETENV
1142 environ_v[which].env_string = g_strdup (value);
1143 setenv (name, environ_v[which].env_string, 1);
1144 #else
1145 environ_v[which].env_string = g_strconcat (name, "=", value, NULL);
1146 putenv (environ_v[which].env_string);
1147 #endif
1148 g_free(editor);
1149 } else { /* not go_ahead */
1150 DBG ("failed to change rfm environment: %s\n", name);
1151 }
1152 return;
1153 }
1154
1155 void
rfm_threadwait(void)1156 rfm_threadwait (void) {
1157 struct timespec thread_wait = {
1158 0, 100000000
1159 };
1160 nanosleep (&thread_wait, NULL);
1161 }
1162
1163 #ifdef DEBUG_NOOP
1164 # include <sys/times.h>
1165 static struct tms *last_clock = NULL;
1166
1167 const gchar *
profile(void)1168 profile (void) {
1169 struct tms this_clock;
1170 static gchar *p = NULL;
1171 g_free (p);
1172 if(!last_clock) {
1173 last_clock = (struct tms *)malloc (sizeof (struct tms));
1174 times (last_clock);
1175 }
1176 times (&this_clock);
1177 p = g_strdup_printf ("\n\tPROFILE*** user=%ld, system=%ld",
1178 (long)(this_clock.tms_utime - last_clock->tms_utime),
1179 (long)(this_clock.tms_stime - last_clock->tms_stime));
1180 memcpy (last_clock, &this_clock, sizeof (struct tms));
1181
1182 return (const gchar *)p;
1183 }
1184 #endif
1185 static gboolean
rect_OK(view_t * view_p,GdkRectangle * rect_p)1186 rect_OK(view_t * view_p, GdkRectangle * rect_p){
1187 // nope return TRUE;
1188 if (view_p->flags.type == DESKVIEW_TYPE) return TRUE;
1189 GtkScrolledWindow *scrolled_window = g_object_get_data(G_OBJECT(view_p->widgets.paper), "scrolled_window");
1190 if (!scrolled_window || !GTK_IS_SCROLLED_WINDOW(scrolled_window)) return FALSE;
1191 if (!GTK_IS_ADJUSTMENT(
1192 gtk_scrolled_window_get_vadjustment (scrolled_window)
1193 )) return TRUE;
1194 g_mutex_lock(view_p->mutexes.status_mutex);
1195 gint status = view_p->flags.status;
1196 g_mutex_unlock(view_p->mutexes.status_mutex);
1197 if (status == STATUS_EXIT) return FALSE;
1198
1199 gdouble position=gtk_adjustment_get_value (
1200 gtk_scrolled_window_get_vadjustment (scrolled_window));
1201 gdouble page=gtk_adjustment_get_page_size (
1202 gtk_scrolled_window_get_vadjustment (scrolled_window));
1203 if (rect_p->y >= position && rect_p->y <= position + page + 0.9){
1204 return TRUE;
1205 }
1206 if (rect_p->y + rect_p->height >= position &&
1207 rect_p->y + rect_p->height <= position + page + 0.9){
1208 return TRUE;
1209 }
1210 NOOP(stderr, "y=(%d,%d), page=%lf position=%lf\n",
1211 rect_p->y, rect_p->y + rect_p->height,
1212 page, position);
1213 return FALSE;
1214 }
1215
1216
1217 typedef struct expose_rect_t {
1218 view_t *view_p;
1219 GdkRectangle rect;
1220 } expose_rect_t;
1221
1222 static void
expose_it(expose_rect_t * expose_rect_p)1223 expose_it(expose_rect_t *expose_rect_p){
1224 NOOP(stderr, "expose_it %d,%d %d,%d --\n",
1225 expose_rect_p->rect.x, expose_rect_p->rect.y,
1226 expose_rect_p->rect.width, expose_rect_p->rect.height);
1227 GdkWindow * window;
1228 window = gtk_widget_get_window(expose_rect_p->view_p->widgets.paper);
1229 gdk_window_invalidate_rect (window, &(expose_rect_p->rect), TRUE);
1230 }
1231
1232 // main context function
1233 static gboolean
expose_rect_f(gpointer data)1234 expose_rect_f(gpointer data){
1235 expose_rect_t *expose_rect_p = data;
1236 if (rfm_main_context_check(expose_rect_p->view_p, FALSE)){
1237 expose_it(expose_rect_p);
1238 }
1239 g_free(data);
1240 return FALSE;
1241 }
1242
1243
1244 void
rfm_expose_rect(view_t * view_p,const GdkRectangle * rect_p)1245 rfm_expose_rect (view_t * view_p, const GdkRectangle *rect_p) {
1246 TRACE("rfm_expose_rect()...\n");
1247 if (!rfm_view_list_lock(view_p, "rfm_expose_rect")) return;
1248 if (rfm_get_gtk_thread() == g_thread_self()) {
1249 expose_rect_t expose_rect_v;
1250 expose_rect_v.view_p = view_p;
1251 memcpy(&(expose_rect_v.rect), rect_p, sizeof(GdkRectangle));
1252 expose_it(&expose_rect_v);
1253 rfm_view_list_unlock("rfm_expose_rect");
1254 return;
1255 }
1256 expose_rect_t *expose_rect_p = (expose_rect_t *)malloc(sizeof(expose_rect_t));
1257 if (!expose_rect_p) g_error("malloc: %s\n", strerror(errno));
1258 expose_rect_p->view_p = view_p;
1259 memcpy(&(expose_rect_p->rect), rect_p, sizeof(GdkRectangle));
1260 // don't wait here
1261 g_main_context_invoke (NULL, expose_rect_f, expose_rect_p);
1262 rfm_view_list_unlock("rfm_expose_rect");
1263 return;
1264 }
1265
1266
1267 // non threaded:
1268
1269 // the +5 refers to the red outline and the cute shadow and is less
1270 // than the empty space between icon and text (< TEXTSPACING)
1271 void
rfm_expose_icon(view_t * view_p,const population_t * population_p)1272 rfm_expose_icon (view_t * view_p, const population_t * population_p) {
1273 NOOP (">> rfm_expose_icon\n");
1274 GdkRectangle rect;
1275 if (!population_p) return;
1276 if (!rfm_get_population_rect(view_p, population_p, &rect))return;
1277 gint icon_size = ICON_SIZE(view_p);
1278 if (icon_size >= SMALL_ICON_SIZE) {
1279 rect.width = CELLWIDTH(view_p);
1280 rect.height = icon_size + 5;
1281 } else if (icon_size >= TINY_ICON_SIZE){
1282 rect.width = rect.height = icon_size+2;
1283 }
1284 else {
1285 rect.width = rect.height = TINY_ICON_SIZE+2;
1286
1287 }
1288
1289 if (!rect_OK(view_p, &rect)){
1290 NOOP(stderr, "icon for %s is out of sight!\n",
1291 (population_p->en)?population_p->en->path:"null");
1292 return;
1293 }
1294 rfm_expose_rect(view_p, &rect);
1295 //gdk_window_invalidate_rect (gtk_widget_get_window(view_p->widgets.paper), &rect, TRUE);
1296 return;
1297 }
1298
1299 // This should only be for saturated case, otherwise a plain expose item.
1300 void
rfm_expose_label(view_t * view_p,const population_t * population_p)1301 rfm_expose_label (view_t * view_p, const population_t * population_p) {
1302 if (!population_p) return;
1303 NOOP (">> rfm_expose_label (full)\n");
1304 GdkRectangle rect;
1305
1306 if (ICON_SIZE_ID(view_p) == 0) {
1307 if (!rfm_get_population_rect(view_p, population_p, &rect)){
1308 return;
1309 }
1310
1311 } else {
1312 if (!rfm_get_population_label_rect_full(view_p, population_p, &rect)){
1313 return;
1314 }
1315 if (ICON_SIZE(view_p) >= SMALL_ICON_SIZE){
1316 // I wonder where the 2 comes from (required).
1317 rect.height = CELLHEIGHT(view_p) - (TEXTSPACING + ICON_SIZE(view_p) + 2);
1318 }
1319 }
1320
1321 // Expose double height to erase non saturated text (hack!)
1322 // rect.height *= 2;
1323
1324 if (!rect_OK(view_p, &rect)){
1325 TRACE( "label for %s is out of sight!\n",
1326 (population_p->en)?population_p->en->path:"null");
1327 return;
1328 }
1329 NOOP(stderr, "expose label: %s x: %d-%d y: %d-%d\n",
1330 population_p->label,
1331 rect.x, rect.x+rect.width, rect.y, rect.y+rect.height);
1332 rfm_expose_rect(view_p, &rect);
1333 //gdk_window_invalidate_rect (gtk_widget_get_window(view_p->widgets.paper), &rect, TRUE);
1334 return;
1335 }
1336
1337 void
rfm_expose_item(view_t * view_p,const population_t * population_p)1338 rfm_expose_item (view_t * view_p, const population_t * population_p) {
1339 GdkRectangle rect;
1340 if (!rfm_get_population_rect(view_p, population_p, &rect)) return;
1341 NOOP ("1>> rfm_expose_item %d,%d %d,%d\n", rect.x, rect.y, rect.width, rect.height);
1342 NOOP ("2>> rfm_expose_item %d,%d %d,%d\n", rect.x, rect.y, rect.width, rect.height);
1343 if (!rect_OK(view_p, &rect)){
1344 NOOP(stderr, "item for %s is out of sight!\n",
1345 (population_p->en)?population_p->en->path:"null");
1346 NOOP (stderr, "2>> rfm_expose_item %d,%d %d,%d\n", rect.x, rect.y, rect.width, rect.height);
1347 return;
1348 }
1349 rfm_expose_rect(view_p, &rect);
1350 //gdk_window_invalidate_rect (gtk_widget_get_window(view_p->widgets.paper), &rect, TRUE);
1351 return;
1352 }
1353
1354 // client based pasteboard (with MIT-shm)
1355 // no interclient communication, but then again,
1356 // I really never found any use for it
1357
1358
1359 void
rfm_clear_paste_buffer(void)1360 rfm_clear_paste_buffer(void){
1361 rfm_rw_lock_writer_lock(&drag_info_lock);
1362 shm_unlink (PASTE_SHM_NAME);
1363 rfm_rw_lock_writer_unlock(&drag_info_lock);
1364 }
1365
1366 void
rfm_store_paste_buffer(gchar * buffer,gint len)1367 rfm_store_paste_buffer(gchar *buffer, gint len){
1368 rfm_rw_lock_writer_lock(&drag_info_lock);
1369
1370 // Remove old MIT-shm pasteboard.
1371 shm_unlink (PASTE_SHM_NAME);
1372
1373 gint fd = shm_open (PASTE_SHM_NAME, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
1374 if(fd < 0){
1375 g_error ("rfm_store_paste_buffer(): shm_open(%s): %s", PASTE_SHM_NAME, strerror (errno));
1376 }
1377 // Get writelock .
1378
1379 // Truncate to necessary memory size to allocate.
1380 if(ftruncate (fd, sizeof(gint)+strlen(buffer)+1) < 0) {
1381 g_error ("rfm_store_paste_buffer(): ftruncate(%s): %s", PASTE_SHM_NAME, strerror (errno));
1382 }
1383
1384 // Get a shared memory pointer.
1385 void *p = mmap (NULL, sizeof(gint)+strlen(buffer)+1,
1386 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1387 // Close file descriptor
1388 close(fd);
1389 // Save size as first sizeof(gint) bytes.
1390 gint *len_p = p;
1391 *len_p=sizeof(gint)+strlen(buffer)+1;
1392 // Save text buffer now.
1393 gchar *buffer_p = p + sizeof(gint);
1394 strcpy(buffer_p, buffer);
1395 // Put in shared memory.
1396 if(msync (p, sizeof(gint)+strlen(buffer)+1, MS_SYNC) < 0){
1397 DBG ("rfm_store_paste_buffer(): msync(%s): %s\n", PASTE_SHM_NAME, strerror (errno));
1398 }
1399 // Release map so other processes may shm_unlink.
1400 munmap (p, sizeof(gint)+strlen(buffer)+1);
1401
1402
1403 // release writelock on shm_name file descriptor
1404 rfm_rw_lock_writer_unlock(&drag_info_lock);
1405 }
1406
1407 static
get_paste_length()1408 gint get_paste_length(){
1409 gint fd = shm_open (PASTE_SHM_NAME, O_RDONLY, S_IRUSR | S_IWUSR);
1410 if(fd < 0){
1411 return 0;
1412 }
1413 // Get readlock on shm_name file descriptor.
1414 rfm_rw_lock_reader_lock(&drag_info_lock);
1415
1416 // Figure out the size.
1417 void *p = mmap (NULL, sizeof(gint),
1418 //void *p = mmap (NULL, 133,
1419 PROT_READ, MAP_SHARED, fd, 0);
1420 close(fd);
1421
1422 gint len = *((gint *)p);
1423 if(msync (p, sizeof(gint), MS_SYNC) < 0){
1424 DBG ("msync(%s): %s\n", PASTE_SHM_NAME, strerror (errno));
1425 }
1426 munmap (p, sizeof(gint));
1427 // release writelock on shm_name file descriptor
1428 rfm_rw_lock_reader_unlock(&drag_info_lock);
1429 return len;
1430 }
1431
1432
1433 gchar *
rfm_get_paste_buffer(void)1434 rfm_get_paste_buffer (void ) {
1435 // Get readlock on shm_name file descriptor.
1436 rfm_rw_lock_reader_lock(&drag_info_lock);
1437 gint len=get_paste_length();
1438 if (len==0) {
1439 rfm_rw_lock_reader_unlock(&drag_info_lock);
1440 return NULL;
1441 }
1442 int fd = shm_open (PASTE_SHM_NAME, O_RDONLY, S_IRUSR | S_IWUSR);
1443 if(fd < 0){
1444 rfm_rw_lock_reader_unlock(&drag_info_lock);
1445 return NULL;
1446 }
1447
1448
1449 void *pp = mmap (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
1450 gchar *buffer_p = pp + sizeof(gint);
1451 gchar *buffer = g_strdup(buffer_p);
1452 munmap (pp, len);
1453 // release writelock on shm_name file descriptor
1454 close(fd);
1455 rfm_rw_lock_reader_unlock(&drag_info_lock);
1456 return buffer;
1457 }
1458
1459
1460 int
rfm_pasteboard_status(view_t * view_p)1461 rfm_pasteboard_status (view_t * view_p) {
1462 if(!view_p) return 0;
1463 // This is cool with client side pasteboard because
1464 // nobody is going to mess with pasteboard.
1465 // With server side, this is not so, because somebody
1466 // might mess it up and rodent will still think the pasteboard
1467 // is valid...
1468
1469 // This crashes with multithread server side pasteboard:
1470 rfm_update_pasteboard (view_p);
1471
1472 gchar *b = view_p->xbuffer;
1473 if(!b || !strlen (b)) return 0;
1474
1475 const gchar *cut = "#xfvalid_buffer:cut";
1476 const gchar *copy = "#xfvalid_buffer:copy";
1477 if(strncmp (b, copy, strlen (copy)) == 0)
1478 return 1;
1479 if(strncmp (b, cut, strlen (cut)) == 0)
1480 return 2;
1481 return 0;
1482 }
1483
1484 gchar **
rfm_pasteboard_v(view_t * view_p)1485 rfm_pasteboard_v(view_t * view_p){
1486 if (!rfm_pasteboard_status (view_p)) return NULL;
1487 // this is to skip the valid buffer line:
1488 gchar *search = strchr (view_p->xbuffer, '\n');
1489 if(!search)return NULL;
1490 search++;
1491 gchar **v = g_strsplit(search, "\n", -1);
1492 return v;
1493 }
1494
1495
1496 /* returns 0 if not in pasteboard, 1 if in copy pasteboard or 2 if
1497 * in cut pasteboard */
1498 int
rfm_in_pasteboard(view_t * view_p,record_entry_t * en)1499 rfm_in_pasteboard (view_t * view_p, record_entry_t * en) {
1500 if(!en || !en->path) return FALSE;
1501 if(IS_ROOT_TYPE (en->type) && !IS_SDIR(en->type)) return FALSE;
1502
1503 gchar **pasteboard_v = rfm_pasteboard_v(view_p);
1504 gchar **p = pasteboard_v;
1505 gint retval=0;
1506 gint status = rfm_pasteboard_status (view_p);
1507 for(; p && *p; p++){
1508 if(strcmp (*p, en->path) == 0) {
1509 retval = status;
1510 break;
1511 }
1512 }
1513 g_strfreev(pasteboard_v);
1514 return retval;
1515 #if 0
1516 obsolete...
1517 int paste_type = rfm_valid_pasteboard (view_p);
1518 if(!paste_type) return FALSE;
1519 gchar *b = g_strdup (view_p->xbuffer);
1520
1521 // this is the valid buffer line:
1522 gchar *search = strtok (b, "\n");
1523 if(!search) {
1524 g_free (b);
1525 return 0;
1526 }
1527 // now we check line per line for the path.
1528 search = strtok (NULL, "\n");
1529 while(search) {
1530 if(strcmp (search, en->path) == 0) {
1531 NOOP ("PASTE type =%d %s\n", paste_type, en->path);
1532 g_free (b);
1533 return paste_type;
1534 }
1535 search = strtok (NULL, "\n");
1536 }
1537 g_free (b);
1538 return 0;
1539 #endif
1540 }
1541
1542 gboolean
rfm_update_pasteboard(view_t * view_p)1543 rfm_update_pasteboard (view_t * view_p) {
1544 if(!view_p->xbuffer) view_p->xbuffer = rfm_get_paste_buffer ();
1545 gchar *current_xbuffer = rfm_get_paste_buffer ();
1546 if (!current_xbuffer && !view_p->xbuffer) return FALSE;
1547 if(!view_p->xbuffer && current_xbuffer){
1548 view_p->xbuffer = current_xbuffer;
1549 return TRUE;
1550 }
1551 if(view_p->xbuffer && !current_xbuffer){
1552 g_free (view_p->xbuffer);
1553 view_p->xbuffer = NULL;
1554 return TRUE;
1555 }
1556 // here both pointers are valid
1557 if(strcmp (current_xbuffer, view_p->xbuffer)) {
1558 NOOP ("XBUFFER: xbuffer has changed! %s\n", current_xbuffer);
1559 g_free (view_p->xbuffer);
1560 view_p->xbuffer = current_xbuffer;
1561 return TRUE;
1562 } else {
1563 NOOP ("XBUFFER: xbuffer OK!\n");
1564 g_free (current_xbuffer);
1565 return FALSE;
1566 }
1567
1568 }
1569
1570 ////////////////////////////////////////////////////////////////////////////////////////////
1571
1572 gboolean
rfm_write_ok_path(const gchar * target_path)1573 rfm_write_ok_path(const gchar *target_path){
1574 if (!target_path) return FALSE;
1575 if (!g_path_is_absolute(target_path)){
1576 DBG("rfm_write_ok_path() is FALSE: %s is not absolute!\n",
1577 target_path);
1578 return FALSE;
1579 }
1580
1581 gchar *dirname = NULL;
1582 if (rfm_g_file_test (target_path, G_FILE_TEST_IS_DIR)) {
1583 dirname =g_strdup(target_path);
1584 } else {
1585 dirname = g_path_get_dirname(target_path);
1586 }
1587 gboolean result = access(dirname, W_OK);
1588 g_free(dirname);
1589 return (result<0)?0:1;
1590 }
1591
1592
1593 gboolean
rfm_read_ok_path(const gchar * target_path)1594 rfm_read_ok_path(const gchar *target_path){
1595 if (!target_path) return FALSE;
1596 if (!g_path_is_absolute(target_path)){
1597 DBG("rfm_read_ok_path() is FALSE: %s is not absolute!\n",
1598 target_path);
1599 return FALSE;
1600 }
1601
1602
1603 gboolean result = access(target_path, R_OK);
1604 return (result<0)?0:1;
1605
1606 }
1607
1608 static
1609 gchar *
default_shell(void)1610 default_shell(void){
1611 gchar *shell=NULL;
1612 if(!shell)
1613 shell = g_find_program_in_path ("bash");
1614 if(!shell)
1615 shell = g_find_program_in_path ("zsh");
1616 if(!shell)
1617 shell = g_find_program_in_path ("sh");
1618 if (!shell && rfm_void(PLUGIN_DIR, "ps", "module_active")) {
1619 shell = g_find_program_in_path ("tcsh");
1620 if(!shell)
1621 shell = g_find_program_in_path ("csh");
1622 }
1623 if(!shell)
1624 shell = g_find_program_in_path ("ksh");
1625 if(!shell)
1626 shell = g_find_program_in_path ("sash");
1627 if(!shell)
1628 shell = g_find_program_in_path ("ash");
1629 if(!shell){
1630 DBG("unable to find a valid shell\n");
1631 }
1632
1633 return shell;
1634 }
1635
1636 // dash is OK now.
1637 // Only csh/tcsh is broken, since it will not
1638 // pass on SIGTERM when controler gets SIGUSR1
1639 // This is only a problem if rodent_ps is not
1640 // loadable.
1641 // gchar *
1642 static gchar *
check_shell(gchar * shell)1643 check_shell(gchar *shell){
1644 if (!shell) return NULL;
1645 // This is because the stop process button will not work with csh.
1646 if (!rfm_void(PLUGIN_DIR, "ps", "module_active") && strstr(shell, "csh")) {
1647 g_free(shell);
1648 shell = NULL;
1649 }
1650 return shell;
1651 }
1652
1653 gchar *
rfm_shell(void)1654 rfm_shell(void){
1655 gchar *shell=NULL;
1656 if(getenv ("SHELL") && strlen (getenv ("SHELL"))) {
1657 shell = g_find_program_in_path (getenv ("SHELL"));
1658 }
1659
1660 if(!shell && getenv ("XTERM_SHELL") && strlen (getenv ("XTERM_SHELL"))) {
1661 shell = g_find_program_in_path (getenv ("XTERM_SHELL"));
1662 }
1663 shell = check_shell(shell);
1664
1665 if (!shell){
1666 shell = default_shell();
1667 }
1668 return shell;
1669 }
1670
1671 gchar *
rfm_xterm_shell(void)1672 rfm_xterm_shell(void){
1673 gchar *shell=NULL;
1674
1675 if(!shell && getenv ("XTERM_SHELL") && strlen (getenv ("XTERM_SHELL"))) {
1676 shell = g_find_program_in_path (getenv ("XTERM_SHELL"));
1677 }
1678 if(!shell && getenv ("SHELL") && strlen (getenv ("SHELL"))) {
1679 shell = g_find_program_in_path (getenv ("SHELL"));
1680 }
1681 shell = check_shell(shell);
1682 if (!shell){
1683 shell = default_shell();
1684 }
1685 return shell;
1686 }
1687
1688
1689 // Quickie test
1690 population_t *
rfm_locate_path(view_t * view_p,const gchar * pathname)1691 rfm_locate_path(view_t *view_p, const gchar *pathname){
1692 if (!view_p || ! view_p->en || !view_p->population_pp) return NULL;
1693 if (!rfm_population_read_lock(view_p, "rfm_locate_path")) return NULL;
1694 population_t **pp = view_p->population_pp;
1695 for (;pp && *pp; pp++){
1696 population_t *p = *pp;
1697 if (p->en && strcmp(p->en->path, pathname)==0){
1698 rfm_population_read_unlock(view_p, "rfm_locate_path");
1699 return p;
1700 }
1701 }
1702 rfm_population_read_unlock(view_p, "rfm_locate_path");
1703 return NULL;
1704 }
1705
1706
1707 ///////////////// timeout functinality ////////////////////////////////7
1708 //#define DISABLE_FILE_TEST_WITH_TIMEOUT 1
1709 #ifdef DISABLE_FILE_TEST_WITH_TIMEOUT
1710 gboolean
rfm_g_file_test_with_wait(const gchar * path,GFileTest test)1711 rfm_g_file_test_with_wait(const gchar *path, GFileTest test){
1712 return rfm_g_file_test(path, test);
1713 }
1714
1715 #else
1716 typedef struct heartbeat_t{
1717 gboolean condition;
1718 GMutex *mutex;
1719 GCond *signal;
1720 GThread *thread;
1721 gchar *path;
1722 GFileTest test;
1723 } heartbeat_t;
1724
1725 static void *
heartbeat_g_file_test(gpointer data)1726 heartbeat_g_file_test(gpointer data){
1727 heartbeat_t *heartbeat_p = data;
1728
1729 // This function call may block
1730 NOOP("heartbeat doing stat %s\n", heartbeat_p->path);
1731 struct stat st;
1732 if (lstat(heartbeat_p->path, &st) < 0) return NULL;
1733
1734 // If test is not for symlink, and item is a symlink,
1735 // then follow the symlink for the test.
1736 if (S_ISLNK(st.st_mode)){
1737 if (heartbeat_p->test == G_FILE_TEST_IS_SYMLINK){
1738 return GINT_TO_POINTER(TRUE);
1739 }
1740 if (stat(heartbeat_p->path, &st) < 0) return NULL;
1741 }
1742
1743 gboolean retval = FALSE;
1744 switch (heartbeat_p->test){
1745 case G_FILE_TEST_EXISTS: retval = TRUE; break;
1746 case G_FILE_TEST_IS_REGULAR: retval = S_ISREG(st.st_mode); break;
1747 case G_FILE_TEST_IS_EXECUTABLE:
1748 retval = ((st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) && S_ISREG(st.st_mode));
1749 break;
1750 case G_FILE_TEST_IS_SYMLINK: retval = S_ISLNK(st.st_mode); break;
1751 case G_FILE_TEST_IS_DIR: retval = S_ISDIR (st.st_mode); break;
1752
1753 }
1754
1755 g_mutex_lock(heartbeat_p->mutex);
1756 heartbeat_p->condition = TRUE;
1757 g_mutex_unlock(heartbeat_p->mutex);
1758 NOOP("heartbeat signal %d\n", retval);
1759 g_cond_signal(heartbeat_p->signal);
1760 return GINT_TO_POINTER(retval);
1761
1762 }
1763
1764 static
wait_on_thread(gpointer data)1765 void *wait_on_thread(gpointer data){
1766 heartbeat_t *heartbeat_p = data;
1767 void *value = g_thread_join(heartbeat_p->thread);
1768
1769 rfm_mutex_free(heartbeat_p->mutex);
1770 rfm_cond_free(heartbeat_p->signal);
1771 g_free (heartbeat_p->path);
1772 g_free (heartbeat_p);
1773 return value;
1774 }
1775
1776 // g_file_test_with_timeout
1777 gboolean
rfm_g_file_test_with_wait(const gchar * path,GFileTest test)1778 rfm_g_file_test_with_wait(const gchar *path, GFileTest test){
1779 if (!path) return FALSE;
1780 if (!g_path_is_absolute(path)) return FALSE;
1781 NOOP(stderr, "rfm_g_file_test_with_wait: %s\n", path);
1782
1783 heartbeat_t *heartbeat_p = (heartbeat_t *)malloc(sizeof(heartbeat_t));
1784 if (!heartbeat_p) g_error("malloc heartbeat_p: %s\n",strerror(errno));
1785 memset(heartbeat_p, 0, sizeof(heartbeat_t));
1786
1787 rfm_mutex_init(heartbeat_p->mutex);
1788 rfm_cond_init(heartbeat_p->signal);
1789 heartbeat_p->condition = 0;
1790 heartbeat_p->path = g_strdup(path);
1791 heartbeat_p->test = test;
1792
1793 g_mutex_lock(heartbeat_p->mutex);
1794 NOOP("Creating wait thread for heartbeat_g_file_test_with_timeout\n");
1795 // This thread does not affect view nor window.
1796 heartbeat_p->thread =
1797 rfm_thread_create ("heartbeat_g_file_test", heartbeat_g_file_test, heartbeat_p, TRUE);
1798 if (heartbeat_p->thread && !heartbeat_p->condition) {
1799 if (!rfm_cond_timed_wait(heartbeat_p->signal, heartbeat_p->mutex, 2)) {
1800 g_mutex_unlock(heartbeat_p->mutex);
1801 NOOP("dead heartbeat: rfm_g_file_test\n");
1802 // Dead heartbeat:
1803 // Fire off a wait and cleanup thread.
1804 rfm_thread_create ("wait_on_thread", wait_on_thread, heartbeat_p, FALSE);
1805 return FALSE;
1806 }
1807 }
1808 g_mutex_unlock(heartbeat_p->mutex);
1809 return (GPOINTER_TO_INT(wait_on_thread(heartbeat_p)));
1810
1811 }
1812 #endif
1813 gboolean
rfm_g_file_test(const gchar * path,GFileTest test)1814 rfm_g_file_test(const gchar *path, GFileTest test){
1815 if (!path) return FALSE;
1816 if (!g_path_is_absolute(path)) return FALSE;
1817 return g_file_test(path, test);
1818 }
1819
1820
1821