From: ndparker <>
Date: Wed, 25 Apr 2001 18:19:39 +0000 (+0000)
Subject: Lock.pm: now you can convert a shared lock into an exclusive lock without a race... 
X-Git-Url: https://git.p6c8.net/selfforum.git/commitdiff_plain/42e7ab8a13679442f2cd7524ab7fa38bb2166951?ds=inline;hp=2a78476e5bf2aee7120b59f5ee5e30ad5609fe55
Lock.pm: now you can convert a shared lock into an exclusive lock without a race condition
other files adjusted to work with fo_voting.pl
---
diff --git a/selfforum-cgi/shared/Conf/Admin.pm b/selfforum-cgi/shared/Conf/Admin.pm
index 0c3a469..27a8c93 100644
--- a/selfforum-cgi/shared/Conf/Admin.pm
+++ b/selfforum-cgi/shared/Conf/Admin.pm
@@ -69,6 +69,9 @@ sub read_admin_conf ($) {
                       quoting       => $quoting -> getAttribute ('quotingON'),
                       quoteChars    => $char?$char -> getFirstChild -> getData:undef};
 
+      my $voting = $forum -> getElementsByTagName ('Voting', 0) -> item (0);
+      $conf {Voting} = {voteLock => $voting -> getAttribute ('voteLock')};
+
       # Severance
       $conf {Severance} = &get_severance ($forum -> getElementsByTagName ('Severance', 0) -> item (0));
 
diff --git a/selfforum-cgi/shared/Encode/Posting.pm b/selfforum-cgi/shared/Encode/Posting.pm
index c0815f4..e35e648 100644
--- a/selfforum-cgi/shared/Encode/Posting.pm
+++ b/selfforum-cgi/shared/Encode/Posting.pm
@@ -209,6 +209,7 @@ sub message_field ($$) {
   my $posting = ${+shift};
   my $params = shift || {};
 
+  my $break = '
';
 
   if ($params -> {quoting}) {       # quotes are displayed as special?
     my @array = [0 => []];
@@ -224,10 +225,14 @@ sub message_field ($$) {
     }
     shift @array unless @{$array[0][-1]};
 
+    my $ll=0;
     $posting = join '
' => map {
-      $_->[0]
-        ? join join ('
' => @{$_->[-1]}) => ($params->{startCite}, $params->{endCite})
-        : (join '
' => @{$_->[-1]});
+      my $string = $_->[0]
+        ? (($ll and $ll != $_->[0]) ? $break : '') .
+          join join ($break => @{$_->[-1]})
+            => ($params->{startCite}, $params->{endCite})
+        : (join $break => @{$_->[-1]});
+      $ll = $_->[0]; $string;
     } @array;
   }
 
diff --git a/selfforum-cgi/shared/Lock.pm b/selfforum-cgi/shared/Lock.pm
index f3291c6..3f01861 100644
--- a/selfforum-cgi/shared/Lock.pm
+++ b/selfforum-cgi/shared/Lock.pm
@@ -11,16 +11,19 @@ package Lock;
 ################################################################################
 
 use strict;
-use Carp;
 use vars qw(
   @EXPORT_OK
   %EXPORT_TAGS
+  %LOCKED
   $Timeout
   $violentTimeout
   $masterTimeout
   $iAmMaster
 );
 
+use Carp;
+use Fcntl;
+
 ################################################################################
 #
 # Export
@@ -48,17 +51,26 @@ use base qw(Exporter);
     write_unlock_file
     violent_unlock_file
   )],
-  ALL   => [qw(
-    lock_file
-    unlock_file
-    write_lock_file
-    write_unlock_file
-    violent_unlock_file
-    set_master_lock
-    release_file
-  )]
+  ALL   => \@EXPORT_OK
 );
 
+### sub ~file ($) ##############################################################
+#
+# create lock file names
+#
+sub reffile ($) {
+  return $_[0].'.lock.ref';
+}
+sub lockfile ($) {
+  return $_[0].'.lock';
+}
+sub masterfile ($) {
+  return $_[0].'.master';
+}
+sub masterlockfile ($) {
+  return lockfile(masterfile $_[0]);
+}
+
 ################################################################################
 #
 # Windows section (no symlinks)
@@ -78,21 +90,23 @@ sub w_lock_file ($;$) {
   my $filename = shift;
   my $timeout  = +shift || $Timeout;
 
-  if (-f &masterlockfile($filename)) {
-    for (0..$timeout) {
-
-      # try to increment the reference counter
-      #
-      &set_ref($filename,1,$timeout) and return 1;
-      sleep (1);
+  unless ($LOCKED{$filename}) {
+    if (-f masterlockfile($filename)) {
+      for (1..$timeout) {
+
+        # try to increment the reference counter
+        #
+        if (set_ref($filename,1,$timeout)) {
+          $LOCKED{$filename}=1;
+          return 1;
+        }
+        sleep (1);
+      }
+    }
+    else {
+      # master lock is set or file has not been released yet
+      return;
     }
-   }
-
-  else {
-    # master lock is set
-    # or file has not been realeased yet
-    #
-    return;
   }
 
   # time out
@@ -114,17 +128,25 @@ sub w_unlock_file ($;$) {
   my $filename = shift;
   my $timeout  = shift || $Timeout;
 
-  if (-f &masterlockfile($filename)) {
-
-    # try do decrement the reference counter
-    #
-    &set_ref($filename,-1,$timeout) and return 1;
+  if ($LOCKED{$filename}) {
+    if ($LOCKED{$filename} == 3) {
+      return unless write_unlock_file($filename, $timeout);
+      $LOCKED{$filename} = 1;
+    }
+    if ($LOCKED{$filename} == 1) {
+      if (-f masterlockfile($filename)) {
+
+        # try do decrement the reference counter
+        #
+        if (set_ref($filename,-1,$timeout)) {
+          delete $LOCKED{$filename};
+          return 1;
+        }
+      }
+    }
   }
 
   # time out
-  # maybe the system is occupied
-  # or file has not been released yet
-  #
   return;
 }
 
@@ -141,32 +163,36 @@ sub w_unlock_file ($;$) {
 sub w_write_lock_file ($;$) {
   my $filename=shift;
   my $timeout= shift || $Timeout;
+  my $rest = ($LOCKED{$filename} and $LOCKED{$filename} == 1) ? 1 : 0;
 
-  if (-f &masterlockfile($filename) or $iAmMaster) {
+  if (-f masterlockfile($filename) or $iAmMaster) {
 
     # announce the write lock
     # and wait $timeout seconds for
     # references == 0 (no shared locks set)
     #
-    &simple_lock ($filename,$timeout) or return 0;
-    for (0..$timeout) {
+    simple_lock ($filename,$timeout) or return 0;
+    for (1..$timeout) {
       # lock reference counter
       # or fail
       #
-      unless (&simple_lock (&reffile($filename),$timeout)) {
-        &simple_unlock($filename,$timeout);
+      unless (simple_lock (reffile($filename),$timeout)) {
+        simple_unlock($filename,$timeout);
         return 0;
       }
 
       # ready if we have no shared locks
       #
-      return 1 if (&get_ref ($filename) == 0);
+      if (get_ref ($filename) == $rest) {
+        $LOCKED{$filename} = 2 | ($rest ? 1 : 0);
+        return 1;
+      };
 
       # release reference counter
       # shared locks get the chance to be removed
       #
-      unless (&simple_unlock (&reffile($filename),$timeout)) {
-        &simple_unlock($filename,$timeout);
+      unless (simple_unlock (reffile($filename),$timeout)) {
+        simple_unlock($filename,$timeout);
         return 0;
       }
       sleep(1);
@@ -175,17 +201,15 @@ sub w_write_lock_file ($;$) {
     # write lock failed
     # remove the announcement
     #
-    &simple_unlock ($filename);}
+    simple_unlock ($filename);
+  }
 
   else {
-    # master lock is set
-    # or file has not been released yet
-    #
-    return;}
+    # master lock is set or file has not been released yet
+    return;
+  }
 
   # time out
-  # maybe the system is occupied
-  #
   0;
 }
 
@@ -203,17 +227,19 @@ sub w_write_unlock_file ($;$) {
   my $filename = shift;
   my $timeout  = shift || $Timeout;
 
-  if (-f &masterlockfile($filename) or $iAmMaster) {
+  if (-f masterlockfile($filename) or $iAmMaster) {
 
     # remove reference counter lock
     #
-    &simple_unlock (&reffile($filename),$timeout) or return;
+    simple_unlock (reffile($filename),$timeout) or return;
 
     # remove the write lock announce
     #
-    &simple_unlock ($filename,$timeout) or return;}
+    simple_unlock ($filename,$timeout) or return;
+  }
 
   # done
+  delete $LOCKED{$filename};
   1;
 }
 
@@ -229,20 +255,22 @@ sub w_write_unlock_file ($;$) {
 sub w_violent_unlock_file ($) {
   my $filename = shift;
 
-  if (-f &masterlockfile($filename)) {
+  if (-f masterlockfile($filename)) {
 
     # find out last modification time
     # and do nothing unless 'violent-timout' is over
     #
     my $reffile;
-    if (-f ($reffile = $filename) or -f ($reffile = &lockfile($filename))) {
+    if (-f ($reffile = $filename) or -f ($reffile = lockfile($filename))) {
       my $time = (stat $reffile)[9];
-      return if ((time - $time) < $violentTimeout);}
+      (time - $time) >= $violentTimeout   or return;
+    }
 
     write_lock_file ($filename,1);       # last try, to set an exclusive lock on $filename
-    unlink (&reffile($filename));        # reference counter = 0
-    simple_unlock (&reffile($filename)); # release reference counter file
+    unlink (reffile($filename));         # reference counter = 0
+    simple_unlock (reffile($filename));  # release reference counter file
     simple_unlock ($filename);}          # release file
+    delete $LOCKED{$filename};
 
   return;
 }
@@ -263,11 +291,11 @@ sub w_set_master_lock ($;$) {
 
   # set exclusive lock or fail
   #
-  return unless (&write_lock_file ($filename,$timeout));
+  return unless (write_lock_file ($filename,$timeout));
 
   # set master lock
   #
-  unlink &masterlockfile($filename) and return 1;
+  unlink masterlockfile($filename)    and return 1;
 
   # no chance (occupied?, master lock set yet?)
   return;
@@ -286,11 +314,12 @@ sub w_set_master_lock ($;$) {
 sub w_release_file ($) {
   my $filename=shift;
 
-  unlink (&reffile($filename));                              # reference counter = 0
-  return if (-f &reffile($filename));                      # really?
-  return unless (simple_unlock (&reffile($filename)));     # release reference counter
-  return unless (&simple_unlock ($filename));              # remove any write lock announce
-  return unless (&simple_unlock (&masterfile($filename))); # remove master lock
+  unlink (reffile($filename));                              # reference counter = 0
+  return if (-f reffile($filename));                        # really?
+  return unless (simple_unlock (reffile($filename)));       # release reference counter
+  return unless (simple_unlock ($filename));                # remove any write lock announce
+  return unless (simple_unlock (masterfile($filename)));    # remove master lock
+  delete $LOCKED{$filename};
 
   # done
   1;
@@ -315,25 +344,27 @@ sub x_lock_file ($;$) {
   my $filename = shift;
   my $timeout  = shift || $Timeout;
 
-  unless (-l &masterlockfile($filename)) {
-    for (0..$timeout) {
-
-      # try to increment the reference counter
-      #
-      &set_ref($filename,1,$timeout) and return 1;
-      sleep (1);
+  unless ($LOCKED{$filename}) {
+    unless (-l masterlockfile($filename)) {
+      for (1..$timeout) {
+
+        # try to increment the reference counter
+        #
+        if (set_ref($filename,1,$timeout)) {
+          $LOCKED{$filename} = 1;
+          return 1;
+        }
+        sleep (1);
+      }
     }
-  }
 
-  else {
-    # master lock is set
-    # or file has not been realeased yet
-    #
-    return;
+    else {
+      # master lock is set or file has not been realeased yet
+      return;
+    }
   }
 
   # time out
-  # maybe the system is occupied
   0;
 }
 
@@ -351,16 +382,25 @@ sub x_unlock_file ($;$) {
   my $filename=shift;
   my ($timeout)=(shift (@_) or $Timeout);
 
-  unless (-l &masterlockfile($filename)) {
-    # try do decrement the reference counter
-    #
-    &set_ref($filename,-1,$timeout) and return 1;}
+  if ($LOCKED{$filename}) {
+    if ($LOCKED{$filename} == 3) {
+      return unless write_unlock_file($filename, $timeout);
+      $LOCKED{$filename} = 1;
+    }
+    if ($LOCKED{$filename} == 1) {
+      unless (-l masterlockfile($filename)) {
+        # try to decrement the reference counter
+        #
+        set_ref($filename,-1,$timeout) and do {
+          delete $LOCKED{$filename};
+          return 1;
+        }
+      }
 
-  # time out
-  # maybe the system is occupied
-  # or file has not been released yet
-  #
-  return;
+      # time out
+      return;
+    }
+  }
 }
 
 ### sub x_write_lock_file ($;$) ################################################
@@ -376,32 +416,36 @@ sub x_unlock_file ($;$) {
 sub x_write_lock_file ($;$) {
   my $filename = shift;
   my $timeout  = shift || $Timeout;
+  my $rest = ($LOCKED{$filename} and $LOCKED{$filename} == 1) ? 1 : 0;
 
-  unless (-l &masterlockfile($filename) and not $iAmMaster) {
+  unless (-l masterlockfile($filename) and not $iAmMaster) {
     # announce the write lock
     # and wait $timeout seconds for
     # references == 0 (no shared locks set)
     #
-    &simple_lock ($filename,$timeout) or return 0;
-    for (0..$timeout) {
+    simple_lock ($filename,$timeout) or return 0;
+    for (1..$timeout) {
 
       # lock reference counter
       # or fail
       #
-      unless (&simple_lock (&reffile($filename),$timeout)) {
-        &simple_unlock($filename,$timeout);
+      unless (simple_lock (&reffile($filename),$timeout)) {
+        simple_unlock($filename,$timeout);
         return 0;
       }
 
       # ready if we have no shared locks
       #
-      return 1 if (&get_ref ($filename) == 0);
+      if (get_ref ($filename) == $rest) {
+        $LOCKED{$filename} = 2 | ($rest ? 1 : 0);
+        return 1;
+      };
 
       # release reference counter
       # shared locks get the chance to be removed
       #
-      unless (&simple_unlock (&reffile($filename),$timeout)) {
-        &simple_unlock($filename,$timeout);
+      unless (simple_unlock (&reffile($filename),$timeout)) {
+        simple_unlock($filename,$timeout);
         return 0;
       }
       sleep(1);
@@ -410,7 +454,8 @@ sub x_write_lock_file ($;$) {
     # write lock failed
     # remove the announcement
     #
-    &simple_unlock ($filename);}
+    simple_unlock ($filename);
+  }
 
   else {
     # master lock is set
@@ -442,14 +487,15 @@ sub x_write_unlock_file ($;$) {
   unless (-l &masterlockfile($filename) and not $iAmMaster) {
     # remove reference counter lock
     #
-    &simple_unlock (&reffile($filename),$timeout) or return;
+    simple_unlock (reffile($filename),$timeout) or return;
 
     # remove the write lock announce
     #
-    &simple_unlock ($filename,$timeout) or return;
+    simple_unlock ($filename,$timeout) or return;
   }
 
   # done
+  delete $LOCKED{$filename};
   1;
 }
 
@@ -475,16 +521,17 @@ sub x_violent_unlock_file ($) {
     if (-f ($reffile = $filename)) {
       $time = (stat $reffile)[9];}
 
-    elsif (-l ($reffile = &lockfile($filename))) {
+    elsif (-l ($reffile = lockfile($filename))) {
       $time = (lstat $reffile)[9];}
 
     if ($reffile) {
       return if ((time - $time) < $violentTimeout);}
 
     write_lock_file ($filename,1);       # last try, to set an exclusive lock on $filename
-    unlink (&reffile($filename));        # reference counter = 0
-    simple_unlock (&reffile($filename)); # release reference counter file
+    unlink (reffile($filename));         # reference counter = 0
+    simple_unlock (reffile($filename));  # release reference counter file
     simple_unlock ($filename);}          # release file
+    delete $LOCKED{$filename};
 }
 
 ### sub x_set_master_lock ($;$) ################################################
@@ -503,11 +550,11 @@ sub x_set_master_lock ($;$) {
 
   # set exclusive lock or fail
   #
-  return unless (&write_lock_file ($filename,$timeout));
+  return unless (write_lock_file ($filename,$timeout));
 
   # set master lock
   #
-  symlink $filename, &masterlockfile($filename) and return 1;
+  symlink $filename, masterlockfile($filename) and return 1;
 
   # no chance (occupied?, master lock set yet?)
   return;
@@ -526,39 +573,17 @@ sub x_set_master_lock ($;$) {
 sub x_release_file ($) {
   my $filename=shift;
 
-  unlink (&reffile($filename));                            # reference counter = 0
-  return if (-f &reffile($filename));                      # really?
-  return unless (simple_unlock (&reffile($filename)));     # release reference counter
-  return unless (&simple_unlock ($filename));              # remove any write lock announce
-  return unless (&simple_unlock (&masterfile($filename))); # remove master lock
+  unlink (reffile($filename));                            # reference counter = 0
+  return if (-f reffile($filename));                      # really?
+  return unless (simple_unlock (reffile($filename)));     # release reference counter
+  return unless (simple_unlock ($filename));              # remove any write lock announce
+  return unless (simple_unlock (masterfile($filename)));  # remove master lock
+  delete $LOCKED{$filename};
 
   # done
   1;
 }
 
-################################################################################
-#
-# private subs
-#
-
-### sub ~file ($) ##############################################################
-#
-# create lock file names
-#
-sub reffile ($) {
-  "$_[0].lock.ref";
-}
-sub lockfile ($) {
-  "$_[0].lock";
-}
-sub masterlockfile ($) {
-  &lockfile(&masterfile($_[0]));
-}
-sub masterfile ($) {
-  confess unless defined $_[0];
-  "$_[0].master";
-}
-
 ### sub w_simple_lock ($;$) ####################################################
 ### sub w_simple_unlock ($) ####################################################
 #
@@ -575,7 +600,7 @@ sub w_simple_lock ($;$) {
   my $timeout  = shift || $Timeout;
   my $lockfile = lockfile $filename;
 
-  for (0..$timeout) {
+  for (1..$timeout) {
     unlink $lockfile and return 1;
     sleep(1);
   }
@@ -590,7 +615,7 @@ sub w_simple_unlock ($) {
   my $lockfile = lockfile $filename;
   local *LF;
 
-  if (open(LF, "> $lockfile")) {
+  if (sysopen(LF, $lockfile, O_WRONLY|O_CREAT|O_TRUNC)) {
     return 1 if close (LF);
   }
 
@@ -599,8 +624,8 @@ sub w_simple_unlock ($) {
   return;
 }
 
-### sub w_simple_lock ($;$) ####################################################
-### sub w_simple_unlock ($) ####################################################
+### sub x_simple_lock ($;$) ####################################################
+### sub x_simple_unlock ($) ####################################################
 #
 # simple file lock/unlock
 # (symlinks possible: create/unlink symlink)
@@ -615,21 +640,19 @@ sub x_simple_lock ($;$) {
   my $timeout  = shift || $Timeout;
   my $lockfile = lockfile $filename;
 
-  for (0..$timeout) {
+  for (1..$timeout) {
     symlink $filename,$lockfile and return 1;
     sleep(1);
   }
 
   # time out
-  # locking failed (occupied?)
-  #
   return;
 }
 
 sub x_simple_unlock ($) {
   my $filename=shift;
 
-  unlink (&lockfile($filename)) and return 1;
+  unlink (lockfile $filename) and return 1;
 
   # not able to unlink symlink, hmmm...
   #
@@ -652,30 +675,20 @@ sub w_set_ref ($$$) {
   my $filename = shift;
   my $z        = shift;
   my $timeout  = shift || $Timeout;
-  my $old;
-  my $reffile = reffile $filename;
+  my $reffile  = reffile $filename;
   local *REF;
 
   # if write lock announced, only count down allowed
   #
-  if ($z > 0) {
-    return unless(-f lockfile($filename));
-  }
+  ($z < 0 or -f lockfile ($filename))                     or return;
 
   # lock reference counter file
   #
-  return unless(&simple_lock ($reffile,$timeout));
+  simple_lock ($reffile,$timeout)                         or return;
 
   # load reference counter
   #
-  unless (open REF,"<$reffile") {
-    $old=0;
-  }
-  else {
-    $old=[;
-    chomp $old;
-    close REF or return;
-  }
+  my $old = get_ref ($filename);
 
   # compute and write new ref. counter
   #
@@ -686,17 +699,21 @@ sub w_set_ref ($$$) {
   # if ref. counter == 0
   #
   if ($old == 0) {
-    unlink $reffile or return;
+    unlink $reffile                                       or return;
   }
   else {
-    open REF,">$reffile" or return;
-    print REF $old or return;
-    close REF or return;
+    local $\="\n";
+    sysopen (REF, $reffile, O_WRONLY | O_TRUNC | O_CREAT) or return;
+    print REF $old                                        or do {
+                                                            close REF;
+                                                            return
+                                                          };
+    close REF                                             or return;
   }
 
   # release ref. counter file
   #
-  return unless(&simple_unlock($reffile));
+  simple_unlock($reffile)                                 or return;
 
   # done
   1;
@@ -718,47 +735,44 @@ sub x_set_ref ($$$) {
   my $filename = shift;
   my $z        = shift;
   my $timeout  = shift || $Timeout;
-  my $old;
-  my $reffile = reffile $filename;
+  my $reffile  = reffile $filename;
   local *REF;
 
   # if write lock announced, only count down allowed
   #
   if ($z > 0) {
-    return if(-l &lockfile($filename));
+    return if(-l lockfile($filename));
   }
 
   # lock reference counter file
   #
-  return unless(&simple_lock ($reffile,$timeout));
+  return unless(simple_lock ($reffile,$timeout));
 
   # load reference counter
   #
-  unless (open REF,"<$reffile") {
-    $old=0;
-  }
-  else {
-    $old=][;
-    chomp $old;
-    close REF or return;
-  }
+  my $old = get_ref ($filename);
 
   # compute and write new ref. counter
   #
   $old += $z;
   $old = 0 if ($old < 0);
+
   if ($old == 0) {
-    unlink $reffile or return;
+    unlink $reffile                                       or return;
   }
   else {
-    open REF,">$reffile" or return;
-    print REF $old or return;
-    close REF or return;
+    local $\="\n";
+    sysopen (REF, $reffile, O_WRONLY | O_TRUNC | O_CREAT) or return;
+    print REF $old                                        or do {
+                                                            close REF;
+                                                            return
+                                                          };
+    close REF                                             or return;
   }
 
   # release ref. counter file
   #
-  return unless(&simple_unlock($reffile));
+  simple_unlock($reffile)                                 or return;
 
   # done
   1;
@@ -774,23 +788,21 @@ sub x_set_ref ($$$) {
 #
 # Return: reference counter
 #
-sub get_ref ($$) {
+sub get_ref ($) {
   my $filename = shift;
   my $reffile  = reffile $filename;
   my $old;
   local *REF;
 
-  unless (open REF,"< $reffile") {
-    $old = 0;
-  }
-  else {
-    $old=][;
-    chomp $old;
+  if (sysopen (REF, $reffile, O_RDONLY)) {
+    local $/="\n";
+    read REF, $old, -s $reffile;
     close REF;
+    chomp $old;
   }
 
   # return value
-  $old;
+  $old or 0;
 }
 
 ################################################################################
@@ -806,6 +818,8 @@ BEGIN {
 
   $iAmMaster = 0;        # default: I am nobody
 
+  %LOCKED = ();
+
   # assign the aliases to the needed functions
   # (perldoc -f symlink)
 
diff --git a/selfforum-cgi/shared/Posting/Write.pm b/selfforum-cgi/shared/Posting/Write.pm
index 507cd81..98f71fe 100644
--- a/selfforum-cgi/shared/Posting/Write.pm
+++ b/selfforum-cgi/shared/Posting/Write.pm
@@ -117,7 +117,7 @@ sub write_new_thread ($) {
 
   save_file ($param -> {forumFile}, $forum) or return $error{forumWrite};
   release_file ($param -> {messagePath}.$tid.'.xml');
-  return (0, $thread, $mid);
+  return (0, $thread, $mid, $tid);
 }
 
 ### sub write_reply_posting ($) ################################################
@@ -228,14 +228,14 @@ sub write_reply_posting ($) {
       $param -> {parsedThreads},
       { dtd         => $param -> {dtd},
         lastMessage => $mid,
-        lastThread  => $tid
+        lastThread  => 't'.$param -> {lastThread}
       }
     );
 
     save_file ($param -> {forumFile}, $forum) or return $error{forumWrite};
   }
 
-  return (0, $thread, $mid);
+  return (0, $thread, $mid, $tid);
 }
 
 # keep 'require' happy
diff --git a/selfforum-cgi/shared/Template/Forum.pm b/selfforum-cgi/shared/Template/Forum.pm
index 9140478..5963008 100644
--- a/selfforum-cgi/shared/Template/Forum.pm
+++ b/selfforum-cgi/shared/Template/Forum.pm
@@ -14,7 +14,10 @@ use strict;
 
 use Lock qw(:READ);
 use Encode::Plain; $Encode::Plain::utf8 = 1;
-use Posting::_lib qw(get_all_threads long_hr_time);
+use Posting::_lib qw(
+  get_all_threads
+  long_hr_time
+);
 use Template;
 use Template::_conf;
 use Template::_thread;
@@ -100,4 +103,4 @@ sub print_forum_as_HTML ($$$) {
 
 #
 #
-### end of Template::Forum #####################################################
+### end of Template::Forum #####################################################
\ No newline at end of file
diff --git a/selfforum-cgi/shared/Template/Posting.pm b/selfforum-cgi/shared/Template/Posting.pm
index 348a7db..0ffb6c2 100644
--- a/selfforum-cgi/shared/Template/Posting.pm
+++ b/selfforum-cgi/shared/Template/Posting.pm
@@ -24,6 +24,7 @@ use Posting::_lib qw(
   parse_xml_file
   hr_time
 );
+use Posting::Cache;
 use Template;
 use Template::_conf;
 use Template::_query;
@@ -169,11 +170,26 @@ sub print_posting_as_HTML ($$$) {
           $formdata->{uniqueID}  ->{assign}->{value} => plain(unique_id),
           $formdata->{followUp}  ->{assign}->{value} => plain($param -> {thread}.';'.$param -> {posting}),
           $formdata->{quoteChar} ->{assign}->{value} => "ÿ".plain(defined $view -> {quoteChars} ? $view -> {quoteChars} : ''),
-          $formdata->{userID}    ->{assign}->{value} => ''
+          $formdata->{userID}    ->{assign}->{value} => '',
+          $assign->{firsttime}                       => $param->{firsttime} ? $param->{firsttime} : '',
+          $assign->{voted}                           => $param->{voted} ? $param->{voted} : ''
         },
         $pars,
         $parent_pars
       )};
+
+      # all output done
+      #
+      close STDOUT;
+
+      if ($param->{firsttime}) {
+        my $cache = new Posting::Cache ($param->{cachefile});
+        $cache -> add_view (
+          { thread  => $param -> {thread},
+            posting => $param -> {posting}
+          }
+        );
+      }
     }
   }
 
diff --git a/selfforum-cgi/shared/Template/_thread.pm b/selfforum-cgi/shared/Template/_thread.pm
index 7b65d1d..3f00ad2 100644
--- a/selfforum-cgi/shared/Template/_thread.pm
+++ b/selfforum-cgi/shared/Template/_thread.pm
@@ -170,5 +170,4 @@ sub html_thread ($$$) {
 
 #
 #
-### end of Template::_thread ###################################################
-
+### end of Template::_thread ###################################################
\ No newline at end of file
diff --git a/selfforum-cgi/user/config/fo_view.xml b/selfforum-cgi/user/config/fo_view.xml
index 6ec121f..c9f86ee 100644
--- a/selfforum-cgi/user/config/fo_view.xml
+++ b/selfforum-cgi/user/config/fo_view.xml
@@ -3,31 +3,6 @@
 
 
   
-    
-
-      
-        i
-        t
-        m
-        c
-      
-
-      
-        _THREAD
-        TREE_START
-        TREE_LINE
-        TREE_CLOSED
-        TREE_START_NC
-        TREE_LINE_NC
-        TREE_CLOSED_NC
-        _LINK
-        _NAME
-        _COMMAND
-        _TITLE
-        _CATEGORY
-        _TIME
-      
-    
 
     
 
@@ -47,147 +22,6 @@
 
     
 
-    
-      e:/localhosts/i_selfhtml/cgi-local/user/config/posting.tmpl.xml
-
-      
-        DOC_POSTING
-        DOC_ERROR
-        _CSS_FILE
-        _MESSAGE
-        _BEF_NAME
-        _BEF_MAIL
-        _BEF_TIME
-        _BEF_HOME
-        _BEF_IMAGE
-        _BEF_TITLE
-        _REF_CATEGORY
-        _BEF_CATEGORY
-        _REF_TITLE
-        _REF_NAME
-        _REF_TIME
-        _REF_LINK
-        CITE_START
-        CITE_END
-
-        _N_A
-        _OCCUPIED
-        _ERROR_TEXT
-      
-
-      
-        
-          
-            /cgi-local/user/fo_posting.pl
-
-            _FORM_ACTION
-          
-
-          
-            /cgi-local/user/fo_voting.pl
-
-            _VOTE_ACTION
-          
-        
-
-        
-          
-            
-              _FORM_FUP_NAME
-              _FORM_FUP_VALUE
-            
-
-            fup
-            20
-          
-
-          
-            
-              _FORM_UID_NAME
-              _FORM_UID_VALUE
-            
-
-            userid
-            25
-          
-
-          
-            
-              _FORM_UNID_NAME
-              _FORM_UNID_VALUE
-            
-
-            unid
-            25
-          
-
-          
-            
-              _FORM_QCHAR_NAME
-              _FORM_QCHAR_VALUE
-            
-
-            qchar
-            5
-          
-
-          
-            
-              _FORM_NAME_NAME
-              _FORM_NAME_VALUE
-            
-
-            name
-            60
-            2
-          
-
-          
-            
-              _FORM_MAIL_NAME
-              _FORM_MAIL_VALUE
-            
-
-            email
-            60
-            7
-          
-
-          
-            
-              _FORM_BODY_NAME
-              _FORM_BODY_VALUE
-            
-            body
-            12288
-            10
-          
-
-          
-            _FORM_SIGN_VALUE
-          
-
-          
-            
-              _FORM_URL_NAME
-              _FORM_URL_VALUE
-            
-            url
-            1024
-          
-
-          
-            
-              _FORM_IMG_NAME
-              _FORM_IMG_VALUE
-            
-            image
-            1024
-          
-
-        
-      
-    
   
 
 
diff --git a/selfforum-cgi/user/fo_posting.pl b/selfforum-cgi/user/fo_posting.pl
index 0bb18de..c78cab7 100644
--- a/selfforum-cgi/user/fo_posting.pl
+++ b/selfforum-cgi/user/fo_posting.pl
@@ -11,22 +11,35 @@
 ################################################################################
 
 use strict;
-use vars qw($Bin $Shared $Script $Config);
+use vars qw(
+  $Bin
+  $Shared
+  $Script
+  $Config
+);
 
 # locate the script
 #
 BEGIN {
-  my $null = $0; $null =~ s/\\/\//g; # for win :-(
+#  my $null = $0; $null =~ s/\\/\//g; # for win :-(
+#  $Bin     = ($null =~ /^(.*)\/.*$/)? $1 : '.';
+#  $Shared  = "$Bin/../shared";
+#  $Config  = "$Bin/config";
+#  $Script  = ($null =~ /^.*\/(.*)$/)? $1 : $null;
+
+  my $null = $0; #$null =~ s/\\/\//g; # for win :-(
   $Bin     = ($null =~ /^(.*)\/.*$/)? $1 : '.';
-  $Shared  = "$Bin/../shared";
-  $Config  = "$Bin/config";
+  $Config  = "$Bin/../../../cgi-config/devforum";
+  $Shared  = "$Bin/../../../cgi-shared";
   $Script  = ($null =~ /^.*\/(.*)$/)? $1 : $null;
 }
+
 use lib "$Shared";
 use CGI::Carp qw(fatalsToBrowser);
 
 use Conf;
 use Conf::Admin;
+use Posting::Cache;
 
 # load script configuration and admin default conf.
 #
@@ -396,17 +409,17 @@ sub save {
             if (defined $q -> param ($formdata -> {$may{$_}} -> {name}));
         }
 
-        my ($stat, $xml, $mid);
+        my ($stat, $xml, $mid, $tid);
 
         # we've got a fup if it's a reply
         #
         if ($self -> {response} -> {reply}) {
           $pars -> {parentMessage} = $self -> {fup_mid};
           $pars -> {thread}        = $self -> {fup_tid};
-          ($stat, $xml, $mid) = write_reply_posting ($pars);
+          ($stat, $xml, $mid, $tid) = write_reply_posting ($pars);
         }
         else {
-          ($stat, $xml, $mid) = write_new_thread ($pars);
+          ($stat, $xml, $mid, $tid) = write_new_thread ($pars);
         }
 
         if ($stat) {
@@ -417,6 +430,13 @@ sub save {
           };
         }
         else {
+          my $cache = new Posting::Cache ($self->{conf}->{original}->{files}->{cacheFile});
+          $cache -> add_posting (
+            { thread  => ($tid =~ /(\d+)/)[0],
+              posting => ($mid =~ /(\d+)/)[0]
+            }
+          );
+
           $self -> {check_success} = 1;
           my $thx = $self -> {conf} -> {show_posting} -> {thanx};
 
diff --git a/selfforum-cgi/user/fo_view.pl b/selfforum-cgi/user/fo_view.pl
index ee83ce2..37260e3 100644
--- a/selfforum-cgi/user/fo_view.pl
+++ b/selfforum-cgi/user/fo_view.pl
@@ -11,13 +11,24 @@
 ################################################################################
 
 use strict;
-use vars qw($Bin $Shared $Script $Config);
+use vars qw(
+  $Bin
+  $Shared
+  $Script
+  $Config
+);
 
 BEGIN {
-  my $null = $0; $null =~ s/\\/\//g; # for win :-(
+#  my $null = $0; $null =~ s/\\/\//g; # for win :-(
+#  $Bin     = ($null =~ /^(.*)\/.*$/)? $1 : '.';
+#  $Shared  = "$Bin/../shared";
+#  $Config  = "$Bin/config";
+#  $Script  = ($null =~ /^.*\/(.*)$/)? $1 : $null;
+
+  my $null = $0; #$null =~ s/\\/\//g; # for win :-(
   $Bin     = ($null =~ /^(.*)\/.*$/)? $1 : '.';
-  $Shared  = "$Bin/../shared";
-  $Config  = "$Bin/config";
+  $Config  = "$Bin/../../../cgi-config/devforum";
+  $Shared  = "$Bin/../../../cgi-shared";
   $Script  = ($null =~ /^.*\/(.*)$/)? $1 : $null;
 }
 
@@ -58,7 +69,9 @@ if (defined ($tid) and defined ($mid)) {
       messages     => $conf -> {template} -> {messages},
       form         => $show_posting -> {form},
       cgi          => $cgi,
-      tree         => $tree
+      tree         => $tree,
+      firsttime    => 1,
+      cachefile    => $conf -> {files} -> {cacheFile}
     }
   );
 }
]