summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/builtin.c48
-rw-r--r--Src/params.c56
2 files changed, 92 insertions, 12 deletions
diff --git a/Src/builtin.c b/Src/builtin.c
index d36b697bc..9f1c2dcb6 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1653,6 +1653,13 @@ getasg(char *s)
     return &asg;
 }
 
+/* for new special parameters */
+enum {
+    NS_NONE,
+    NS_NORMAL,
+    NS_SECONDS
+};
+
 /* function to set a single parameter */
 
 /**/
@@ -1661,7 +1668,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	       int on, int off, int roff, char *value, Param altpm,
 	       Options ops, int auxlen)
 {
-    int usepm, tc, keeplocal = 0, newspecial = 0;
+    int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly;
     char *subscript;
 
     /*
@@ -1694,14 +1701,15 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	 * local.  It can be applied either to the special or in the
 	 * typeset/local statement for the local variable.
 	 */
-	newspecial = (pm->flags & PM_SPECIAL)
-	    && !(on & PM_HIDE) && !(pm->flags & PM_HIDE & ~off);
+	if ((pm->flags & PM_SPECIAL)
+	    && !(on & PM_HIDE) && !(pm->flags & PM_HIDE & ~off))
+	    newspecial = NS_NORMAL;
 	usepm = 0;
     }
 
     /* attempting a type conversion, or making a tied colonarray? */
     tc = 0;
-    if (usepm || newspecial) {
+    if (usepm || newspecial != NS_NONE) {
 	int chflags = ((off & pm->flags) | (on & ~pm->flags)) &
 	    (PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
 	     PM_ARRAY|PM_TIED|PM_AUTOLOAD);
@@ -1716,11 +1724,31 @@ typeset_single(char *cname, char *pname, Param pm, int func,
      * with a special or a parameter which isn't loaded yet (which
      * may be special when it is loaded; we can't tell yet).
      */
-    if (tc || ((usepm || newspecial) && (off & pm->flags & PM_READONLY))) {
+    if ((readonly =
+	 ((usepm || newspecial != NS_NONE) && 
+	  (off & pm->flags & PM_READONLY))) ||
+	tc) {
 	if (pm->flags & PM_SPECIAL) {
-	    zerrnam(cname, "%s: can't change type of a special parameter",
-		    pname, 0);
-	    return NULL;
+	    int err = 1;
+	    if (!readonly && !strcmp(pname, "SECONDS"))
+	    {
+		if (newspecial != NS_NONE)
+		{
+		    newspecial = NS_SECONDS;
+		    err = 0;	/* and continue */
+		    tc = 0;	/* but don't do a normal conversion */
+		} else if (!setsecondstype(pm, on, off)) {
+		    if (value && !setsparam(pname, ztrdup(value)))
+			return NULL;
+		    return pm;
+		}
+	    }
+	    if (err)
+	    {
+		zerrnam(cname, "%s: can't change type of a special parameter",
+			pname, 0);
+		return NULL;
+	    }
 	} else if (pm->flags & PM_AUTOLOAD) {
 	    zerrnam(cname, "%s: can't change type of autoloaded parameter",
 		    pname, 0);
@@ -1820,7 +1848,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	unsetparam_pm(pm, 0, 1);
     }
 
-    if (newspecial) {
+    if (newspecial != NS_NONE) {
 	Param tpm, pm2;
 	if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
 	    zerrnam(cname, "%s: restricted", pname, 0);
@@ -1866,6 +1894,8 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	 * because we've checked for unpleasant surprises above.
 	 */
 	pm->flags = (PM_TYPE(pm->flags) | on | PM_SPECIAL) & ~off;
+	if (newspecial == NS_SECONDS)
+	    setsecondstype(pm, on, off);
 	/*
 	 * Final tweak: if we've turned on one of the flags with
 	 * numbers, we should use the appropriate integer.
diff --git a/Src/params.c b/Src/params.c
index 0b3a065ba..0d796871f 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -141,7 +141,7 @@ IPDEF1("EGID", egidgetfn, egidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("HISTSIZE", histsizegetfn, histsizesetfn, PM_RESTRICTED),
 IPDEF1("RANDOM", randomgetfn, randomsetfn, 0),
 IPDEF1("SAVEHIST", savehistsizegetfn, savehistsizesetfn, PM_RESTRICTED),
-IPDEF1("SECONDS", secondsgetfn, secondssetfn, 0),
+IPDEF1("SECONDS", intsecondsgetfn, intsecondssetfn, 0),
 IPDEF1("UID", uidgetfn, uidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("EUID", euidgetfn, euidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("TTYIDLE", ttyidlegetfn, nullintsetfn, PM_READONLY),
@@ -2670,7 +2670,7 @@ randomsetfn(Param pm, zlong v)
 
 /**/
 zlong
-secondsgetfn(Param pm)
+intsecondsgetfn(Param pm)
 {
     return time(NULL) - shtimer.tv_sec;
 }
@@ -2679,12 +2679,60 @@ secondsgetfn(Param pm)
 
 /**/
 void
-secondssetfn(Param pm, zlong x)
+intsecondssetfn(Param pm, zlong x)
 {
     shtimer.tv_sec = time(NULL) - x;
     shtimer.tv_usec = 0;
 }
 
+/**/
+double
+floatsecondsgetfn(Param pm)
+{
+    struct timeval now;
+    struct timezone dummy_tz;
+
+    gettimeofday(&now, &dummy_tz);
+
+    return (double)(now.tv_sec - shtimer.tv_sec) +
+	(double)(now.tv_usec - shtimer.tv_usec) / 1000000.0;
+}
+
+/**/
+void
+floatsecondssetfn(Param pm, double x)
+{
+    struct timeval now;
+    struct timezone dummy_tz;
+
+    gettimeofday(&now, &dummy_tz);
+    shtimer.tv_sec = now.tv_sec - (int)x;
+    shtimer.tv_usec = now.tv_usec - (int)((x - (double)(int)x) * 1000000.0);
+}
+
+/**/
+int
+setsecondstype(Param pm, int on, int off)
+{
+    int newflags = (pm->flags | on) & ~off;
+    int tp = PM_TYPE(newflags);
+    /* Only one of the numeric types is allowed. */
+    if (tp == PM_EFLOAT || tp == PM_FFLOAT)
+    {
+	pm->gets.ffn = floatsecondsgetfn;
+	pm->sets.ffn = floatsecondssetfn;
+    }
+    else if (tp == PM_INTEGER)
+    {
+	pm->gets.ifn = intsecondsgetfn;
+	pm->sets.ifn = intsecondssetfn;
+    }
+    else
+	return 1;
+    pm->flags = newflags;
+    return 0;
+}
+
 /* Function to get value for special parameter `USERNAME' */
 
 /**/
@@ -3435,6 +3483,8 @@ scanendscope(HashNode hn, int flags)
 	     */
 	    Param tpm = pm->old;
 
+	    if (!strcmp(pm->nam, "SECONDS"))
+		setsecondstype(pm, PM_TYPE(tpm->flags), PM_TYPE(pm->flags));
 	    DPUTS(!tpm || PM_TYPE(pm->flags) != PM_TYPE(tpm->flags) ||
 		  !(tpm->flags & PM_SPECIAL),
 		  "BUG: in restoring scope of special parameter");