From 94f0c281a22d4614d62f352896d16d0e691bec76 Mon Sep 17 00:00:00 2001
From: Anders Blomdell <anders.blomdell@control.lth.se>
Date: Mon, 8 Sep 2008 10:08:11 +0100
Subject: [PATCH] Version 2008-09-08 11:08

---
 src/mio/exception.py |   7 +++
 src/mio/installer.py |  58 ++++++++++++++++++-
 src/mio/log.py       |   6 +-
 src/mio/node.py      | 135 +++++++++++++++++++++++++++----------------
 src/mio/parser.py    |   3 +
 5 files changed, 157 insertions(+), 52 deletions(-)

diff --git a/src/mio/exception.py b/src/mio/exception.py
index b831988..bb0ff40 100755
--- a/src/mio/exception.py
+++ b/src/mio/exception.py
@@ -53,6 +53,13 @@ class GroupException(InstallerException):
         self.value_1 = "'%s'" % (decl_1.decl.group[0:] or "")
         self.value_2 = "'%s'" % (decl_2.decl.group[0:] or "")
             
+class DeleteException(InstallerException):
+    def __init__(self, decl_1, decl_2):
+        self._1 = decl_1
+        self._2 = decl_2
+        self.value_1 = "'%s'" % (decl_1.delete)
+        self.value_2 = "'%s'" % (decl_2.delete)
+            
 class TargetException(MioException):
     def __init__(self, group_1, group_2):
         only_in_1 = []
diff --git a/src/mio/installer.py b/src/mio/installer.py
index 01978d2..f83ad4f 100755
--- a/src/mio/installer.py
+++ b/src/mio/installer.py
@@ -29,6 +29,61 @@ class Installer:
         else:
             chain = list(chain)
             chain.append(group)
+            for node in g._children:
+                if node._tag == 'description':
+                    pass
+
+                elif node._tag == 'dependency':
+                    # Dependencies are handled after everything else
+                    pass
+
+                elif node._tag == 'pre':
+                    if node.command[0]:
+                        group.add_pre(command_node(node))
+                    if node.script[0]:
+                        group.add_pre(script_node(node))
+
+                elif node._tag == 'post':
+                    if node.command[0]:
+                        group.add_post(command_node(node))
+                    if node.script[0]:
+                        group.add_post(script_node(node))
+
+                elif node._tag == 'rpm':
+                    rpm = rpm_node(node.name[0], node, group)
+                    if rpm in exclude:
+                        mio.log.log(CHATTY,
+                                    "excluding rpm '%s'" % node.name[0])
+                    else:
+                        group.add_rpm(rpm)
+                 
+                elif node._tag == 'dir':
+                    dir = dir_node(node.name[0], node, group)
+                    if dir in exclude:
+                        mio.log.log(CHATTY,
+                                    "excluding dir '%s'" % node.name[0])
+                    else:
+                        group.add_dir(dir)
+                    
+                elif node._tag == 'symlink':
+                    symlink = symlink_node(node.name[0], node, group)
+                    if symlink in exclude:
+                        mio.log.log(CHATTY,
+                                    "excluding symlink '%s'" % node.name[0])
+                    else:
+                        group.add_symlink(symlink)
+                    
+                elif node._tag == 'file':
+                    file = file_node(node.name[0], node, group)
+                    if file in exclude:
+                        mio.log.log(CHATTY,
+                                    "excluding file '%s'" % node.name[0])
+                    else:
+                        group.add_file(file)
+
+                else:
+                    raise Exception("unknown tag '%s'" % node._tag)
+            """
             for a in g._pre_:
                 # Add pre actions
                 if a.command[0]:
@@ -75,7 +130,8 @@ class Installer:
                     mio.log.log(CHATTY, "excluding file '%s'" % target.name[0])
                 else:
                     group.add_file(file)
-
+            """
+                    
             if group in self.group:
                 (old_group, old_chain) = self.group[group][0]
                 try:
diff --git a/src/mio/log.py b/src/mio/log.py
index 6c80a06..e1d7e7c 100755
--- a/src/mio/log.py
+++ b/src/mio/log.py
@@ -1,3 +1,5 @@
+import sys
+
 MUTE=0
 SILENT=1
 NORMAL=2
@@ -12,7 +14,9 @@ def set_verbosity(level):
 
 def log(level, *message):
     if verbosity >= level:
-        print " ".join(message)
+        sys.stderr.write(" ".join(message))
+        sys.stderr.write("\n")
+        sys.stderr.flush()
 
 def declaration(node):
     try:
diff --git a/src/mio/node.py b/src/mio/node.py
index 86d67dc..fe77351 100755
--- a/src/mio/node.py
+++ b/src/mio/node.py
@@ -1,14 +1,24 @@
 import mio.exception
 import mio.filecache
 import mio.util
-import os.path
+import errno
+import os
 import sha
 import shutil
+import tempfile
 
 from mio.exception import *
 from mio.log import log, declaration, SILENT, NORMAL, VERBOSE
 
-
+def parse_boolean(value):
+    if value == None:
+        return False
+    if value.upper() == 'YES' or value.upper() == 'TRUE':
+        return True
+    if value.upper() == 'NO' or value.upper() == 'FALSE':
+        return False
+    raise Exception("yes, no, true or false expected (not '%s')" % value)
+    
 class target_node:
     def __init__(self, kind, name, decl, group):
         assert name != None
@@ -53,7 +63,6 @@ class target_node:
             self.is_installed = True
             self.do_install(prefix, backup)
     
-        
     def test(self, prefix):
         if self.trigged(prefix) and not self.is_tested:
             self.is_tested = True
@@ -96,6 +105,9 @@ class group_node(target_node):
     def add_post(self, post):
         self.post.append(post)
 
+    def add_post(self, post):
+        self.post.append(post)
+
     def do_trig(self, prefix):
         for t in self.target:
             if t.trigged(prefix):
@@ -217,6 +229,7 @@ class file_node(target_node):
             self.mode = mio.util.filemode(decl)
             self.uid = mio.util.uid(decl)
             self.gid = mio.util.gid(decl)
+            self.delete = parse_boolean(self.decl.delete[0])
         
     def assert_identical(self, other):
         if self.__cmp__(other) != 0:
@@ -227,8 +240,10 @@ class file_node(target_node):
             raise SourceException(self, other)
         if self.uid != other.uid:
             raise OwnerException(self, other)
-        if self.gid!= other.gid:
+        if self.gid != other.gid:
             raise GroupException(self, other)
+        if self.delete != other.delete:
+            raise DeleteException(self, other)
 
     def digest(self, name):
         f = open(name)
@@ -240,53 +255,56 @@ class file_node(target_node):
         result = False
         dir = os.path.dirname(self.decl._url)
         self.target = mio.filecache.locatefile([prefix, self.decl.name[0]])
-        if self.decl.source[0] != "":
-            file = self.decl.source[0] or self.decl.name[0]
-            self.source = mio.filecache.loadfile([dir,
-                                                  self.decl.files[0:],
-                                                  file])
-            source_sum = self.digest(self.source)
-            try:
-                target_sum = self.digest(self.target)
-            except:
-                target_sum = None
-
-            if os.path.islink(self.target):
-                if self.decl.force[0] == 'yes':
-                    self.action.append(self.unlink)
+        if self.delete:
+            if os.path.exists(self.target):
+                if os.path.islink(self.target):
+                    raise Exception("%s is a link" % self.target)
+                elif not os.path.isfile(self.target):
+                    raise Exception("%s is not a file" % self.target)
+                self.action.append(self.unlink)
+                result  = True
+        else:
+            if self.decl.source[0] != "":
+                file = self.decl.source[0] or self.decl.name[0]
+                self.source = mio.filecache.loadfile([dir,
+                                                      self.decl.files[0:],
+                                                      file])
+                source_sum = self.digest(self.source)
+                try:
+                    target_sum = self.digest(self.target)
+                except:
+                    target_sum = None
+    
+                if os.path.islink(self.target):
+                    if parse_boolean(self.decl.force[0]):
+                        self.action.append(self.unlink)
+                        self.action.append(self.copy)
+                        return True
+                    else:
+                        raise Exception("%s is a link" % self.target)
+    
+                if source_sum != target_sum:
                     self.action.append(self.copy)
-                    self.action.append(self.chown)
-                    self.action.append(self.chmod)
-                    self.action.append(self.mtime)
                     return True
-                else:
-                    raise Exception("%s is a link" % self.target)
-
-            if source_sum != target_sum:
-                self.action.append(self.copy)
+    
+                tstat = os.stat(self.target)
+                sstat = os.stat(self.source)
+                if tstat.st_mtime != sstat.st_mtime:
+                    result = True
+                    self.action.append(self.mtime)
+            else:
+                # No source file, we will only set owner and mode
+                tstat = os.stat(self.target)
+        
+    
+            if self.uid != tstat.st_uid or self.gid != tstat.st_gid:
+                result = True
                 self.action.append(self.chown)
-                self.action.append(self.chmod)
-                self.action.append(self.mtime)
-                return True
-
-            tstat = os.stat(self.target)
-            sstat = os.stat(self.source)
-            if tstat.st_mtime != sstat.st_mtime:
+    
+            if self.mode != (tstat.st_mode & 07777):
                 result = True
-                self.action.append(self.mtime)
-        else:
-            # No surce file, we will only set owner and mode
-            tstat = os.stat(self.target)
+                self.action.append(self.chmod)
     
-
-        if self.uid != tstat.st_uid or self.gid != tstat.st_gid:
-            result = True
-            self.action.append(self.chown)
-
-        if self.mode != (tstat.st_mode & 07777):
-            result = True
-            self.action.append(self.chmod)
-
         return result
 
 
@@ -308,6 +326,7 @@ class file_node(target_node):
                 mio.exception.Report(e, self.decl, a)
 
     def unlink(self, backup):
+        # TODO: backup here?
         os.unlink(self.target)
 
     def copy(self, backup):
@@ -327,10 +346,27 @@ class file_node(target_node):
             dirmode = mio.util.dirmode(self.decl._parent)
             os.makedirs(dir, dirmode)
 
-        # Copy file
-        shutil.copyfile(self.source, self.target)
-            
+        # Copy file to a temporary file
+        (fd, target) = tempfile.mkstemp(
+            dir=os.path.dirname(self.target),
+            prefix=os.path.basename(self.target) + '.')
+        os.close(fd)
+        shutil.copyfile(self.source, target)
+        # Change owner, mode and date on temporary
+        os.chown(target, self.uid, self.gid)
+        os.chmod(target, self.mode)
+        stat = os.stat(self.source)
+        os.utime(target, (stat.st_atime, stat.st_mtime))
+        # Replace target with temporary file
+        try:
+            os.unlink(self.target)
+        except:
+            pass
+        os.link(target, self.target)
+        os.unlink(target)
+           
     def chmod(self, backup):
+        # Change mode
         os.chmod(self.target, self.mode)
 
     def chown(self, backup):
@@ -342,7 +378,6 @@ class file_node(target_node):
         stat = os.stat(self.source)
         os.utime(self.target, (stat.st_atime, stat.st_mtime))
 
-
 class symlink_node(target_node):
     def __init__(self, name, decl=None, group=None):
         target_node.__init__(self, 'symlink', name, decl, group)
diff --git a/src/mio/parser.py b/src/mio/parser.py
index 8be620b..be97647 100755
--- a/src/mio/parser.py
+++ b/src/mio/parser.py
@@ -421,6 +421,9 @@ class Node:
         child._parent = self
         self._children.append(child)
 
+    def __repr__(self):
+        return self._xml()
+
 
 class Parser(xml.sax.ContentHandler):
     def __init__(self, url):
-- 
GitLab