Tooltalk fixes for OpenBSD. This consists mainly of #ifdefs, casts and some small...
[oweals/cde.git] / cde / lib / tt / lib / tttk / ttdtfile.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 //%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                  
24 //%%  (c) Copyright 1993, 1994 International Business Machines Corp.    
25 //%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                   
26 //%%  (c) Copyright 1993, 1994 Novell, Inc.                             
27 //%%  $XConsortium: ttdtfile.C /main/3 1995/10/23 10:32:19 rswiston $                                                   
28 /*
29  * @(#)ttdtfile.C       1.3 93/09/07
30  *
31  * Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
32  */
33 #include <unistd.h>
34 #include "api/c/tt_c.h"
35 #include "api/c/api_api.h"
36 #include "tttk/tttk.h"
37 #include "tttk/tttkpattern.h"
38 #include "tttk/tttkmessage.h"
39 #include "tttk/tttk2free.h"
40 #include "tttk/tttkutils.h"
41  
42 typedef struct {
43         Tt_scope        theScope;
44         const char     *pathname;
45         Ttdt_file_cb    cb;
46         void           *clientData;
47 } TtDtFileJoinInfo;
48
49 //
50 // Parse file message and pass it to user callback
51 //
52 static Tt_message
53 _ttDtFileCB(
54         Tt_message      msg,
55         Tt_pattern      ,               // Not needed
56         void           *clientCB,
57         void           *clientData
58 )
59 {
60         if (clientCB == 0) {
61                 return msg;
62         }
63         _TttkItem2Free fuse = msg;
64         char *file = tt_message_file( msg );
65         Tt_status status = tt_ptr_error( file );
66         if (status != TT_OK) {
67                 return (Tt_message)tt_error_pointer( status );
68         }
69         char *opname = tt_message_op( msg );
70         status = tt_ptr_error( opname );
71         if (status != TT_OK) {
72                 return (Tt_message)tt_error_pointer( status );
73         }
74         Tttk_op op = tttk_string_op( opname );
75         tt_free( opname );
76         int trust = (    ( geteuid() == tt_message_uid( msg ))
77                       && ( getegid() == tt_message_gid( msg )));
78         int from_me = 0;
79         char *sender = tt_message_sender( msg );
80         char *me = tt_default_procid();
81         if ((tt_ptr_error( sender ) == TT_OK) && (tt_ptr_error(me) == TT_OK)) {
82                 from_me = (strcmp( sender, me ) == 0);
83         }
84         tt_free( sender );
85         tt_free( me );
86         if ((op == TTDT_GET_MODIFIED) && (_tttk_message_am_handling(msg))) {
87                 //
88                 // If our pattern even matches Get_Modified, it is because
89                 // the client called ttdt_file_event(,TTDT_MODIFIED,,).
90                 // Reverting or saving unregisters this pattern, so if
91                 // we even see the question we know the answer is yes.
92                 //
93                 tt_message_arg_ival_set( msg, 1, 1 );
94                 tt_message_reply( msg );
95                 // message destroyed by fuse
96                 return 0;
97         }
98         Ttdt_file_cb _cb = (Ttdt_file_cb)clientCB;
99         msg = (*_cb)( msg, op, file, clientData, trust, from_me );
100         fuse = (caddr_t)0; // prevent message destruction
101         return msg;
102 }
103
104 //
105 // Create the pattern registered at file-join-time
106 //
107 static Tt_pattern
108 _ttDtFileJoinPat(
109         const char     *pathname,
110         Tt_scope        theScope,
111         Ttdt_file_cb    cb,
112         void           *clientData
113 )
114 {
115         Tt_pattern pat = _ttDtPatternCreate( TT_OBSERVE, theScope,
116                                              theScope != TT_FILE, pathname,
117                                              TTDT_DELETED, _ttDtFileCB,
118                                              (void *)cb, clientData, 0 );
119         Tt_status status = tt_ptr_error( pat );
120         if (status != TT_OK) {
121                 return pat;
122         }
123         _TttkItem2Free fuse = pat;
124         tt_pattern_op_add( pat, _ttDtOp( TTDT_MODIFIED ) );
125         tt_pattern_op_add( pat, _ttDtOp( TTDT_REVERTED ) );
126         tt_pattern_op_add( pat, _ttDtOp( TTDT_MOVED ) );
127         tt_pattern_op_add( pat, _ttDtOp( TTDT_SAVED ) );
128         status = tt_pattern_class_add( pat, TT_NOTICE );
129         if (status != TT_OK) {
130                 return (Tt_pattern)tt_error_pointer( status );
131         }
132         status = tt_pattern_arg_add( pat, TT_IN, Tttk_file, 0 );
133         if (status != TT_OK) {
134                 return (Tt_pattern)tt_error_pointer( status );
135         }
136         status = tt_pattern_register( pat );
137         if (status != TT_OK) {
138                 return (Tt_pattern)tt_error_pointer( status );
139         }
140         fuse = (caddr_t)0;
141         return pat;
142 }
143
144 Tt_pattern *
145 ttdt_file_join(
146         const char     *pathname,
147         Tt_scope        theScope,
148         int             join,
149         Ttdt_file_cb    cb,
150         void           *clientData
151 )
152 {
153         //
154         // One pattern to observe notices, one to handle requests, and
155         // a null terminator.
156         //
157         Tt_pattern *pPats = (Tt_pattern *)malloc( 3 * sizeof(Tt_pattern) );
158         if (pPats == 0) {
159                 return (Tt_pattern *)tt_error_pointer( TT_ERR_NOMEM );
160         }
161         _TttkList2Free fuses( 3 );
162         fuses += (caddr_t)pPats;
163         pPats[1] = 0;
164         pPats[2] = 0;
165         Tt_scope toTry = theScope;
166         if (theScope == TT_SCOPE_NONE) {
167                 toTry = TT_BOTH;
168         }
169         pPats[0] = _ttDtFileJoinPat( pathname, toTry, cb, clientData );
170         Tt_status status = tt_ptr_error( pPats[0] );
171         if ((status != TT_OK) && (theScope == TT_SCOPE_NONE)) {
172                 toTry = TT_FILE_IN_SESSION;
173                 pPats[0] = _ttDtFileJoinPat( pathname, toTry, cb, clientData );
174                 status = tt_ptr_error( pPats[0] );
175         }
176         if (status != TT_OK) {
177                 return (Tt_pattern *)tt_error_pointer( status );
178         }
179         fuses += pPats[0];
180         TtDtFileJoinInfo *info = (TtDtFileJoinInfo *)
181                                  malloc( sizeof( TtDtFileJoinInfo ));
182         if (info == 0) {
183                 return (Tt_pattern *)tt_error_pointer( TT_DESKTOP_ENOMEM );
184         }
185         fuses += (caddr_t)info;
186         info->pathname = _tt_strdup( pathname );
187         info->theScope = toTry;
188         info->cb       = cb;
189         info->clientData = clientData;
190         status = tt_pattern_user_set( pPats[0], _TttkJoinInfoKey, info );
191         if (status != TT_OK) {
192                 return (Tt_pattern *)tt_error_pointer( status );
193         }
194         if (join) {
195                 status = tt_file_join( pathname );
196                 if (status != TT_OK) {
197                         return (Tt_pattern *)tt_error_pointer( status );
198                 }
199         }
200         fuses.flush();
201         return pPats;
202 }
203
204 Tt_message
205 ttdt_file_notice(
206         Tt_message      context,
207         Tttk_op         op,
208         Tt_scope        theScope,
209         const char     *file,
210         int             sendAndDestroy
211 )
212 {
213         if ((op == TTDT_MOVED) && sendAndDestroy) {
214                 return (Tt_message)tt_error_pointer( TT_DESKTOP_EINVAL );
215         }
216         Tt_message msg = tttk_message_create( context, TT_NOTICE, theScope, 0,
217                                               _ttDtOp( op ), 0 );
218         Tt_status status = tt_ptr_error( msg );
219         if (status != TT_OK) {
220                 return msg;
221         }
222         //
223         // Guarantees that msg will be destroyed when this function returns
224         //
225         _TttkItem2Free fuse = msg;
226         status = tt_message_file_set( msg, file );
227         if (status != TT_OK) {
228                 return (Tt_message)tt_error_pointer( status );
229         }
230         status = tt_message_arg_add( msg, TT_IN, Tttk_file, 0 );
231         if (status != TT_OK) {
232                 return (Tt_message)tt_error_pointer( status );
233         }
234         if (sendAndDestroy) {
235                 status = tt_message_send( msg );
236                 if (status != TT_OK) {
237                         return (Tt_message)tt_error_pointer( status );
238                 }
239                 return 0;
240         }
241         fuse = (caddr_t)0;
242         return msg;
243 }
244
245 Tt_message
246 ttdt_file_request(
247         Tt_message      context,
248         Tttk_op         op,
249         Tt_scope        theScope,
250         const char     *file,
251         Ttdt_file_cb    cb,
252         void           *clientData,
253         int             send
254 )
255 {
256         Tt_message msg = _ttDtPMessageCreate( context, TT_REQUEST, theScope, 0,
257                                               op, _ttDtFileCB, (void *)cb, clientData);
258         Tt_status status = tt_ptr_error( msg );
259         if (status != TT_OK) {
260                 return msg;
261         }
262         _TttkItem2Free fuse = msg;
263         status = tt_message_file_set( msg, file );
264         if (status != TT_OK) {
265                 return (Tt_message)tt_error_pointer( status );
266         }
267         status = tt_message_arg_add( msg, TT_IN, Tttk_file, 0 );
268         if (status != TT_OK) {
269                 return (Tt_message)tt_error_pointer( status );
270         }
271         if (op == TTDT_GET_MODIFIED) {
272                 status = tt_message_arg_add( msg, TT_OUT, Tttk_boolean, 0 );
273                 if (status != TT_OK) {
274                         return (Tt_message)tt_error_pointer( status );
275                 }
276         }
277         if (send) {
278                 status = tt_message_send( msg );
279                 if (status != TT_OK) {
280                         return (Tt_message)tt_error_pointer( status );
281                 }
282         }
283         fuse = (caddr_t)0;
284         return msg;
285 }
286
287 //
288 // Create the pattern registered at file-event-time
289 //
290 static Tt_pattern
291 _ttDtFileEventPat(
292         const char     *pathname,
293         Tt_scope        theScope,
294         Ttdt_file_cb    cb,
295         void           *clientData
296 )
297 {
298         Tt_pattern pat = _ttDtPatternCreate( TT_HANDLE, theScope,
299                                              theScope != TT_FILE, pathname,
300                                              TTDT_SAVE, _ttDtFileCB,
301                                              (void *)cb, clientData, 0 );
302         Tt_status status = tt_ptr_error( pat );
303         if (status != TT_OK) {
304                 return pat;
305         }
306         _TttkItem2Free fuse = pat;
307         tt_pattern_op_add( pat, _ttDtOp( TTDT_GET_MODIFIED ) );
308         tt_pattern_op_add( pat, _ttDtOp( TTDT_REVERT ) );
309         status = tt_pattern_class_add( pat, TT_REQUEST );
310         if (status != TT_OK) {
311                 return (Tt_pattern)tt_error_pointer( status );
312         }
313         status = tt_pattern_arg_add( pat, TT_IN, Tttk_file, 0 );
314         if (status != TT_OK) {
315                 return (Tt_pattern)tt_error_pointer( status );
316         }
317         status = tt_pattern_register( pat );
318         if (status != TT_OK) {
319                 return (Tt_pattern)tt_error_pointer( status );
320         }
321         fuse = (caddr_t)0;
322         return pat;
323 }
324
325 Tt_status
326 ttdt_file_event(
327         Tt_message      context,
328         Tttk_op         event,
329         Tt_pattern     *pats,
330         int             send
331 )
332 {
333         if ((pats == 0) || (tt_ptr_error( pats ) != TT_OK)) {
334                 return TT_ERR_POINTER;
335         }
336         TtDtFileJoinInfo *info = (TtDtFileJoinInfo *)
337                                  tt_pattern_user( pats[0], _TttkJoinInfoKey );
338         Tt_status status = tt_ptr_error( info );
339         if ((status != TT_OK) || (info == 0)) {
340                 return status;
341         }
342         if (event == TTDT_MODIFIED) {
343                 pats[1] = _ttDtFileEventPat( info->pathname, info->theScope,
344                                              info->cb, info->clientData );
345                 Tt_status status = tt_ptr_error( pats[1] );
346                 if (status != TT_OK) {
347                         // Leave pats[1] as an error pointer
348                         return status;
349                 }
350         }
351         if (send) {
352                 Tt_message msg = ttdt_file_notice( context, event,
353                                         info->theScope, info->pathname, 1 );
354                 status = tt_ptr_error( msg );
355                 if (status != TT_OK) {
356                         return status;
357                 }
358         }
359         if ((event == TTDT_SAVED) || (event == TTDT_REVERTED)) {
360                 status = tt_pattern_unregister( pats[1] );
361                 if (status != TT_OK) {
362                         return status;
363                 }
364         }
365         return TT_OK;
366 }
367
368 Tt_status
369 ttdt_file_quit(
370         Tt_pattern     *pats,
371         int             quit
372 )
373 {
374         if ((pats == 0) || (tt_ptr_error( pats ) != TT_OK)) {
375                 return TT_ERR_POINTER;
376         }
377         TtDtFileJoinInfo *info = (TtDtFileJoinInfo *)
378                                  tt_pattern_user( pats[0], _TttkJoinInfoKey );
379         Tt_status status = tt_ptr_error( info );
380         if ((status != TT_OK) || (info == 0)) {
381                 return status;
382         }
383         status = _tttk_patterns_destroy( pats );
384         if (quit) {
385                 if (info->pathname != 0) {
386                         status = tt_file_quit( info->pathname );
387                         tt_free( (char *)info->pathname );
388                 }
389                 free( info );
390         }
391         return status;
392 }
393
394 //
395 // Parse Get_Modified reply and set *(int *)clientData
396 //
397 static Tt_message
398 _ttDtGetModifiedCB(
399         Tt_message      msg,
400         Tttk_op         ,
401         char           *pathName,
402         void           *clientData,
403         int             ,
404         int
405 )
406 {
407         tt_free( pathName );
408         if (! _tttk_message_in_final_state( msg )) {
409                 // Not in final state; our address space is probably handler
410                 return msg;
411         }
412         *(int *)clientData = 0; // assume not modified
413         if (tt_message_state(msg) == TT_HANDLED) {
414                 int modified;
415                 Tt_status status = tt_message_arg_ival( msg, 1, &modified );
416                 if (status != TT_OK) {
417                         tttk_message_destroy( msg );
418                         return 0;
419                 }
420                 if (modified) {
421                         *(int *)clientData = -1;
422                 }
423         }
424         tttk_message_destroy( msg );
425         return 0;
426 }
427
428 int
429 ttdt_Get_Modified(
430         Tt_message      context,
431         const char     *pathname,
432         Tt_scope        theScope,
433         XtAppContext    app2run,
434         int             msTimeOut
435 )
436 {
437         int waiting = 1;
438         Tt_message msg = ttdt_file_request( context, TTDT_GET_MODIFIED,
439                                             theScope, pathname,
440                                             _ttDtGetModifiedCB, &waiting, 1 );
441         Tt_status status = tt_ptr_error( msg );
442         if (status != TT_OK) {
443                 return 0;
444         }
445         status = tttk_block_while( app2run, &waiting, msTimeOut );
446         if (status == TT_DESKTOP_ETIMEDOUT) {
447                 //
448                 // _ttDtGetModifiedCB() would have destroyed msg,
449                 // but this removes the callback.  Now, any
450                 // late-breaking reply will pop out of
451                 // tt_message_receive().  Well-behaved applications
452                 // should do [what is done by] tttk_message_abandon(),
453                 // but poorly-written apps might panic. XXX
454                 //
455                 tttk_message_destroy( msg );
456         }
457         return (waiting < 0);
458 }
459
460 //
461 // Private Ttdt_file_cb hung on requests (Get_Modified, Save, Revert)
462 // that we send synchronously.
463 //
464 static Tt_message
465 _ttDtFileRequestCB(
466         Tt_message      msg,
467         Tttk_op         ,
468         char           *pathName,
469         void           *clientData,
470         int             ,
471         int
472 )
473 {
474         tt_free( pathName );
475         return _ttTkNoteReplyStatus( msg, 0, 0, clientData );
476 }
477
478 Tt_status
479 _ttdt_file_request(
480         Tttk_op         op,
481         Tt_message      context,
482         const char     *pathname,
483         Tt_scope        theScope,
484         XtAppContext    app2run,
485         int             msTimeOut
486 )
487 {
488         int result;
489         Tt_message msg = ttdt_file_request( context, op,
490                                             theScope, pathname,
491                                             _ttDtFileRequestCB, &result, 1 );
492         Tt_status status = tt_ptr_error( msg );
493         if (status != TT_OK) {
494                 return status;
495         }
496         result = 1;
497         status = tttk_block_while( app2run, &result, msTimeOut );
498         if (status == TT_DESKTOP_ETIMEDOUT) {
499                 tttk_message_destroy( msg );
500                 return status;
501         }
502         return (Tt_status)-result;
503 }
504
505 Tt_status
506 ttdt_Save(
507         Tt_message      context,
508         const char     *pathname,
509         Tt_scope        theScope,
510         XtAppContext    app2run,
511         int             msTimeOut
512 )
513 {
514         return _ttdt_file_request( TTDT_SAVE, context, pathname, theScope,
515                                    app2run, msTimeOut );
516 }
517
518 Tt_status
519 ttdt_Revert(
520         Tt_message      context,
521         const char     *pathname,
522         Tt_scope        theScope,
523         XtAppContext    app2run,
524         int             msTimeOut
525 )
526 {
527         return _ttdt_file_request( TTDT_REVERT, context, pathname, theScope,
528                                    app2run, msTimeOut );
529 }