Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <config_features.h>
21 :
22 : #include "cppu/EnvDcp.hxx"
23 :
24 : #include "sal/alloca.h"
25 : #include "sal/log.hxx"
26 : #include "osl/diagnose.h"
27 : #include "osl/interlck.h"
28 : #include "osl/mutex.hxx"
29 : #include "osl/module.h"
30 : #include "osl/process.h"
31 : #include "rtl/process.h"
32 : #include "rtl/string.hxx"
33 : #include "rtl/ustring.hxx"
34 : #include "rtl/ustrbuf.hxx"
35 : #include "rtl/instance.hxx"
36 : #include "typelib/typedescription.h"
37 : #include "uno/dispatcher.h"
38 : #include "uno/environment.h"
39 : #include "uno/lbnames.h"
40 : #include "prim.hxx"
41 : #include "destr.hxx"
42 : #include "loadmodule.hxx"
43 :
44 : #include <boost/unordered_map.hpp>
45 : #include <vector>
46 : #include <stdio.h>
47 :
48 :
49 : using ::rtl::OUString;
50 : using ::rtl::OUStringHash;
51 :
52 : namespace
53 : {
54 :
55 :
56 1315701 : inline static bool td_equals( typelib_InterfaceTypeDescription * pTD1,
57 : typelib_InterfaceTypeDescription * pTD2 )
58 : {
59 2631402 : return (pTD1 == pTD2 ||
60 1100133 : (((typelib_TypeDescription *)pTD1)->pTypeName->length ==
61 67732 : ((typelib_TypeDescription *)pTD2)->pTypeName->length &&
62 : ::rtl_ustr_compare(
63 : ((typelib_TypeDescription *) pTD1)->pTypeName->buffer,
64 1383433 : ((typelib_TypeDescription *) pTD2)->pTypeName->buffer ) == 0));
65 : }
66 :
67 : struct ObjectEntry;
68 : struct uno_DefaultEnvironment;
69 :
70 :
71 : struct InterfaceEntry
72 : {
73 : sal_Int32 refCount;
74 : void * pInterface;
75 : uno_freeProxyFunc fpFreeProxy;
76 : typelib_InterfaceTypeDescription * pTypeDescr;
77 : };
78 :
79 335428 : struct ObjectEntry
80 : {
81 : OUString oid;
82 : sal_Int32 nRef;
83 : ::std::vector< InterfaceEntry > aInterfaces;
84 : bool mixedObject;
85 :
86 : inline ObjectEntry( const OUString & rOId_ );
87 :
88 : inline void append(
89 : uno_DefaultEnvironment * pEnv,
90 : void * pInterface, typelib_InterfaceTypeDescription * pTypeDescr,
91 : uno_freeProxyFunc fpFreeProxy );
92 : inline InterfaceEntry * find(
93 : typelib_InterfaceTypeDescription * pTypeDescr );
94 : inline sal_Int32 find( void * iface_ptr, ::std::size_t pos );
95 : };
96 :
97 :
98 : struct FctPtrHash :
99 : public ::std::unary_function< const void *, ::std::size_t >
100 : {
101 1871328 : ::std::size_t operator () ( const void * pKey ) const
102 1871328 : { return reinterpret_cast< ::std::size_t>( pKey ); }
103 : };
104 :
105 :
106 : // mapping from environment name to environment
107 : typedef ::boost::unordered_map<
108 : OUString, uno_Environment *, OUStringHash > OUString2EnvironmentMap;
109 :
110 : // mapping from ptr to object entry
111 : typedef ::boost::unordered_map<
112 : void *, ObjectEntry *, FctPtrHash,
113 : ::std::equal_to< void * > > Ptr2ObjectMap;
114 : // mapping from oid to object entry
115 : typedef ::boost::unordered_map<
116 : OUString, ObjectEntry *, OUStringHash > OId2ObjectMap;
117 :
118 :
119 :
120 : struct EnvironmentsData
121 : {
122 : ::osl::Mutex mutex;
123 : OUString2EnvironmentMap aName2EnvMap;
124 :
125 642 : EnvironmentsData() : isDisposing(false) {}
126 : ~EnvironmentsData();
127 :
128 : inline void getEnvironment(
129 : uno_Environment ** ppEnv, const OUString & rEnvDcp, void * pContext );
130 : inline void registerEnvironment( uno_Environment ** ppEnv );
131 : inline void getRegisteredEnvironments(
132 : uno_Environment *** pppEnvs, sal_Int32 * pnLen,
133 : uno_memAlloc memAlloc, const OUString & rEnvDcp );
134 :
135 : bool isDisposing;
136 : };
137 :
138 : namespace
139 : {
140 : struct theEnvironmentsData : public rtl::Static< EnvironmentsData, theEnvironmentsData > {};
141 : }
142 :
143 :
144 : struct uno_DefaultEnvironment : public uno_ExtEnvironment
145 : {
146 : sal_Int32 nRef;
147 : sal_Int32 nWeakRef;
148 :
149 : ::osl::Mutex mutex;
150 : Ptr2ObjectMap aPtr2ObjectMap;
151 : OId2ObjectMap aOId2ObjectMap;
152 :
153 : uno_DefaultEnvironment(
154 : const OUString & rEnvDcp_, void * pContext_ );
155 : ~uno_DefaultEnvironment();
156 : };
157 :
158 :
159 337944 : inline ObjectEntry::ObjectEntry( OUString const & rOId_ )
160 : : oid( rOId_ ),
161 : nRef( 0 ),
162 337944 : mixedObject( false )
163 : {
164 337944 : aInterfaces.reserve( 2 );
165 337944 : }
166 :
167 :
168 419840 : inline void ObjectEntry::append(
169 : uno_DefaultEnvironment * pEnv,
170 : void * pInterface, typelib_InterfaceTypeDescription * pTypeDescr,
171 : uno_freeProxyFunc fpFreeProxy )
172 : {
173 : InterfaceEntry aNewEntry;
174 419840 : if (! fpFreeProxy)
175 200109 : (*pEnv->acquireInterface)( pEnv, pInterface );
176 419840 : aNewEntry.refCount = 1;
177 419840 : aNewEntry.pInterface = pInterface;
178 419840 : aNewEntry.fpFreeProxy = fpFreeProxy;
179 419840 : typelib_typedescription_acquire( (typelib_TypeDescription *) pTypeDescr );
180 419840 : aNewEntry.pTypeDescr = pTypeDescr;
181 :
182 : ::std::pair< Ptr2ObjectMap::iterator, bool > i(
183 : pEnv->aPtr2ObjectMap.insert( Ptr2ObjectMap::value_type(
184 419840 : pInterface, this ) ) );
185 : SAL_WARN_IF(
186 : !i.second && (find(pInterface, 0) == -1 || i.first->second != this),
187 : "cppu",
188 : "map already contains " << i.first->second << " != " << this << " for "
189 : << pInterface);
190 419840 : aInterfaces.push_back( aNewEntry );
191 419840 : }
192 :
193 :
194 459339 : inline InterfaceEntry * ObjectEntry::find(
195 : typelib_InterfaceTypeDescription * pTypeDescr_ )
196 : {
197 : OSL_ASSERT( ! aInterfaces.empty() );
198 459339 : if (aInterfaces.empty())
199 0 : return 0;
200 :
201 : // shortcut common case:
202 : OUString const & type_name =
203 : OUString::unacquired(
204 459339 : &((typelib_TypeDescription *) pTypeDescr_)->pTypeName );
205 459339 : if ( type_name == "com.sun.star.uno.XInterface" )
206 : {
207 71828 : return &aInterfaces[ 0 ];
208 : }
209 :
210 387511 : ::std::size_t nSize = aInterfaces.size();
211 919150 : for ( ::std::size_t nPos = 0; nPos < nSize; ++nPos )
212 : {
213 : typelib_InterfaceTypeDescription * pITD =
214 747207 : aInterfaces[ nPos ].pTypeDescr;
215 2594547 : while (pITD)
216 : {
217 1315701 : if (td_equals( pITD, pTypeDescr_ ))
218 215568 : return &aInterfaces[ nPos ];
219 1100133 : pITD = pITD->pBaseTypeDescription;
220 : }
221 : }
222 171943 : return 0;
223 : }
224 :
225 :
226 13136 : inline sal_Int32 ObjectEntry::find(
227 : void * iface_ptr, ::std::size_t pos )
228 : {
229 13136 : ::std::size_t size = aInterfaces.size();
230 21756 : for ( ; pos < size; ++pos )
231 : {
232 15786 : if (aInterfaces[ pos ].pInterface == iface_ptr)
233 7166 : return pos;
234 : }
235 5970 : return -1;
236 : }
237 :
238 : extern "C"
239 : {
240 :
241 :
242 311351 : static void SAL_CALL defenv_registerInterface(
243 : uno_ExtEnvironment * pEnv, void ** ppInterface,
244 : rtl_uString * pOId, typelib_InterfaceTypeDescription * pTypeDescr )
245 : {
246 : OSL_ENSURE( pEnv && ppInterface && pOId && pTypeDescr, "### null ptr!" );
247 311351 : OUString const & rOId = OUString::unacquired( &pOId );
248 :
249 : uno_DefaultEnvironment * that =
250 311351 : static_cast< uno_DefaultEnvironment * >( pEnv );
251 311351 : ::osl::ClearableMutexGuard guard( that->mutex );
252 :
253 : // try to insert dummy 0:
254 : std::pair<OId2ObjectMap::iterator, bool> const insertion(
255 311351 : that->aOId2ObjectMap.insert( OId2ObjectMap::value_type( rOId, (ObjectEntry*)0 ) ) );
256 311351 : if (insertion.second)
257 : {
258 165222 : ObjectEntry * pOEntry = new ObjectEntry( rOId );
259 165222 : insertion.first->second = pOEntry;
260 165222 : ++pOEntry->nRef; // another register call on object
261 165222 : pOEntry->append( that, *ppInterface, pTypeDescr, 0 );
262 : }
263 : else // object entry exists
264 : {
265 146129 : ObjectEntry * pOEntry = insertion.first->second;
266 146129 : ++pOEntry->nRef; // another register call on object
267 146129 : InterfaceEntry * pIEntry = pOEntry->find( pTypeDescr );
268 :
269 146129 : if (pIEntry) // type entry exists
270 : {
271 111242 : ++pIEntry->refCount;
272 111242 : if (pIEntry->pInterface != *ppInterface)
273 : {
274 0 : void * pInterface = pIEntry->pInterface;
275 0 : (*pEnv->acquireInterface)( pEnv, pInterface );
276 0 : guard.clear();
277 0 : (*pEnv->releaseInterface)( pEnv, *ppInterface );
278 0 : *ppInterface = pInterface;
279 : }
280 : }
281 : else
282 : {
283 34887 : pOEntry->append( that, *ppInterface, pTypeDescr, 0 );
284 : }
285 311351 : }
286 311351 : }
287 :
288 :
289 226070 : static void SAL_CALL defenv_registerProxyInterface(
290 : uno_ExtEnvironment * pEnv, void ** ppInterface, uno_freeProxyFunc freeProxy,
291 : rtl_uString * pOId, typelib_InterfaceTypeDescription * pTypeDescr )
292 : {
293 : OSL_ENSURE( pEnv && ppInterface && pOId && pTypeDescr && freeProxy,
294 : "### null ptr!" );
295 226070 : OUString const & rOId = OUString::unacquired( &pOId );
296 :
297 : uno_DefaultEnvironment * that =
298 226070 : static_cast< uno_DefaultEnvironment * >( pEnv );
299 226070 : ::osl::ClearableMutexGuard guard( that->mutex );
300 :
301 : // try to insert dummy 0:
302 : std::pair<OId2ObjectMap::iterator, bool> const insertion(
303 226070 : that->aOId2ObjectMap.insert( OId2ObjectMap::value_type( rOId, (ObjectEntry*)0 ) ) );
304 226070 : if (insertion.second)
305 : {
306 172722 : ObjectEntry * pOEntry = new ObjectEntry( rOId );
307 172722 : insertion.first->second = pOEntry;
308 172722 : ++pOEntry->nRef; // another register call on object
309 172722 : pOEntry->append( that, *ppInterface, pTypeDescr, freeProxy );
310 : }
311 : else // object entry exists
312 : {
313 53348 : ObjectEntry * pOEntry = insertion.first->second;
314 :
315 : // first registration was an original, then registerProxyInterface():
316 : pOEntry->mixedObject |=
317 106696 : (!pOEntry->aInterfaces.empty() &&
318 106696 : pOEntry->aInterfaces[ 0 ].fpFreeProxy == 0);
319 :
320 53348 : ++pOEntry->nRef; // another register call on object
321 53348 : InterfaceEntry * pIEntry = pOEntry->find( pTypeDescr );
322 :
323 53348 : if (pIEntry) // type entry exists
324 : {
325 6339 : if (pIEntry->pInterface == *ppInterface)
326 : {
327 5903 : ++pIEntry->refCount;
328 : }
329 : else
330 : {
331 436 : void * pInterface = pIEntry->pInterface;
332 436 : (*pEnv->acquireInterface)( pEnv, pInterface );
333 436 : --pOEntry->nRef; // manual revoke of proxy to be freed
334 436 : guard.clear();
335 436 : (*freeProxy)( pEnv, *ppInterface );
336 436 : *ppInterface = pInterface;
337 : }
338 : }
339 : else
340 : {
341 47009 : pOEntry->append( that, *ppInterface, pTypeDescr, freeProxy );
342 : }
343 226070 : }
344 226070 : }
345 :
346 :
347 531881 : static void SAL_CALL s_stub_defenv_revokeInterface(va_list * pParam)
348 : {
349 531881 : uno_ExtEnvironment * pEnv = va_arg(*pParam, uno_ExtEnvironment *);
350 531881 : void * pInterface = va_arg(*pParam, void *);
351 :
352 : OSL_ENSURE( pEnv && pInterface, "### null ptr!" );
353 : uno_DefaultEnvironment * that =
354 531881 : static_cast< uno_DefaultEnvironment * >( pEnv );
355 531881 : ::osl::ClearableMutexGuard guard( that->mutex );
356 :
357 : Ptr2ObjectMap::const_iterator const iFind(
358 531881 : that->aPtr2ObjectMap.find( pInterface ) );
359 : OSL_ASSERT( iFind != that->aPtr2ObjectMap.end() );
360 531881 : ObjectEntry * pOEntry = iFind->second;
361 531881 : if (! --pOEntry->nRef)
362 : {
363 : // cleanup maps
364 335428 : that->aOId2ObjectMap.erase( pOEntry->oid );
365 : sal_Int32 nPos;
366 1079748 : for ( nPos = pOEntry->aInterfaces.size(); nPos--; )
367 : {
368 408892 : that->aPtr2ObjectMap.erase( pOEntry->aInterfaces[nPos].pInterface );
369 : }
370 :
371 : // the last proxy interface of the environment might kill this
372 : // environment, because of releasing its language binding!!!
373 335428 : guard.clear();
374 :
375 : // release interfaces
376 1079748 : for ( nPos = pOEntry->aInterfaces.size(); nPos--; )
377 : {
378 408892 : InterfaceEntry const & rEntry = pOEntry->aInterfaces[nPos];
379 : typelib_typedescription_release(
380 408892 : (typelib_TypeDescription *) rEntry.pTypeDescr );
381 408892 : if (rEntry.fpFreeProxy) // is proxy or used interface?
382 : {
383 210461 : (*rEntry.fpFreeProxy)( pEnv, rEntry.pInterface );
384 : }
385 : else
386 : {
387 198431 : (*pEnv->releaseInterface)( pEnv, rEntry.pInterface );
388 : }
389 : }
390 :
391 335428 : delete pOEntry;
392 : }
393 196453 : else if (pOEntry->mixedObject)
394 : {
395 : OSL_ASSERT( !pOEntry->aInterfaces.empty() &&
396 : pOEntry->aInterfaces[ 0 ].fpFreeProxy == 0 );
397 :
398 7166 : sal_Int32 index = pOEntry->find( pInterface, 1 );
399 : OSL_ASSERT( index > 0 );
400 7166 : if (index > 0)
401 : {
402 7166 : InterfaceEntry & entry = pOEntry->aInterfaces[ index ];
403 : OSL_ASSERT( entry.pInterface == pInterface );
404 7166 : if (entry.fpFreeProxy != 0)
405 : {
406 5970 : --entry.refCount;
407 5970 : if (entry.refCount == 0)
408 : {
409 5970 : uno_freeProxyFunc fpFreeProxy = entry.fpFreeProxy;
410 : typelib_TypeDescription * pTypeDescr =
411 : reinterpret_cast< typelib_TypeDescription * >(
412 5970 : entry.pTypeDescr );
413 :
414 : pOEntry->aInterfaces.erase(
415 5970 : pOEntry->aInterfaces.begin() + index );
416 5970 : if (pOEntry->find( pInterface, index ) < 0)
417 : {
418 : // proxy ptr not registered for another interface:
419 : // remove from ptr map
420 : #if OSL_DEBUG_LEVEL > 0
421 : ::std::size_t erased =
422 : #endif
423 5970 : that->aPtr2ObjectMap.erase( pInterface );
424 : OSL_ASSERT( erased == 1 );
425 : }
426 :
427 5970 : guard.clear();
428 :
429 5970 : typelib_typedescription_release( pTypeDescr );
430 5970 : (*fpFreeProxy)( pEnv, pInterface );
431 : }
432 : }
433 : }
434 531881 : }
435 531881 : }
436 :
437 531881 : static void SAL_CALL defenv_revokeInterface(uno_ExtEnvironment * pEnv, void * pInterface)
438 : {
439 531881 : uno_Environment_invoke(&pEnv->aBase, s_stub_defenv_revokeInterface, pEnv, pInterface);
440 531881 : }
441 :
442 :
443 504781 : static void SAL_CALL defenv_getObjectIdentifier(
444 : uno_ExtEnvironment * pEnv, rtl_uString ** ppOId, void * pInterface )
445 : {
446 : OSL_ENSURE( pEnv && ppOId && pInterface, "### null ptr!" );
447 504781 : if (*ppOId)
448 : {
449 175888 : ::rtl_uString_release( *ppOId );
450 175888 : *ppOId = 0;
451 : }
452 :
453 : uno_DefaultEnvironment * that =
454 504781 : static_cast< uno_DefaultEnvironment * >( pEnv );
455 504781 : ::osl::ClearableMutexGuard guard( that->mutex );
456 :
457 : Ptr2ObjectMap::const_iterator const iFind(
458 504781 : that->aPtr2ObjectMap.find( pInterface ) );
459 504781 : if (iFind == that->aPtr2ObjectMap.end())
460 : {
461 199998 : guard.clear();
462 199998 : (*pEnv->computeObjectIdentifier)( pEnv, ppOId, pInterface );
463 : }
464 : else
465 : {
466 304783 : rtl_uString * hstr = iFind->second->oid.pData;
467 304783 : rtl_uString_acquire( hstr );
468 304783 : *ppOId = hstr;
469 504781 : }
470 504781 : }
471 :
472 :
473 435636 : static void SAL_CALL defenv_getRegisteredInterface(
474 : uno_ExtEnvironment * pEnv, void ** ppInterface,
475 : rtl_uString * pOId, typelib_InterfaceTypeDescription * pTypeDescr )
476 : {
477 : OSL_ENSURE( pEnv && ppInterface && pOId && pTypeDescr, "### null ptr!" );
478 435636 : if (*ppInterface)
479 : {
480 0 : (*pEnv->releaseInterface)( pEnv, *ppInterface );
481 0 : *ppInterface = 0;
482 : }
483 :
484 435636 : OUString const & rOId = OUString::unacquired( &pOId );
485 : uno_DefaultEnvironment * that =
486 435636 : static_cast< uno_DefaultEnvironment * >( pEnv );
487 435636 : ::osl::MutexGuard guard( that->mutex );
488 :
489 : OId2ObjectMap::const_iterator const iFind
490 435636 : ( that->aOId2ObjectMap.find( rOId ) );
491 435636 : if (iFind != that->aOId2ObjectMap.end())
492 : {
493 259862 : InterfaceEntry const * pIEntry = iFind->second->find( pTypeDescr );
494 259862 : if (pIEntry)
495 : {
496 169815 : (*pEnv->acquireInterface)( pEnv, pIEntry->pInterface );
497 169815 : *ppInterface = pIEntry->pInterface;
498 : }
499 435636 : }
500 435636 : }
501 :
502 :
503 0 : static void SAL_CALL defenv_getRegisteredInterfaces(
504 : uno_ExtEnvironment * pEnv, void *** pppInterfaces, sal_Int32 * pnLen,
505 : uno_memAlloc memAlloc )
506 : {
507 : assert(pEnv && pppInterfaces && pnLen && memAlloc && "### null ptr!");
508 : uno_DefaultEnvironment * that =
509 0 : static_cast< uno_DefaultEnvironment * >( pEnv );
510 0 : ::osl::MutexGuard guard( that->mutex );
511 :
512 0 : sal_Int32 nLen = that->aPtr2ObjectMap.size();
513 0 : sal_Int32 nPos = 0;
514 0 : void ** ppInterfaces = (void **) (*memAlloc)( nLen * sizeof (void *) );
515 :
516 0 : Ptr2ObjectMap::const_iterator iPos( that->aPtr2ObjectMap.begin() );
517 0 : Ptr2ObjectMap::const_iterator const iEnd( that->aPtr2ObjectMap.end() );
518 0 : while (iPos != iEnd)
519 : {
520 0 : (*pEnv->acquireInterface)( pEnv, ppInterfaces[nPos++] = (*iPos).first );
521 0 : ++iPos;
522 : }
523 :
524 0 : *pppInterfaces = ppInterfaces;
525 0 : *pnLen = nLen;
526 0 : }
527 :
528 :
529 281494 : static void SAL_CALL defenv_acquire( uno_Environment * pEnv )
530 : {
531 281494 : uno_DefaultEnvironment * that = (uno_DefaultEnvironment *)pEnv;
532 281494 : osl_atomic_increment( &that->nWeakRef );
533 281494 : osl_atomic_increment( &that->nRef );
534 281494 : }
535 :
536 :
537 427044 : static void SAL_CALL defenv_release( uno_Environment * pEnv )
538 : {
539 427044 : uno_DefaultEnvironment * that = (uno_DefaultEnvironment *)pEnv;
540 427044 : if (! osl_atomic_decrement( &that->nRef ))
541 : {
542 : // invoke dispose callback
543 17889 : if (pEnv->environmentDisposing)
544 : {
545 2606 : (*pEnv->environmentDisposing)( pEnv );
546 : }
547 :
548 : OSL_ENSURE( that->aOId2ObjectMap.empty(), "### object entries left!" );
549 : }
550 : // free memory if no weak refs left
551 427044 : if (! osl_atomic_decrement( &that->nWeakRef ))
552 : {
553 4 : delete that;
554 : }
555 427044 : }
556 :
557 :
558 18340 : static void SAL_CALL defenv_acquireWeak( uno_Environment * pEnv )
559 : {
560 18340 : uno_DefaultEnvironment * that = (uno_DefaultEnvironment *)pEnv;
561 18340 : osl_atomic_increment( &that->nWeakRef );
562 18340 : }
563 :
564 :
565 18340 : static void SAL_CALL defenv_releaseWeak( uno_Environment * pEnv )
566 : {
567 18340 : uno_DefaultEnvironment * that = (uno_DefaultEnvironment *)pEnv;
568 18340 : if (! osl_atomic_decrement( &that->nWeakRef ))
569 : {
570 17885 : delete that;
571 : }
572 18340 : }
573 :
574 :
575 182082 : static void SAL_CALL defenv_harden(
576 : uno_Environment ** ppHardEnv, uno_Environment * pEnv )
577 : {
578 182082 : if (*ppHardEnv)
579 : {
580 0 : (*(*ppHardEnv)->release)( *ppHardEnv );
581 0 : *ppHardEnv = 0;
582 : }
583 :
584 182082 : EnvironmentsData & rData = theEnvironmentsData::get();
585 :
586 182082 : if (rData.isDisposing)
587 967 : return;
588 :
589 181115 : uno_DefaultEnvironment * that = (uno_DefaultEnvironment *)pEnv;
590 : {
591 181115 : ::osl::MutexGuard guard( rData.mutex );
592 181115 : if (1 == osl_atomic_increment( &that->nRef )) // is dead
593 : {
594 34746 : that->nRef = 0;
595 34746 : return;
596 146369 : }
597 : }
598 146369 : osl_atomic_increment( &that->nWeakRef );
599 146369 : *ppHardEnv = pEnv;
600 : }
601 :
602 :
603 0 : static void SAL_CALL defenv_dispose( SAL_UNUSED_PARAMETER uno_Environment * )
604 : {
605 0 : }
606 : }
607 :
608 :
609 18344 : uno_DefaultEnvironment::uno_DefaultEnvironment(
610 : const OUString & rEnvDcp_, void * pContext_ )
611 : : nRef( 0 ),
612 18344 : nWeakRef( 0 )
613 : {
614 18344 : uno_Environment * that = reinterpret_cast< uno_Environment * >(this);
615 18344 : that->pReserved = 0;
616 : // functions
617 18344 : that->acquire = defenv_acquire;
618 18344 : that->release = defenv_release;
619 18344 : that->acquireWeak = defenv_acquireWeak;
620 18344 : that->releaseWeak = defenv_releaseWeak;
621 18344 : that->harden = defenv_harden;
622 18344 : that->dispose = defenv_dispose;
623 18344 : that->pExtEnv = this;
624 : // identifier
625 18344 : ::rtl_uString_acquire( rEnvDcp_.pData );
626 18344 : that->pTypeName = rEnvDcp_.pData;
627 18344 : that->pContext = pContext_;
628 :
629 : // will be late initialized
630 18344 : that->environmentDisposing = 0;
631 :
632 18344 : uno_ExtEnvironment::registerInterface = defenv_registerInterface;
633 18344 : uno_ExtEnvironment::registerProxyInterface = defenv_registerProxyInterface;
634 18344 : uno_ExtEnvironment::revokeInterface = defenv_revokeInterface;
635 18344 : uno_ExtEnvironment::getObjectIdentifier = defenv_getObjectIdentifier;
636 18344 : uno_ExtEnvironment::getRegisteredInterface = defenv_getRegisteredInterface;
637 : uno_ExtEnvironment::getRegisteredInterfaces =
638 18344 : defenv_getRegisteredInterfaces;
639 :
640 18344 : }
641 :
642 :
643 35778 : uno_DefaultEnvironment::~uno_DefaultEnvironment()
644 : {
645 17889 : ::rtl_uString_release( ((uno_Environment *) this)->pTypeName );
646 17889 : }
647 :
648 :
649 0 : static void writeLine(
650 : void * stream, const sal_Char * pLine, const sal_Char * pFilter )
651 : {
652 0 : if (pFilter && *pFilter)
653 : {
654 : // lookup pFilter in pLine
655 0 : while (*pLine)
656 : {
657 0 : if (*pLine == *pFilter)
658 : {
659 0 : sal_Int32 nPos = 1;
660 0 : while (pLine[nPos] && pFilter[nPos] == pLine[nPos])
661 : {
662 0 : ++nPos;
663 : }
664 0 : if (! pFilter[nPos])
665 : {
666 0 : if (stream)
667 : {
668 0 : fprintf( (FILE *) stream, "%s\n", pLine );
669 : }
670 : else
671 : {
672 : OSL_TRACE( "%s", pLine );
673 : }
674 : }
675 : }
676 0 : ++pLine;
677 0 : }
678 : }
679 : else
680 : {
681 0 : if (stream)
682 : {
683 0 : fprintf( (FILE *) stream, "%s\n", pLine );
684 : }
685 : else
686 : {
687 0 : fprintf( stderr, "%s\n", pLine );
688 : }
689 : }
690 0 : }
691 :
692 :
693 0 : static void writeLine(
694 : void * stream, const OUString & rLine, const sal_Char * pFilter )
695 : {
696 : ::rtl::OString aLine( ::rtl::OUStringToOString(
697 0 : rLine, RTL_TEXTENCODING_ASCII_US ) );
698 0 : writeLine( stream, aLine.getStr(), pFilter );
699 0 : }
700 :
701 :
702 0 : extern "C" CPPU_DLLPUBLIC void SAL_CALL uno_dumpEnvironment(
703 : void * stream, uno_Environment * pEnv, const sal_Char * pFilter )
704 : SAL_THROW_EXTERN_C()
705 : {
706 : OSL_ENSURE( pEnv, "### null ptr!" );
707 0 : ::rtl::OUStringBuffer buf;
708 :
709 0 : if (! pEnv->pExtEnv)
710 : {
711 : writeLine( stream, "###################################"
712 0 : "###########################################", pFilter );
713 0 : buf.append( "environment: " );
714 0 : buf.append( pEnv->pTypeName );
715 0 : writeLine( stream, buf.makeStringAndClear(), pFilter );
716 0 : writeLine( stream, "NO INTERFACE INFORMATION AVAILABLE!", pFilter );
717 0 : return;
718 : }
719 :
720 : writeLine( stream, "########################################"
721 0 : "######################################", pFilter );
722 0 : buf.append( "environment dump: " );
723 0 : buf.append( pEnv->pTypeName );
724 0 : writeLine( stream, buf.makeStringAndClear(), pFilter );
725 :
726 : uno_DefaultEnvironment * that =
727 0 : reinterpret_cast< uno_DefaultEnvironment * >(pEnv);
728 0 : ::osl::MutexGuard guard( that->mutex );
729 :
730 0 : Ptr2ObjectMap ptr2obj( that->aPtr2ObjectMap );
731 0 : OId2ObjectMap::const_iterator iPos( that->aOId2ObjectMap.begin() );
732 0 : while (iPos != that->aOId2ObjectMap.end())
733 : {
734 0 : ObjectEntry * pOEntry = iPos->second;
735 :
736 0 : buf.append( "+ " );
737 0 : if (pOEntry->mixedObject)
738 0 : buf.append( "mixed " );
739 0 : buf.append( "object entry: nRef=" );
740 0 : buf.append( pOEntry->nRef, 10 );
741 0 : buf.append( "; oid=\"" );
742 0 : buf.append( pOEntry->oid );
743 0 : buf.append( '\"' );
744 0 : writeLine( stream, buf.makeStringAndClear(), pFilter );
745 :
746 0 : for ( ::std::size_t nPos = 0;
747 0 : nPos < pOEntry->aInterfaces.size(); ++nPos )
748 : {
749 0 : const InterfaceEntry & rIEntry = pOEntry->aInterfaces[nPos];
750 :
751 0 : buf.append( " - " );
752 : buf.append(
753 0 : ((typelib_TypeDescription *) rIEntry.pTypeDescr)->pTypeName );
754 0 : if (rIEntry.fpFreeProxy)
755 : {
756 0 : buf.append( "; proxy free=0x" );
757 : buf.append(
758 0 : reinterpret_cast< sal_IntPtr >(rIEntry.fpFreeProxy), 16 );
759 : }
760 : else
761 : {
762 0 : buf.append( "; original" );
763 : }
764 0 : buf.append( "; ptr=0x" );
765 : buf.append(
766 0 : reinterpret_cast< sal_IntPtr >(rIEntry.pInterface), 16 );
767 :
768 0 : if (pOEntry->find( rIEntry.pInterface, nPos + 1 ) < 0)
769 : {
770 0 : ::std::size_t erased = ptr2obj.erase( rIEntry.pInterface );
771 0 : if (erased != 1)
772 : {
773 0 : buf.append( " (ptr not found in map!)" );
774 : }
775 : }
776 0 : writeLine( stream, buf.makeStringAndClear(), pFilter );
777 : }
778 0 : ++iPos;
779 : }
780 0 : if (! ptr2obj.empty())
781 0 : writeLine( stream, "ptr map inconsistency!!!", pFilter );
782 : writeLine( stream, "#####################################"
783 0 : "#########################################", pFilter );
784 : }
785 :
786 :
787 0 : extern "C" CPPU_DLLPUBLIC void SAL_CALL uno_dumpEnvironmentByName(
788 : void * stream, rtl_uString * pEnvDcp, const sal_Char * pFilter )
789 : SAL_THROW_EXTERN_C()
790 : {
791 0 : uno_Environment * pEnv = 0;
792 0 : uno_getEnvironment( &pEnv, pEnvDcp, 0 );
793 0 : if (pEnv)
794 : {
795 0 : ::uno_dumpEnvironment( stream, pEnv, pFilter );
796 0 : (*pEnv->release)( pEnv );
797 : }
798 : else
799 : {
800 0 : ::rtl::OUStringBuffer buf( 32 );
801 0 : buf.append( "environment \"" );
802 0 : buf.append( pEnvDcp );
803 0 : buf.append( "\" does not exist!" );
804 0 : writeLine( stream, buf.makeStringAndClear(), pFilter );
805 : }
806 0 : }
807 :
808 : namespace
809 : {
810 281 : class makeOIdPart
811 : {
812 : private:
813 : OUString m_sOidPart;
814 : public:
815 281 : makeOIdPart()
816 281 : {
817 281 : ::rtl::OUStringBuffer aRet( 64 );
818 281 : aRet.append( "];" );
819 : // pid
820 : oslProcessInfo info;
821 281 : info.Size = sizeof(oslProcessInfo);
822 281 : if (::osl_getProcessInfo( 0, osl_Process_IDENTIFIER, &info ) ==
823 : osl_Process_E_None)
824 : {
825 281 : aRet.append( (sal_Int64)info.Ident, 16 );
826 : }
827 : else
828 : {
829 0 : aRet.append( "unknown process id" );
830 : }
831 : // good guid
832 : sal_uInt8 ar[16];
833 281 : ::rtl_getGlobalProcessId( ar );
834 281 : aRet.append( ';' );
835 4777 : for ( sal_Int32 i = 0; i < 16; ++i )
836 4496 : aRet.append( (sal_Int32)ar[i], 16 );
837 :
838 281 : m_sOidPart = aRet.makeStringAndClear();
839 281 : }
840 34528 : const OUString& getOIdPart() const { return m_sOidPart; }
841 : };
842 :
843 : class theStaticOIdPart : public rtl::Static<makeOIdPart, theStaticOIdPart> {};
844 : }
845 :
846 :
847 34528 : inline static const OUString & unoenv_getStaticOIdPart()
848 : {
849 34528 : return theStaticOIdPart::get().getOIdPart();
850 : }
851 :
852 : extern "C"
853 : {
854 :
855 :
856 34528 : static void SAL_CALL unoenv_computeObjectIdentifier(
857 : uno_ExtEnvironment * pEnv, rtl_uString ** ppOId, void * pInterface )
858 : {
859 : assert(pEnv && ppOId && pInterface && "### null ptr!");
860 34528 : if (*ppOId)
861 : {
862 0 : ::rtl_uString_release( *ppOId );
863 0 : *ppOId = 0;
864 : }
865 :
866 : uno_Interface * pUnoI = (uno_Interface *)
867 : ::cppu::binuno_queryInterface(
868 : pInterface, *typelib_static_type_getByTypeClass(
869 34528 : typelib_TypeClass_INTERFACE ) );
870 34528 : if (0 != pUnoI)
871 : {
872 34528 : (*pUnoI->release)( pUnoI );
873 : // interface
874 34528 : ::rtl::OUStringBuffer oid( 64 );
875 34528 : oid.append( reinterpret_cast< sal_Int64 >(pUnoI), 16 );
876 34528 : oid.append( ';' );
877 : // environment[context]
878 34528 : oid.append( ((uno_Environment *) pEnv)->pTypeName );
879 34528 : oid.append( '[' );
880 : oid.append( reinterpret_cast< sal_Int64 >(
881 : reinterpret_cast<
882 34528 : uno_Environment * >(pEnv)->pContext ), 16 );
883 : // process;good guid
884 34528 : oid.append( unoenv_getStaticOIdPart() );
885 69056 : OUString aStr( oid.makeStringAndClear() );
886 69056 : ::rtl_uString_acquire( *ppOId = aStr.pData );
887 : }
888 34528 : }
889 :
890 :
891 109211 : static void SAL_CALL unoenv_acquireInterface(
892 : SAL_UNUSED_PARAMETER uno_ExtEnvironment *, void * pUnoI_ )
893 : {
894 109211 : uno_Interface * pUnoI = reinterpret_cast< uno_Interface * >(pUnoI_);
895 109211 : (*pUnoI->acquire)( pUnoI );
896 109211 : }
897 :
898 :
899 35300 : static void SAL_CALL unoenv_releaseInterface(
900 : SAL_UNUSED_PARAMETER uno_ExtEnvironment *, void * pUnoI_ )
901 : {
902 35300 : uno_Interface * pUnoI = reinterpret_cast< uno_Interface * >(pUnoI_);
903 35300 : (*pUnoI->release)( pUnoI );
904 35300 : }
905 : }
906 :
907 :
908 1284 : EnvironmentsData::~EnvironmentsData()
909 : {
910 642 : ::osl::MutexGuard guard( mutex );
911 642 : isDisposing = true;
912 :
913 4827 : for ( OUString2EnvironmentMap::const_iterator iPos( aName2EnvMap.begin() );
914 3218 : iPos != aName2EnvMap.end(); ++iPos )
915 : {
916 967 : uno_Environment * pWeak = iPos->second;
917 967 : uno_Environment * pHard = 0;
918 967 : (*pWeak->harden)( &pHard, pWeak );
919 967 : (*pWeak->releaseWeak)( pWeak );
920 :
921 967 : if (pHard)
922 : {
923 : #if OSL_DEBUG_LEVEL > 1
924 : ::uno_dumpEnvironment( 0, pHard, 0 );
925 : #endif
926 0 : (*pHard->dispose)( pHard ); // send explicit dispose
927 0 : (*pHard->release)( pHard );
928 : }
929 642 : }
930 642 : }
931 :
932 :
933 164709 : inline void EnvironmentsData::getEnvironment(
934 : uno_Environment ** ppEnv, const OUString & rEnvDcp, void * pContext )
935 : {
936 164709 : if (*ppEnv)
937 : {
938 0 : (*(*ppEnv)->release)( *ppEnv );
939 0 : *ppEnv = 0;
940 : }
941 :
942 : OUString aKey(
943 164709 : OUString::number( reinterpret_cast< sal_IntPtr >(pContext) ) );
944 164709 : aKey += rEnvDcp;
945 :
946 : // try to find registered mapping
947 : OUString2EnvironmentMap::const_iterator const iFind(
948 164709 : aName2EnvMap.find( aKey ) );
949 164709 : if (iFind != aName2EnvMap.end())
950 : {
951 163742 : uno_Environment * pWeak = iFind->second;
952 163742 : (*pWeak->harden)( ppEnv, pWeak );
953 164709 : }
954 164709 : }
955 :
956 :
957 18340 : inline void EnvironmentsData::registerEnvironment( uno_Environment ** ppEnv )
958 : {
959 : OSL_ENSURE( ppEnv, "### null ptr!" );
960 18340 : uno_Environment * pEnv = *ppEnv;
961 :
962 : OUString aKey(
963 18340 : OUString::number( reinterpret_cast< sal_IntPtr >(pEnv->pContext) ) );
964 18340 : aKey += pEnv->pTypeName;
965 :
966 : // try to find registered environment
967 : OUString2EnvironmentMap::const_iterator const iFind(
968 18340 : aName2EnvMap.find( aKey ) );
969 18340 : if (iFind == aName2EnvMap.end())
970 : {
971 967 : (*pEnv->acquireWeak)( pEnv );
972 : ::std::pair< OUString2EnvironmentMap::iterator, bool > insertion (
973 : aName2EnvMap.insert(
974 967 : OUString2EnvironmentMap::value_type( aKey, pEnv ) ) );
975 : SAL_WARN_IF( !insertion.second, "cppu", "key " << aKey << " already in env map" );
976 : }
977 : else
978 : {
979 17373 : uno_Environment * pHard = 0;
980 17373 : uno_Environment * pWeak = iFind->second;
981 17373 : (*pWeak->harden)( &pHard, pWeak );
982 17373 : if (pHard)
983 : {
984 0 : (*pEnv->release)( pEnv );
985 0 : *ppEnv = pHard;
986 : }
987 : else // registered one is dead
988 : {
989 17373 : (*pWeak->releaseWeak)( pWeak );
990 17373 : (*pEnv->acquireWeak)( pEnv );
991 17373 : aName2EnvMap[ aKey ] = pEnv;
992 : }
993 18340 : }
994 18340 : }
995 :
996 :
997 0 : inline void EnvironmentsData::getRegisteredEnvironments(
998 : uno_Environment *** pppEnvs, sal_Int32 * pnLen, uno_memAlloc memAlloc,
999 : const OUString & rEnvDcp )
1000 : {
1001 : assert(pppEnvs && pnLen && memAlloc && "### null ptr!");
1002 :
1003 : // max size
1004 0 : uno_Environment ** ppFound = (uno_Environment **)alloca(
1005 : sizeof(uno_Environment *) * aName2EnvMap.size() );
1006 0 : sal_Int32 nSize = 0;
1007 :
1008 : // find matching environment
1009 0 : for ( OUString2EnvironmentMap::const_iterator iPos( aName2EnvMap.begin() );
1010 0 : iPos != aName2EnvMap.end(); ++iPos )
1011 : {
1012 0 : uno_Environment * pWeak = iPos->second;
1013 0 : if (rEnvDcp.isEmpty() ||
1014 0 : rEnvDcp.equals( pWeak->pTypeName ))
1015 : {
1016 0 : ppFound[nSize] = 0;
1017 0 : (*pWeak->harden)( &ppFound[nSize], pWeak );
1018 0 : if (ppFound[nSize])
1019 0 : ++nSize;
1020 : }
1021 : }
1022 :
1023 0 : *pnLen = nSize;
1024 0 : if (nSize)
1025 : {
1026 : *pppEnvs = (uno_Environment **) (*memAlloc)(
1027 0 : sizeof (uno_Environment *) * nSize );
1028 : OSL_ASSERT( *pppEnvs );
1029 0 : while (nSize--)
1030 : {
1031 0 : (*pppEnvs)[nSize] = ppFound[nSize];
1032 : }
1033 : }
1034 : else
1035 : {
1036 0 : *pppEnvs = 0;
1037 : }
1038 0 : }
1039 :
1040 2995 : static bool loadEnv(OUString const & cLibStem,
1041 : uno_Environment * pEnv)
1042 : {
1043 : #ifdef DISABLE_DYNLOADING
1044 : uno_initEnvironmentFunc fpInit;
1045 :
1046 : if ( cLibStem == CPPU_CURRENT_LANGUAGE_BINDING_NAME "_uno" )
1047 : fpInit = CPPU_ENV_uno_initEnvironment;
1048 : #if HAVE_FEATURE_JAVA
1049 : else if ( cLibStem == "java_uno" )
1050 : fpInit = java_uno_initEnvironment;
1051 : #endif
1052 : else
1053 : {
1054 : #if OSL_DEBUG_LEVEL > 1
1055 : OSL_TRACE( "%s: Unhandled env: %s", __PRETTY_FUNCTION__, OUStringToOString( cLibStem, RTL_TEXTENCODING_ASCII_US).getStr() );
1056 : #endif
1057 : return false;
1058 : }
1059 : #else
1060 : // late init with some code from matching uno language binding
1061 : // will be unloaded by environment
1062 2995 : oslModule hMod = cppu::detail::loadModule( cLibStem );
1063 :
1064 2995 : if (!hMod)
1065 0 : return false;
1066 :
1067 2995 : OUString aSymbolName(UNO_INIT_ENVIRONMENT);
1068 : uno_initEnvironmentFunc fpInit = (uno_initEnvironmentFunc)
1069 2995 : ::osl_getFunctionSymbol( hMod, aSymbolName.pData );
1070 :
1071 2995 : if (!fpInit)
1072 : {
1073 0 : ::osl_unloadModule( hMod );
1074 0 : return false;
1075 : }
1076 : #endif
1077 :
1078 2995 : (*fpInit)( pEnv ); // init of environment
1079 2995 : return true;
1080 : }
1081 :
1082 :
1083 : extern "C"
1084 : {
1085 :
1086 :
1087 18344 : static uno_Environment * initDefaultEnvironment(
1088 : const OUString & rEnvDcp, void * pContext )
1089 : {
1090 18344 : uno_Environment * pEnv = &(new uno_DefaultEnvironment( rEnvDcp, pContext ))->aBase;
1091 18344 : (*pEnv->acquire)( pEnv );
1092 :
1093 18344 : OUString envTypeName = cppu::EnvDcp::getTypeName(rEnvDcp);
1094 :
1095 : // create default environment
1096 18344 : if ( envTypeName == UNO_LB_UNO )
1097 : {
1098 15353 : uno_DefaultEnvironment * that = (uno_DefaultEnvironment *)pEnv;
1099 15353 : that->computeObjectIdentifier = unoenv_computeObjectIdentifier;
1100 15353 : that->acquireInterface = unoenv_acquireInterface;
1101 15353 : that->releaseInterface = unoenv_releaseInterface;
1102 :
1103 15353 : OUString envPurpose = cppu::EnvDcp::getPurpose(rEnvDcp);
1104 15353 : if (!envPurpose.isEmpty())
1105 : {
1106 4 : rtl::OUString libStem = envPurpose.copy(envPurpose.lastIndexOf(':') + 1);
1107 4 : libStem += rtl::OUString("_uno_uno");
1108 :
1109 4 : if(!loadEnv(libStem, pEnv))
1110 : {
1111 0 : pEnv->release(pEnv);
1112 0 : return NULL;
1113 4 : }
1114 15353 : }
1115 : }
1116 : else
1117 : {
1118 : // late init with some code from matching uno language binding
1119 2991 : ::rtl::OUStringBuffer aLibName( 16 );
1120 2991 : aLibName.append( envTypeName );
1121 2991 : aLibName.append( "_uno" );
1122 5982 : OUString aStr( aLibName.makeStringAndClear() );
1123 :
1124 2991 : if (!loadEnv(aStr, pEnv))
1125 : {
1126 0 : pEnv->release(pEnv);
1127 0 : return NULL;
1128 2991 : }
1129 : }
1130 :
1131 18344 : return pEnv;
1132 : }
1133 :
1134 :
1135 4 : CPPU_DLLPUBLIC void SAL_CALL uno_createEnvironment(
1136 : uno_Environment ** ppEnv, rtl_uString * pEnvDcp, void * pContext )
1137 : SAL_THROW_EXTERN_C()
1138 : {
1139 : assert(ppEnv && "### null ptr!");
1140 4 : if (*ppEnv)
1141 0 : (*(*ppEnv)->release)( *ppEnv );
1142 :
1143 4 : OUString const & rEnvDcp = OUString::unacquired( &pEnvDcp );
1144 4 : *ppEnv = initDefaultEnvironment( rEnvDcp, pContext );
1145 4 : }
1146 :
1147 164709 : CPPU_DLLPUBLIC void SAL_CALL uno_getEnvironment(
1148 : uno_Environment ** ppEnv, rtl_uString * pEnvDcp, void * pContext )
1149 : SAL_THROW_EXTERN_C()
1150 : {
1151 : assert(ppEnv && "### null ptr!");
1152 164709 : OUString const & rEnvDcp = OUString::unacquired( &pEnvDcp );
1153 :
1154 164709 : EnvironmentsData & rData = theEnvironmentsData::get();
1155 :
1156 164709 : ::osl::MutexGuard guard( rData.mutex );
1157 164709 : rData.getEnvironment( ppEnv, rEnvDcp, pContext );
1158 164709 : if (! *ppEnv)
1159 : {
1160 18340 : *ppEnv = initDefaultEnvironment( rEnvDcp, pContext );
1161 18340 : if (*ppEnv)
1162 : {
1163 : // register new environment:
1164 18340 : rData.registerEnvironment( ppEnv );
1165 : }
1166 164709 : }
1167 164709 : }
1168 :
1169 0 : CPPU_DLLPUBLIC void SAL_CALL uno_getRegisteredEnvironments(
1170 : uno_Environment *** pppEnvs, sal_Int32 * pnLen, uno_memAlloc memAlloc,
1171 : rtl_uString * pEnvDcp )
1172 : SAL_THROW_EXTERN_C()
1173 : {
1174 0 : EnvironmentsData & rData = theEnvironmentsData::get();
1175 :
1176 0 : ::osl::MutexGuard guard( rData.mutex );
1177 : rData.getRegisteredEnvironments(
1178 : pppEnvs, pnLen, memAlloc,
1179 0 : (pEnvDcp ? OUString(pEnvDcp) : OUString()) );
1180 0 : }
1181 :
1182 : } // extern "C"
1183 :
1184 : }
1185 :
1186 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|