(file) Return to InheritanceTree.cpp CVS log (file) (dir) Up to [Pegasus] / pegasus / src / Pegasus / Repository

  1 karl  1.35 //%2006////////////////////////////////////////////////////////////////////////
  2 mike  1.16 //
  3 karl  1.27 // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development
  4            // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems.
  5            // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.;
  6 karl  1.23 // IBM Corp.; EMC Corporation, The Open Group.
  7 karl  1.27 // Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.;
  8            // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group.
  9 karl  1.29 // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
 10            // EMC Corporation; VERITAS Software Corporation; The Open Group.
 11 karl  1.35 // Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.;
 12            // EMC Corporation; Symantec Corporation; The Open Group.
 13 mike  1.16 //
 14            // Permission is hereby granted, free of charge, to any person obtaining a copy
 15 kumpf 1.20 // of this software and associated documentation files (the "Software"), to
 16            // deal in the Software without restriction, including without limitation the
 17            // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 18 mike  1.16 // sell copies of the Software, and to permit persons to whom the Software is
 19            // furnished to do so, subject to the following conditions:
 20            // 
 21 kumpf 1.20 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
 22 mike  1.16 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
 23            // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 24 kumpf 1.20 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 25            // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 26            // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 27 mike  1.16 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 28            // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 29            //
 30            //==============================================================================
 31            //
 32            //%/////////////////////////////////////////////////////////////////////////////
 33            
 34            #include <Pegasus/Common/String.h>
 35            #include <Pegasus/Common/HashTable.h>
 36            #include <Pegasus/Common/Dir.h>
 37 kumpf 1.22 #include <Pegasus/Common/XmlWriter.h>
 38 chuck 1.24 #include <Pegasus/Common/CommonUTF.h>
 39 r.kieninger 1.33 #include <Pegasus/Common/CIMNameUnchecked.h>
 40 mike        1.16 #include "InheritanceTree.h"
 41                  
 42 schuur      1.25 #if 0
 43                  #undef PEG_METHOD_ENTER
 44                  #undef PEG_METHOD_EXIT
 45                  #define PEG_METHOD_ENTER(x,y)  cout<<"--- Enter: "<<y<<endl;
 46                  #define PEG_METHOD_EXIT()
 47                  #endif
 48                  
 49 mike        1.16 PEGASUS_NAMESPACE_BEGIN
 50                  
 51                  PEGASUS_USING_STD;
 52                  
 53                  ////////////////////////////////////////////////////////////////////////////////
 54                  //
 55                  // NoCaseEqualFunc
 56                  //
 57                  ////////////////////////////////////////////////////////////////////////////////
 58                  
 59                  struct NoCaseEqualFunc
 60                  {
 61                      static Boolean equal(const String& x, const String& y)
 62                      {
 63                          return String::equalNoCase(x, y);
 64                      }
 65                  };
 66                  
 67                  ////////////////////////////////////////////////////////////////////////////////
 68                  //
 69 schuur      1.25 // InheritanceTreeRep
 70                  //
 71                  ////////////////////////////////////////////////////////////////////////////////
 72                  
 73 marek       1.26 struct InheritanceTreeNode;
 74 schuur      1.25 
 75                  struct InheritanceTreeRep
 76                  {
 77 mike        1.32     typedef HashTable<
 78 kumpf       1.37         String, InheritanceTreeNode*, NoCaseEqualFunc, HashLowerCaseFunc> Table;
 79 schuur      1.25     Table table;
 80 mike        1.32 
 81                      // Tradeoff: chosing a larger value decreases hash lookup time but
 82                      // increases iteration (which seems to be the dominant operations).
 83                      // This power of two (256) seems to produce the best results.
 84                  
 85                      InheritanceTreeRep() : table(256)
 86                      {
 87                      }
 88 schuur      1.25 };
 89                  
 90                  ////////////////////////////////////////////////////////////////////////////////
 91                  //
 92 mike        1.16 // InheritanceTreeNode
 93                  //
 94                  ////////////////////////////////////////////////////////////////////////////////
 95                  
 96 schuur      1.25 class NameSpace;
 97                  
 98                  struct InheritanceTreeExt
 99                  {
100                      InheritanceTreeExt(NameSpace* t, InheritanceTreeNode* itn)
101                         : tag(t), node(itn) {}
102                      NameSpace* tag;
103                      InheritanceTreeNode* node;
104                  };
105                  
106                  
107 mike        1.16 struct InheritanceTreeNode
108                  {
109 kumpf       1.22     InheritanceTreeNode(const CIMName& className);
110 mike        1.16 
111                      void addSubClass(InheritanceTreeNode* subClass);
112                  
113                      Boolean removeSubClass(InheritanceTreeNode* subClass);
114                  
115                      void getSubClassNames(
116 kumpf       1.37         Array<CIMName>& subClassNames,
117                          Boolean deepInheritance,
118                          NameSpace* tag = 0);
119 mike        1.16 
120 kumpf       1.22     void getSuperClassNames(Array<CIMName>& superClassNames);
121 mike        1.16 
122                      void print(PEGASUS_STD(ostream)& os) const;
123 schuur      1.25 #if 0
124 kumpf       1.22     Boolean isSubClass(const CIMName& className) const;
125 schuur      1.25 #endif
126 kumpf       1.22     CIMName className;
127 mike        1.16     InheritanceTreeNode* superClass;
128                      InheritanceTreeNode* sibling;
129 schuur      1.25     union {
130 mike        1.16     InheritanceTreeNode* subClasses;
131 schuur      1.25        Array<InheritanceTreeExt*>* extNodes;
132                      };
133 mike        1.16     Boolean provisional;
134 schuur      1.25     Boolean extension;
135 mike        1.16 };
136                  
137 kumpf       1.37 InheritanceTreeNode::InheritanceTreeNode(const CIMName& className)
138                      : className(className), superClass(0),
139 schuur      1.25     sibling(0), subClasses(0), provisional(true), extension(false)
140 mike        1.16 {
141                  }
142                  
143                  void InheritanceTreeNode::addSubClass(InheritanceTreeNode* subClass)
144                  {
145                      subClass->superClass = this;
146                      subClass->sibling = subClasses;
147                      subClasses = subClass;
148                  }
149                  
150                  Boolean InheritanceTreeNode::removeSubClass(InheritanceTreeNode* subClass)
151                  {
152                      InheritanceTreeNode* prev = 0;
153                  
154                      for (InheritanceTreeNode* p = subClasses; p; p = p->sibling)
155                      {
156 kumpf       1.37         if (p == subClass)
157                          {
158                              if (prev)
159                                  prev->sibling = subClass->sibling;
160                              else
161                                  subClasses = subClass->sibling;
162                  
163                              return true;
164                          }
165                          prev = p;
166 mike        1.16     }
167                  
168                      return false;
169                  }
170                  
171                  void InheritanceTreeNode::getSubClassNames(
172 schuur      1.25     Array<CIMName>& subClassNames,
173                      Boolean deepInheritance,
174 kumpf       1.37     NameSpace* ns)
175 mike        1.16 {
176                      // For each subClass:
177                  
178 kumpf       1.37     for (InheritanceTreeNode* p = subClasses; p; p = p->sibling)
179                      {
180                          if (p->extension)
181                          {
182                              for (int j = 0, m = p->extNodes->size(); j < m; j++)
183                              {
184                                  InheritanceTreeExt* itx = (*(p->extNodes))[j];
185                                  subClassNames.append(p->className);
186                                  if (!ns)
187                                  {
188                                      InheritanceTreeNode* itn=itx->node;
189                                      itn->getSubClassNames(subClassNames, deepInheritance, ns);
190                                  }
191                                  else if (itx->tag == ns)
192                                  {
193                                      InheritanceTreeNode* itn=itx->node;
194                                      itn->getSubClassNames(subClassNames, deepInheritance, ns);
195                                      break;
196                                  }
197                              }
198                          }
199 kumpf       1.37         else
200                          {
201                              subClassNames.append(p->className);
202                              if (deepInheritance)
203                              {
204                                  p->getSubClassNames(subClassNames, true, ns);
205                              }
206                          }
207 mike        1.16     }
208                  }
209                  
210 schuur      1.25 #if 0
211 kumpf       1.22 Boolean InheritanceTreeNode::isSubClass(const CIMName& className_) const
212 mike        1.16 {
213 kumpf       1.22     if (className.equal (className_))
214 kumpf       1.37         return true;
215 mike        1.16 
216                      for (InheritanceTreeNode* p = subClasses; p; p = p->sibling)
217                      {
218 kumpf       1.37         if (p->className.equal (className_))
219                              return true;
220 mike        1.16     }
221                  
222                      return false;
223                  }
224 schuur      1.25 #endif
225 mike        1.16 
226 kumpf       1.22 void InheritanceTreeNode::getSuperClassNames(Array<CIMName>& superClassNames)
227 mike        1.16 {
228                      // For each superClass:
229                  
230                      for (InheritanceTreeNode* p = superClass; p; p = p->superClass)
231                      {
232 kumpf       1.37         superClassNames.append(p->className);
233                          // p->getSuperClassNames(superClassNames);
234 mike        1.16     }
235                  }
236                  
237                  void InheritanceTreeNode::print(PEGASUS_STD(ostream)& os) const
238                  {
239                      os << className << " : " ;
240 kumpf       1.22     os << (superClass ? superClass->className : CIMName ());
241 mike        1.16 
242                      os << " { ";
243                  
244                      for (InheritanceTreeNode* p = subClasses; p; p = p->sibling)
245 kumpf       1.37         os << p->className << ' ';
246 mike        1.16 
247                      os << "}" << endl;
248                  }
249                  
250                  ////////////////////////////////////////////////////////////////////////////////
251                  //
252                  // InheritanceTree
253                  //
254                  ////////////////////////////////////////////////////////////////////////////////
255                  
256                  InheritanceTree::InheritanceTree()
257                  {
258                      _rep = new InheritanceTreeRep;
259                  }
260                  
261                  InheritanceTree::~InheritanceTree()
262                  {
263 sage        1.18     for (InheritanceTreeRep::Table::Iterator i = _rep->table.start(); i; i++)
264                          delete i.value();
265                  
266 mike        1.16     delete _rep;
267                  }
268 schuur      1.25 void InheritanceTree::insert(
269                      const String& className,
270                      const String& superClassName,
271                      InheritanceTree& parentTree,
272 kumpf       1.37     NameSpace* tag)
273 schuur      1.25 {
274                      InheritanceTreeNode* superClassNode = 0;
275                  
276 kumpf       1.37     if (superClassName.size() &&
277                          !parentTree._rep->table.lookup(superClassName, superClassNode))
278                      {
279                          superClassNode =
280                              new InheritanceTreeNode(CIMNameUnchecked(superClassName));
281                          parentTree._rep->table.insert(superClassName, superClassNode);
282 schuur      1.25     }
283                  
284                      InheritanceTreeNode* extNode = 0;
285                  
286 kumpf       1.37     if (!parentTree._rep->table.lookup(className, extNode))
287                      {
288                          extNode = new InheritanceTreeNode(CIMNameUnchecked(className));
289                          parentTree._rep->table.insert(className, extNode);
290                          extNode->extension=true;
291                          extNode->extNodes=new Array<InheritanceTreeExt*>;
292 schuur      1.25     }
293                  
294                      extNode->provisional = false;
295                  
296                      if (superClassNode)
297 kumpf       1.37         superClassNode->addSubClass(extNode);
298 schuur      1.25 
299                      InheritanceTreeNode* classNode = 0;
300                  
301 kumpf       1.37     if (!_rep->table.lookup(className, classNode))
302                      {
303                          classNode = new InheritanceTreeNode(className);
304                          _rep->table.insert(className, classNode);
305 schuur      1.25     }
306                  
307                      extNode->extNodes->append(new InheritanceTreeExt(tag,classNode));
308                  
309                      classNode->superClass = superClassNode;
310 karl        1.31     /* temp comment out this code from bug 3352.  See bug 3498 for reason
311 kumpf       1.37     if (extNode)
312 vijay.eli   1.30     {
313 kumpf       1.37        for (int i=0, m=extNode->extNodes->size(); i < m; i++)
314                            if ((*extNode->extNodes)[i])
315 karl        1.31             delete (*(extNode->extNodes))[i];
316 vijay.eli   1.30        delete extNode;
317 kumpf       1.37     }
318 karl        1.31     extNode = NULL;*/
319                  
320 schuur      1.25 }
321 mike        1.16 
322                  void InheritanceTree::insert(
323 kumpf       1.37     const String& className,
324 mike        1.16     const String& superClassName)
325                  {
326                      // ATTN: need found flag!
327                  
328                      // -- Insert superclass:
329                  
330                      InheritanceTreeNode* superClassNode = 0;
331                  
332 kumpf       1.37     if (superClassName.size() &&
333                          !_rep->table.lookup(superClassName, superClassNode))
334 mike        1.16     {
335 kumpf       1.37         superClassNode =
336                              new InheritanceTreeNode(CIMNameUnchecked(superClassName));
337                          _rep->table.insert(superClassName, superClassNode);
338 mike        1.16     }
339                  
340                      // -- Insert class:
341 kumpf       1.37 
342 mike        1.16     InheritanceTreeNode* classNode = 0;
343                  
344                      if (!_rep->table.lookup(className, classNode))
345                      {
346 kumpf       1.37         classNode = new InheritanceTreeNode(CIMNameUnchecked(className));
347                          _rep->table.insert(className, classNode);
348 mike        1.16     }
349                  
350                      classNode->provisional = false;
351                  
352                      // -- Link the class and superclass nodes:
353                  
354                      if (superClassNode)
355 kumpf       1.37         superClassNode->addSubClass(classNode);
356 mike        1.16 }
357                  
358 schuur      1.25 void InheritanceTree::insertFromPath(const String& path,
359                        InheritanceTree* parentTree,
360 kumpf       1.37       NameSpace* ns)
361 mike        1.16 {
362                      for (Dir dir(path); dir.more(); dir.next())
363                      {
364 kumpf       1.37         String fileName = dir.getName();
365 mike        1.16 
366 kumpf       1.37         // Ignore the current and parent directories.
367 mike        1.16 
368 kumpf       1.37         if (fileName == "." || fileName == "..")
369                              continue;
370 mike        1.16 
371 kumpf       1.37         Uint32 dot = fileName.find('.');
372 mike        1.16 
373 kumpf       1.37         // Ignore files without dots in them:
374 mike        1.16 
375 kumpf       1.37         if (dot == PEG_NOT_FOUND)
376                              continue;
377 mike        1.16 
378 kumpf       1.37         String className = fileName.subString(0, dot);
379                          String superClassName = fileName.subString(dot + 1);
380 mike        1.16 
381 kumpf       1.37         if (superClassName == "#")
382                              superClassName.clear();
383 mike        1.16 
384 bafna.mukesh 1.28 
385 chuck        1.24 #ifdef PEGASUS_REPOSITORY_ESCAPE_UTF8
386 kumpf        1.37         if (ns)
387                               insert(
388                                   escapeStringDecoder(className),
389                                   escapeStringDecoder(superClassName),
390                                   *parentTree, ns);
391                           else
392                               insert(
393                                   escapeStringDecoder(className),
394                                   escapeStringDecoder(superClassName));
395 chuck        1.24 #else
396 kumpf        1.37         if (ns)
397                               insert(className, superClassName, *parentTree, ns);
398                           else
399                               insert(className, superClassName);
400 chuck        1.24 #endif
401 mike         1.16     }
402                   }
403                   
404                   void InheritanceTree::check() const
405                   {
406                       for (InheritanceTreeRep::Table::Iterator i = _rep->table.start(); i; i++)
407                       {
408 kumpf        1.37         if (i.value()->provisional)
409                               throw InvalidInheritanceTree(i.value()->className.getString());
410 mike         1.16     }
411                   }
412                   
413                   Boolean InheritanceTree::getSubClassNames(
414 kumpf        1.22     const CIMName& className,
415 mike         1.16     Boolean deepInheritance,
416 schuur       1.25     Array<CIMName>& subClassNames,
417 kumpf        1.37     NameSpace* ns) const
418 mike         1.16 {
419 kumpf        1.37     // -- Case 1: className is empty: get all class names (if deepInheritance)
420                       // -- or just root class names (if not deepInheritance).
421 mike         1.16 
422 kumpf        1.37     if (className.isNull())
423                       {
424                           for (InheritanceTreeRep::Table::Iterator i = _rep->table.start();i;i++)
425                           {
426                               InheritanceTreeNode* itn=i.value();
427                               if (itn->extension)
428                               {
429                                   if (!ns)
430                                       continue;
431                                   for (int j=0,m=itn->extNodes->size(); j<m; j++)
432                                   {
433                                       InheritanceTreeExt* itx=(*(itn->extNodes))[j];
434                                       if (itx->tag==ns)
435                                       {
436                                           InheritanceTreeNode* itn=itx->node;
437                                           if (deepInheritance)
438                                           {
439                                               subClassNames.append(CIMNameUnchecked(i.key()));
440                                               itn->getSubClassNames(
441                                                   subClassNames, deepInheritance, ns);
442                                           }
443 kumpf        1.37                         else if (!i.value()->superClass)
444                                               subClassNames.append(CIMNameUnchecked(i.key()));
445                                           break;
446                                       }
447                                   }
448                               }
449                               else if (deepInheritance)
450                               {
451                                   // Append all classes:
452                                   subClassNames.append(CIMNameUnchecked(i.key()));
453                               }
454                               else if (!i.value()->superClass)
455                               {
456                                   // Just append root classes:
457                                   subClassNames.append(CIMNameUnchecked(i.key()));
458                               }
459                           }
460                           return true;
461                       }
462 schuur       1.25 
463 kumpf        1.37     // -- Case 2: className non-empty: get names of classes descendent from
464                       // -- the given class.
465 schuur       1.25 
466 kumpf        1.37     for (InheritanceTreeRep::Table::Iterator i = _rep->table.start(); i; i++)
467                       {
468                           if (className.equal (CIMNameUnchecked(i.key())))
469                           {
470                               i.value()->getSubClassNames(subClassNames, deepInheritance, ns);
471                               return true;
472                           }
473                       }
474 schuur       1.25 
475 kumpf        1.37     // Not found!
476                       return false;
477 mike         1.16 }
478 kumpf        1.37 
479 schuur       1.25 #if 0
480 mike         1.17 Boolean InheritanceTree::isSubClass(
481 kumpf        1.37     const CIMName& class1,
482 kumpf        1.22     const CIMName& class2) const
483 mike         1.16 {
484 kumpf        1.37     InheritanceTreeNode* node = 0;
485 mike         1.16 
486 kumpf        1.22     if (!_rep->table.lookup(class1.getString(), node))
487 kumpf        1.37         return false;
488 mike         1.16 
489 kumpf        1.22     return node->isSubClass(class2.getString());
490 mike         1.16 }
491 schuur       1.25 #endif
492 mike         1.16 
493                   Boolean InheritanceTree::getSuperClassNames(
494 kumpf        1.22     const CIMName& className,
495                       Array<CIMName>& superClassNames) const
496 mike         1.16 {
497                       InheritanceTreeNode* classNode;
498                   
499 kumpf        1.22     if (_rep->table.lookup(className.getString(), classNode))
500 mike         1.16     {
501 kumpf        1.37         classNode->getSuperClassNames(superClassNames);
502                           return true;
503 mike         1.16     }
504                   
505                       return false;
506                   }
507                   
508                   Boolean InheritanceTree::getSuperClass(
509 kumpf        1.22     const CIMName& className,
510                       CIMName& superClassName) const
511 mike         1.16 {
512                       InheritanceTreeNode* classNode;
513                   
514 kumpf        1.22     if (_rep->table.lookup(className.getString(), classNode))
515 mike         1.16     {
516 kumpf        1.37         if (classNode->superClass)
517                           {
518                               superClassName = classNode->superClass->className;
519                           }
520                           else
521                           {
522                               superClassName.clear();
523                           }
524 mike         1.16 
525 kumpf        1.37         return true;
526 mike         1.16     }
527                   
528                       return false;
529                   }
530                   
531                   Boolean InheritanceTree::hasSubClasses(
532 kumpf        1.22     const CIMName& className,
533 mike         1.16     Boolean& hasSubClasses) const
534                   {
535 kumpf        1.37     InheritanceTreeNode* node = 0;
536 mike         1.16 
537 kumpf        1.22     if (!_rep->table.lookup(className.getString(), node))
538 kumpf        1.37         return false;
539 mike         1.16 
540                       hasSubClasses = node->subClasses != 0;
541                       return true;
542                   }
543                   
544 kumpf        1.22 Boolean InheritanceTree::containsClass(const CIMName& className) const
545 mike         1.16 {
546 kumpf        1.22     return _rep->table.contains(className.getString());
547 mike         1.16 }
548                   
549 kumpf        1.37 void InheritanceTree::remove(
550                       const CIMName& className,
551                       InheritanceTree& parentTree,
552                       NameSpace* tag)
553 mike         1.16 {
554                       // -- Lookup the node:
555                   
556 kumpf        1.37     InheritanceTreeNode* node = 0;
557 mike         1.16 
558 kumpf        1.22     if (!_rep->table.lookup(className.getString(), node))
559 kumpf        1.37         throw PEGASUS_CIM_EXCEPTION(
560                               CIM_ERR_INVALID_CLASS, className.getString());
561 mike         1.16 
562                       // -- Disallow if is has any subclasses:
563                   
564                       if (node->subClasses)
565 kumpf        1.37         throw PEGASUS_CIM_EXCEPTION(
566                               CIM_ERR_CLASS_HAS_CHILDREN, className.getString());
567 mike         1.16 
568                       // -- Remove as child of superclass:
569                   
570                       InheritanceTreeNode* superClass = node->superClass;
571                   
572 kumpf        1.37     if (tag)
573                       {
574                           InheritanceTreeNode* itn = 0;
575                           if (parentTree._rep->table.lookup(className.getString(), itn))
576                           {
577                               if (itn->extension)
578                               {
579                                   for (int j = 0, m = itn->extNodes->size(); j < m; j++)
580                                   {
581                                       if ((*(itn->extNodes))[j]->tag == tag)
582                                       {
583                                           itn->extNodes->remove(j);
584                                           break;
585                                       }
586                                   }
587                                   if (itn->extNodes->size() == 0)
588                                   {
589                                       delete itn->extNodes;
590                                       parentTree._rep->table.remove(className.getString());
591 schuur       1.25                 }
592 kumpf        1.37             }
593                           }
594                           else
595                           {
596                               Boolean result = superClass->removeSubClass(node);
597                               PEGASUS_ASSERT(result);
598                           }
599 schuur       1.25     }
600                       else if (superClass)
601 mike         1.16     {
602 kumpf        1.37         Boolean result = superClass->removeSubClass(node);
603                           PEGASUS_ASSERT(result);
604 mike         1.16     }
605                   
606 schuur       1.25 
607 mike         1.16     // -- Remove from the hash table and delete:
608                   
609 kumpf        1.22     Boolean result = _rep->table.remove(className.getString());
610 mike         1.16     PEGASUS_ASSERT(result);
611                       delete node;
612                   }
613                   
614                   void InheritanceTree::print(PEGASUS_STD(ostream)& os) const
615                   {
616                       for (InheritanceTreeRep::Table::Iterator i = _rep->table.start(); i; i++)
617 kumpf        1.37         i.value()->print(os);
618 mike         1.16 }
619                   
620                   PEGASUS_NAMESPACE_END

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2