From 62c58a7fae828ac038fd1fbcd3fb5066456ab770 Mon Sep 17 00:00:00 2001
From: Dmitry Yurtaev <dmitry@yurtaev.com>
Date: Thu, 5 May 2016 19:09:19 +0400
Subject: [PATCH] hal: add signal aliases

extends alias command to signals in addition pins and parameters. new hal_lib function, hal_signal_alias(). updated 'hallcmd show' and 'halcmd save' to include signal aliases.
---
 docs/man/man1/halcmd.1             |   10 +--
 docs/man/man3/hal_param_alias.3hal |    1 +
 docs/man/man3/hal_pin_alias.3hal   |    1 +
 src/hal/hal.h                      |    9 ++-
 src/hal/hal_lib.c                  |  132 ++++++++++++++++++++++++++++++++++++
 src/hal/hal_priv.h                 |    1 +
 src/hal/utils/halcmd_commands.c    |   84 +++++++++++++++++++----
 src/hal/utils/halcmd_completion.c  |   60 ++++++++++++++--
 tests/save.0/expected              |    1 +
 9 files changed, 274 insertions(+), 25 deletions(-)

diff --git a/docs/man/man1/halcmd.1 b/docs/man/man1/halcmd.1
index 866bba7..ceb25f3 100644
--- a/docs/man/man1/halcmd.1
+++ b/docs/man/man1/halcmd.1
@@ -308,16 +308,16 @@ equivalent of \fBcomp\fR, \fBsigu\fR, \fBlink\fR, \fBparam\fR, and \fBthread\fR.
 Execute the commands from \fIfilename.hal\fR.
 .TP
 \fBalias\fR \fItype\fR \fIname\fR \fIalias\fR
-Assigns "\fBalias\fR" as a second name for the pin or parameter
+Assigns "\fBalias\fR" as a second name for the pin, signal or parameter
 "name".  For most operations, an alias provides a second
-name that can be used to refer to a pin or parameter, both the
+name that can be used to refer to a pin, signal or parameter, both the
 original name and the alias will work.
-   "type" must be \fBpin\fR or \fBparam\fR.
+   "type" must be \fBpin\fR, \fBsig\fR or \fBparam\fR.
    "name" must be an existing name or \fBalias\fR of the specified type.
 .TP
 \fBunalias\fR \fItype\fR \fIalias\fR
-Removes any alias from the pin or parameter alias.
-  "type" must be \fBpin\fR or \fBparam\fR
+Removes any alias from the pin, signal or parameter alias.
+  "type" must be \fBpin\fR, \fBsig\fR or \fBparam\fR
   "alias" must be an existing name or \fBalias\fR of the specified type.
 .TP
 \fBlist\fR \fItype\fR [\fIpattern\fR]
diff --git a/docs/man/man3/hal_param_alias.3hal b/docs/man/man3/hal_param_alias.3hal
index 9638ccc..adec1e2 100644
--- a/docs/man/man3/hal_param_alias.3hal
+++ b/docs/man/man3/hal_param_alias.3hal
@@ -30,3 +30,4 @@ Returns a HAL status code.
 
 .SH SEE ALSO
 .BR hal_pin_alias (3)
+.BR hal_signal_alias (3)
diff --git a/docs/man/man3/hal_pin_alias.3hal b/docs/man/man3/hal_pin_alias.3hal
index c102ff3..b7a9c1e 100644
--- a/docs/man/man3/hal_pin_alias.3hal
+++ b/docs/man/man3/hal_pin_alias.3hal
@@ -29,4 +29,5 @@ renamed, to preserve compatibility with old versions.
 Returns a HAL status code.
 
 .SH SEE ALSO
+.BR hal_signal_alias (3)
 .BR hal_param_alias (3)
diff --git a/src/hal/hal.h b/src/hal/hal.h
index 34a9004..642b382 100644
--- a/src/hal/hal.h
+++ b/src/hal/hal.h
@@ -464,6 +464,13 @@ extern int hal_link(const char *pin_name, const char *sig_name);
 */
 extern int hal_unlink(const char *pin_name);
 
+/** 'hal_signal_alias()' assigns an alternate name, aka an alias, to
+    a pin.  Once assigned, the signal can be referred to by either its
+    original name or the alias.  Calling this function with 'alias'
+    set to NULL will remove any existing alias.
+*/
+extern int hal_signal_alias(const char *sig_name, const char *alias);
+
 /***********************************************************************
 *                     "PARAMETER" FUNCTIONS                            *
 ************************************************************************/
@@ -570,7 +577,7 @@ extern int hal_param_s32_set(const char *name, signed long value);
     either its original name or the alias.  Calling this function
     with 'alias' set to NULL will remove any existing alias.
 */
-extern int hal_param_alias(const char *pin_name, const char *alias);
+extern int hal_param_alias(const char *param_name, const char *alias);
 
 /** 'hal_param_set()' is a generic function that sets the value of a
     parameter.  It is provided ONLY for those special cases where a
diff --git a/src/hal/hal_lib.c b/src/hal/hal_lib.c
index 14b9c3d..aae919d 100644
--- a/src/hal/hal_lib.c
+++ b/src/hal/hal_lib.c
@@ -1190,6 +1190,128 @@ int hal_unlink(const char *pin_name)
     return 0;
 }
 
+int hal_signal_alias(const char *sig_name, const char *alias)
+{
+    int *prev, next, cmp;
+    hal_sig_t *sig, *ptr;
+    hal_oldname_t *oldname;
+
+    if (hal_data == 0) {
+	rtapi_print_msg(RTAPI_MSG_ERR,
+	    "HAL: ERROR: signal_alias called before init\n");
+	return -EINVAL;
+    }
+    if (hal_data->lock & HAL_LOCK_CONFIG)  {
+	rtapi_print_msg(RTAPI_MSG_ERR,
+	    "HAL: ERROR: signal_alias called while HAL locked\n");
+	return -EPERM;
+    }
+    if (alias != NULL ) {
+	if (strlen(alias) > HAL_NAME_LEN) {
+	    rtapi_print_msg(RTAPI_MSG_ERR,
+	        "HAL: ERROR: alias name '%s' is too long\n", alias);
+	    return -EINVAL;
+	}
+    }
+    /* get mutex before accessing shared data */
+    rtapi_mutex_get(&(hal_data->mutex));
+    if (alias != NULL ) {
+	sig = halpr_find_sig_by_name(alias);
+	if ( sig != NULL ) {
+	    rtapi_mutex_give(&(hal_data->mutex));
+	    rtapi_print_msg(RTAPI_MSG_ERR,
+	        "HAL: ERROR: duplicate signal/alias name '%s'\n", alias);
+	    return -EINVAL;
+	}
+    }
+    /* once we unlink the signal from the list, we don't want to have to
+       abort the change and repair things.  So we allocate an oldname
+       struct here, then free it (which puts it on the free list).  This
+       allocation might fail, in which case we abort the command.  But
+       if we actually need the struct later, the next alloc is guaranteed
+       to succeed since at least one struct is on the free list. */
+    oldname = halpr_alloc_oldname_struct();
+    if ( oldname == NULL ) {
+	rtapi_mutex_give(&(hal_data->mutex));
+	rtapi_print_msg(RTAPI_MSG_ERR,
+	    "HAL: ERROR: insufficient memory for signal_alias\n");
+	return -EINVAL;
+    }
+    free_oldname_struct(oldname);
+    /* find the signal and unlink it from signal list */
+    prev = &(hal_data->sig_list_ptr);
+    next = *prev;
+    while (1) {
+	if (next == 0) {
+	    /* reached end of list, not found */
+	    rtapi_mutex_give(&(hal_data->mutex));
+	    rtapi_print_msg(RTAPI_MSG_ERR,
+		"HAL: ERROR: signal '%s' not found\n", sig_name);
+	    return -EINVAL;
+	}
+	sig = SHMPTR(next);
+	if ( strcmp(sig->name, sig_name) == 0 ) {
+	    /* found it, unlink from list */
+	    *prev = sig->next_ptr;
+	    break;
+	}
+	if (sig->oldname != 0 ) {
+	    oldname = SHMPTR(sig->oldname);
+	    if (strcmp(oldname->name, sig_name) == 0) {
+		/* found it, unlink from list */
+		*prev = sig->next_ptr;
+		break;
+	    }
+	}
+	/* didn't find it yet, look at next one */
+	prev = &(sig->next_ptr);
+	next = *prev;
+    }
+    if ( alias != NULL ) {
+	/* adding a new alias */
+	if ( sig->oldname == 0 ) {
+	    /* save old name (only if not already saved) */
+	    oldname = halpr_alloc_oldname_struct();
+	    sig->oldname = SHMOFF(oldname);
+	    rtapi_snprintf(oldname->name, sizeof(oldname->name), "%s", sig->name);
+	}
+	/* change signal's name to 'alias' */
+	rtapi_snprintf(sig->name, sizeof(sig->name), "%s", alias);
+    } else {
+	/* removing an alias */
+	if ( sig->oldname != 0 ) {
+	    /* restore old name (only if signal is aliased) */
+	    oldname = SHMPTR(sig->oldname);
+	    rtapi_snprintf(sig->name, sizeof(sig->name), "%s", oldname->name);
+	    sig->oldname = 0;
+	    free_oldname_struct(oldname);
+	}
+    }
+    /* insert signal back into list in proper place */
+    prev = &(hal_data->sig_list_ptr);
+    next = *prev;
+    while (1) {
+	if (next == 0) {
+	    /* reached end of list, insert here */
+	    sig->next_ptr = next;
+	    *prev = SHMOFF(sig);
+	    rtapi_mutex_give(&(hal_data->mutex));
+	    return 0;
+	}
+	ptr = SHMPTR(next);
+	cmp = strcmp(ptr->name, sig->name);
+	if (cmp > 0) {
+	    /* found the right place for it, insert here */
+	    sig->next_ptr = next;
+	    *prev = SHMOFF(sig);
+	    rtapi_mutex_give(&(hal_data->mutex));
+	    return 0;
+	}
+	/* didn't find it yet, look at next one */
+	prev = &(ptr->next_ptr);
+	next = *prev;
+    }
+}
 /***********************************************************************
 *                       "PARAM" FUNCTIONS                              *
 ************************************************************************/
@@ -2358,6 +2480,7 @@ hal_sig_t *halpr_find_sig_by_name(const char *name)
 {
     int next;
     hal_sig_t *sig;
+    hal_oldname_t *oldname;
 
     /* search signal list for 'name' */
     next = hal_data->sig_list_ptr;
@@ -2367,6 +2490,13 @@ hal_sig_t *halpr_find_sig_by_name(const char *name)
 	    /* found a match */
 	    return sig;
 	}
+	if (sig->oldname != 0 ) {
+	    oldname = SHMPTR(sig->oldname);
+	    if (strcmp(oldname->name, name) == 0) {
+		/* found a match */
+		return sig;
+	    }
+	}
 	/* didn't find it yet, look at next one */
 	next = sig->next_ptr;
     }
@@ -3250,6 +3380,7 @@ static void free_sig_struct(hal_sig_t * sig)
 	pin = halpr_find_pin_by_sig(sig, pin);
     }
     /* clear contents of struct */
+    if ( sig->oldname != 0 ) free_oldname_struct(SHMPTR(sig->oldname));
     sig->data_ptr = 0;
     sig->type = 0;
     sig->readers = 0;
@@ -3774,4 +3905,5 @@ EXPORT_SYMBOL(halpr_find_funct_by_owner);
 EXPORT_SYMBOL(halpr_find_pin_by_sig);
 
 EXPORT_SYMBOL(hal_pin_alias);
+EXPORT_SYMBOL(hal_signal_alias);
 EXPORT_SYMBOL(hal_param_alias);
diff --git a/src/hal/hal_priv.h b/src/hal/hal_priv.h
index b66a74e..3527fb0 100644
--- a/src/hal/hal_priv.h
+++ b/src/hal/hal_priv.h
@@ -234,6 +234,7 @@ typedef struct {
 typedef struct {
     int next_ptr;		/* next signal in linked list */
     int data_ptr;		/* offset of signal value */
+    int oldname;		/* old name if aliased, else zero */
     hal_type_t type;		/* data type */
     int readers;		/* number of input pins linked */
     int writers;		/* number of output pins linked */
diff --git a/src/hal/utils/halcmd_commands.c b/src/hal/utils/halcmd_commands.c
index 6865aa6..021bfc3 100644
--- a/src/hal/utils/halcmd_commands.c
+++ b/src/hal/utils/halcmd_commands.c
@@ -61,6 +61,7 @@ static int unloadrt_comp(char *mod_name);
 static void print_comp_info(char **patterns);
 static void print_pin_info(int type, char **patterns);
 static void print_pin_aliases(char **patterns);
+static void print_sig_aliases(char **patterns);
 static void print_param_aliases(char **patterns);
 static void print_sig_info(int type, char **patterns);
 static void print_script_sig_info(int type, char **patterns);
@@ -324,37 +325,41 @@ int do_addf_cmd(char *func, char *thread, char **opt) {
     return retval;
 }
 
-int do_alias_cmd(char *pinparam, char *name, char *alias) {
+int do_alias_cmd(char *type, char *name, char *alias) {
     int retval;
 
-    if ( strcmp (pinparam, "pin" ) == 0 ) {
+    if ( strcmp (type, "pin" ) == 0 ) {
 	retval = hal_pin_alias(name, alias);
-    } else if ( strcmp (pinparam, "param" ) == 0 ) {
+    } else if ( strcmp (type, "sig" ) == 0 ) {
+	retval = hal_signal_alias(name, alias);
+    } else if ( strcmp (type, "param" ) == 0 ) {
 	retval = hal_param_alias(name, alias);
     } else {
 	retval = -EINVAL;
     }
     if(retval == 0) {
         halcmd_info("%s '%s' aliased to '%s'\n",
-                    pinparam, name, alias);
+                    type, name, alias);
     } else {
         halcmd_error("alias failed\n");
     }
     return retval;	
 }
 
-int do_unalias_cmd(char *pinparam, char *name) {
+int do_unalias_cmd(char *type, char *name) {
     int retval;
-    if (strcmp(pinparam, "pin") == 0) {
+    if (strcmp(type, "pin") == 0) {
         retval = hal_pin_alias(name, NULL);
-    } else if ( strcmp (pinparam, "param" ) == 0 ) {
+    } else if ( strcmp (type, "sig" ) == 0 ) {
+      retval = hal_signal_alias(name, NULL);
+    } else if ( strcmp (type, "param" ) == 0 ) {
       retval = hal_param_alias(name, NULL);
     } else {
         return -EINVAL;
     };
     if(retval == 0) {
         halcmd_info("%s '%s' unaliased\n",
-                    pinparam, name);
+                    type, name);
     } else {
         halcmd_error("unalias failed\n");
     }
@@ -950,6 +955,7 @@ int do_show_cmd(char *type, char **patterns)
 	print_pin_info(-1, NULL);
 	print_pin_aliases(NULL);
 	print_sig_info(-1, NULL);
+	print_sig_aliases( NULL);
 	print_param_info(-1, NULL);
 	print_param_aliases(NULL);
 	print_funct_info(NULL);
@@ -960,6 +966,7 @@ int do_show_cmd(char *type, char **patterns)
 	print_pin_info(-1, patterns);
 	print_pin_aliases(patterns);
 	print_sig_info(-1, patterns);
+	print_sig_aliases(patterns);
 	print_param_info(-1, patterns);
 	print_param_aliases(patterns);
 	print_funct_info(patterns);
@@ -989,6 +996,7 @@ int do_show_cmd(char *type, char **patterns)
 	print_thread_info(patterns);
     } else if (strcmp(type, "alias") == 0) {
 	print_pin_aliases(patterns);
+	print_sig_aliases(patterns);
 	print_param_aliases(patterns);
     } else {
 	halcmd_error("Unknown 'show' type '%s'\n", type);
@@ -1764,6 +1772,37 @@ static void print_script_sig_info(int type, char **patterns)
     halcmd_output("\n");
 }
 
+static void print_sig_aliases(char **patterns)
+{
+    int next;
+    hal_oldname_t *oldname;
+    hal_sig_t *sig;
+
+    if (scriptmode == 0) {
+	halcmd_output("Signal Aliases:\n");
+	halcmd_output(" %-*s  %s\n", HAL_NAME_LEN, "Alias", "Original Name");
+    }
+    rtapi_mutex_get(&(hal_data->mutex));
+    next = hal_data->sig_list_ptr;
+    while (next != 0) {
+	sig = SHMPTR(next);
+	if ( sig->oldname != 0 ) {
+	    /* name is an alias */
+	    oldname = SHMPTR(sig->oldname);
+	    if ( match(patterns, sig->name) || match(patterns, oldname->name) ) {
+		if (scriptmode == 0) {
+		    halcmd_output(" %-*s  %s\n", HAL_NAME_LEN, sig->name, oldname->name);
+		} else {
+		    halcmd_output(" %s  %s\n", sig->name, oldname->name);
+		}
+	    }
+	}
+	next = sig->next_ptr;
+    }
+    rtapi_mutex_give(&(hal_data->mutex));
+    halcmd_output("\n");
+}
+
 static void print_param_info(int type, char **patterns)
 {
     int next;
@@ -2089,6 +2128,7 @@ static void print_mem_status()
 {
     int active, recycled, next;
     hal_pin_t *pin;
+    hal_sig_t *sig;
     hal_param_t *param;
 
     halcmd_output("HAL memory status\n");
@@ -2114,6 +2154,12 @@ static void print_mem_status()
 	if ( pin->oldname != 0 ) active++;
 	next = pin->next_ptr;
     }
+    next = hal_data->sig_list_ptr;
+    while (next != 0) {
+	sig = SHMPTR(next);
+	if ( sig->oldname != 0 ) active++;
+	next = sig->next_ptr;
+    }
     next = hal_data->param_list_ptr;
     while (next != 0) {
 	param = SHMPTR(next);
@@ -2462,6 +2508,7 @@ static void save_aliases(FILE *dst)
 {
     int next;
     hal_pin_t *pin;
+    hal_sig_t *sig;
     hal_param_t *param;
     hal_oldname_t *oldname;
 
@@ -2477,6 +2524,17 @@ static void save_aliases(FILE *dst)
 	}
 	next = pin->next_ptr;
     }
+    fprintf(dst, "# signal aliases\n");
+    next = hal_data->sig_list_ptr;
+    while (next != 0) {
+	sig = SHMPTR(next);
+	if ( sig->oldname != 0 ) {
+	    /* name is an alias */
+	    oldname = SHMPTR(sig->oldname);
+	    fprintf(dst, "alias sig %s %s\n", oldname->name, sig->name);
+	}
+	next = sig->next_ptr;
+    }
     fprintf(dst, "# param aliases\n");
     next = hal_data->param_list_ptr;
     while (next != 0) {
@@ -2853,16 +2911,16 @@ int do_help_cmd(char *command)
 	printf("  reading from a file or stdin).\n");
     } else if (strcmp(command, "alias") == 0) {
         printf("alias type name alias\n");
-        printf("  Assigns \"alias\" as a second name for the pin or parameter\n");
+        printf("  Assigns \"alias\" as a second name for the pin, signal or parameter\n");
         printf("  \"name\".  For most operations, an alias provides a second\n");
-        printf("  name that can be used to refer to a pin or parameter, both the\n");
+        printf("  name that can be used to refer to a pin, signal or parameter, both the\n");
         printf("  original name and the alias will work.\n");
-        printf("  \"type\" must be pin or param\n");
+        printf("  \"type\" must be pin, sig or param\n");
         printf("  \"name\" must be an existing name or alias of the specified type.\n");
     } else if (strcmp(command, "unalias") == 0) {
         printf("unalias type name\n");
         printf("  Removes any alias from the pin or parameter \"name\".\n");
-        printf("  \"type\" must be pin or param\n");
+        printf("  \"type\" must be pin, sig or param\n");
         printf("  \"name\" must be an existing name or alias of the specified type.\n");
     } else if (strcmp(command, "echo") == 0) {
         printf("echo\n");
@@ -2903,7 +2961,7 @@ static void print_help_commands(void)
     printf("  status              Display status information\n");
     printf("  save                Print config as commands\n");
     printf("  start, stop         Start/stop realtime threads\n");
-    printf("  alias, unalias      Add or remove pin or parameter name aliases\n");
+    printf("  alias, unalias      Add or remove pin, signal or parameter name aliases\n");
     printf("  echo, unecho        Echo commands from stdin to stderr\n");
     printf("  quit, exit          Exit from halcmd\n");
 }
diff --git a/src/hal/utils/halcmd_completion.c b/src/hal/utils/halcmd_completion.c
index fa70ecd..e98c68b 100644
--- a/src/hal/utils/halcmd_completion.c
+++ b/src/hal/utils/halcmd_completion.c
@@ -61,7 +61,7 @@ static const char *nonRT_command_table[] = {
 };
 
 static const char *alias_table[] = {
-    "param", "pin",
+    "param", "pin", "sig",
     NULL,
 };
 
@@ -196,12 +196,12 @@ static char *parameter_generator(const char *text, int state) {
         switch (aliased) {
             case 0: // alias (if any) has not been output
                 if (param->oldname != 0) {
-                    // there's an alias, so use that and do not update the pin pointer
+                    // there's an alias, so use that and do not update the param pointer
                     hal_oldname_t *oldname = SHMPTR(param->oldname);
                     name = oldname->name;
                     aliased = 1;
                 } else {
-                    // no alias, so use the name and update the pin pointer
+                    // no alias, so use the name and update the param pointer
                     name = param->name;
                     next = param->next_ptr;
                 }
@@ -252,18 +252,45 @@ static char *attached_funct_generator(const char *text, int state) {
 static char *signal_generator(const char *text, int state) {
     static int len;
     static int next;
+    static int aliased;
+    char *name;
+
     if(!state) {
         next = hal_data->sig_list_ptr;
         len = strlen(text);
+        aliased = 0;
     }
 
     while(next) {
         hal_sig_t *sig = SHMPTR(next);
-        next = sig->next_ptr;
+        switch (aliased) {
+            case 0: // alias (if any) has not been output
+                if (sig->oldname != 0) {
+                    // there's an alias, so use that and do not update the signal pointer
+                    hal_oldname_t *oldname = SHMPTR(sig->oldname);
+                    name = oldname->name;
+                    aliased = 1;
+                } else {
+                    // no alias, so use the name and update the pin pointer
+                    name = sig->name;
+                    next = sig->next_ptr;
+                }
+            break;
+            case 1:  // there is an alias, and it has been processed already
+                name = sig->name;
+                next = sig->next_ptr;
+                aliased = 0;
+            break;
+            default:
+                // shouldn't be able to get here, so assume we're done
+                rl_attempted_completion_over = 1;
+                return NULL;
+            break;
+        }
         if ( match_type != HAL_TYPE_UNSPECIFIED && match_type != sig->type ) continue; 
         if ( !writer_match( match_direction, sig->writers ) ) continue;
-	if ( strncmp(text, sig->name, len) == 0 )
-            return strdup(sig->name);
+	if ( strncmp(text, name, len) == 0 )
+            return strdup(name);
     }
     return NULL;
 }
@@ -414,6 +441,25 @@ static char *parameter_alias_generator(const char *text, int state) {
     return NULL;
 }
 
+static char *signal_alias_generator(const char *text, int state) {
+    static int len;
+    static int next;
+
+    if(!state) {
+        next = hal_data->sig_list_ptr;
+        len = strlen(text);
+    }
+
+    while(next) {
+        hal_sig_t *sig = SHMPTR(next);
+        next = sig->next_ptr;
+        if (sig->oldname == 0) continue;  // no alias here, move along
+        if ( strncmp(text, sig->name, len) == 0 )
+            return strdup(sig->name);
+    }
+    return NULL;
+}
+
 static char *pin_alias_generator(const char *text, int state) {
     static int len;
     static int next;
@@ -631,6 +677,8 @@ char **halcmd_completer(const char *text, int start, int end, hal_completer_func
             n = nextword(buffer);
             if (startswith(n, "pin")) {
                 result = func(text, pin_alias_generator);
+            } else if (startswith(n, "sig")) {
+                result = func(text, signal_alias_generator);
             } else if (startswith(n, "param")) {
                 result = func(text, parameter_alias_generator);
             }
diff --git a/tests/save.0/expected b/tests/save.0/expected
index 25258a4..43fecb3 100644
--- a/tests/save.0/expected
+++ b/tests/save.0/expected
@@ -4,6 +4,7 @@ loadrt threads name1=fast period1=100000
 loadrt stepgen step_type=0 
 loadrt sampler cfg=bb depth=4096 
 # pin aliases
+# signal aliases
 # param aliases
 # signals
 newsig unlinked bit  
-- 
1.7.10.4

