/* Define variable named NAME with value VALUE in SET.  VALUE is copied. LENGTH is the length of NAME, which does not need to be null-terminated.
         * ORIGIN specifies the origin of the variable (makefile, command line or environment).
         * If RECURSIVE is nonzero a flag is set in the variable saying that it should be recursively re-expanded.  */
        public static variable define_variable_in_set(string name, string value, variable_origin origin, bool recursive, Dictionary <string, variable> set)
        {
            Form1.UpdateVariablesTable(name, false);
            variable v;

            if (set == null)
            {
                set = current_variable_set_list;
            }

            if (set.ContainsKey(name))
            {
                v = set[name];
            }
            else
            {
                v      = new variable();
                v.name = name;
                set.Add(v.name, v);
                v.value       = value;
                v.special     = false;
                v.expanding   = false;
                v.exp_count   = 0;
                v.per_target  = false;
                v.append      = false;
                v.private_var = false;
                v.export      = variable_export.v_default;
                v.exportable  = true;
                if (name[0] != '_' && (name[0] < 'A' || name[0] > 'Z') && (name[0] < 'a' || name[0] > 'z'))
                {
                    v.exportable = false;
                }
                else
                {
                    int index = 1;
                    int temp;
                    for (; index < name.Length; index++)
                    {
                        if (name[index] != '_' && (name[index] < 'a' || name[index] > 'z') && (name[index] < 'A' || name[index] > 'Z') && !int.TryParse(name[index] + "", out temp))
                        {
                            break;
                        }
                    }
                    if (index != name.Length)
                    {
                        v.exportable = false;
                    }
                }
            }
            v.value        = value;
            v.origin       = origin;
            v.recursive    = recursive;
            v.unresolved   = false;
            v.isvaluegraph = false;
            v.printed      = false;
            return(v);
        }
        /* Try to interpret LINE (a null-terminated string) as a variable definition.
         * ORIGIN may be o_file, o_override, o_env, o_env_override, or o_command specifying that the variable definition comes
         * from a makefile, an override directive, the environment with or without the -e switch, or the command line.
         *
         * See the comments for assign_variable_definition().
         * If LINE was recognized as a variable definition, a pointer to its `struct variable' is returned.
         * If LINE is not a variable definition, NULL is returned.  */
        public static variable try_variable_definition(string line, variable_origin origin, int target_var)
        {
            variable v = new variable();

            if (assign_variable_definition(ref v, line) == null)
            {
                return(null);
            }

            return(do_variable_definition(v.name, v.value, origin, v.flavor, target_var));
        }
        // Execute a `undefine' directive. The undefine line has already been read, and NAME is the name of the variable to be undefined.
        public static void do_undefine(string name, variable_origin origin)
        {
            string p   = "";
            string var = "";

            // Expand the variable name and find the beginning (NAME) and end.
            var  = ExpandString(name);
            name = Utils.next_token(var);

            if (name == null || name.Length == 0)
            {
                MessageBox.Show("Empty variable name at line(" + Read.LINENO + ")");
            }

            p = name.TrimEnd(new char[] { ' ' });
            undefine_variable(name, null);
        }
        // Given a variable, a value, and a flavor, define the variable. See the try_variable_definition() function for details on the parameters.
        public static variable do_variable_definition(string varname, string value, variable_origin origin, variable_flavor flavor, int target_var)
        {
            string   p           = "";
            string   alloc_value = "";
            variable v;
            bool     append      = false;
            bool     conditional = false;

            // Calculate the variable's new value in VALUE.
            switch (flavor)
            {
            default:
            case variable_flavor.f_bogus:
                //Should not be possible.
                MessageBox.Show("Abort!");
                break;

            case variable_flavor.f_simple:
                /* A simple variable definition "var := value".  Expand the value. We have to allocate memory since otherwise it'll clobber the
                 * variable buffer, and we may still need that if we're looking at a target-specific variable.  */
                p = alloc_value = ExpandString(value);
                break;

            case variable_flavor.f_conditional:
                // A conditional variable definition "var ?= value". The value is set IFF the variable is not defined yet.
                v = lookup_variable(varname);
                if (v != null)
                {
                    return(v.special? set_special_var(v) : v);
                }
                conditional = true;
                flavor      = variable_flavor.f_recursive;
                p           = value;
                break;

            case variable_flavor.f_recursive:
                // A recursive variable definition "var = value". The value is used verbatim.
                p = value;
                break;

            case variable_flavor.f_append:
            {
                // If we have += but we're in a target variable context, we want to append only with other variables in the context of this target.
                if (target_var == 1)
                {
                    append = true;
                    v      = lookup_variable_in_set(varname, varname.Length, current_variable_set_list);

                    // Don't append from the global set if a previous non-appending target-specific variable definition exists.
                    if (v != null && !v.append)
                    {
                        append = false;
                    }
                }
                else
                {
                    v = lookup_variable(varname);
                }

                if (v == null)
                {
                    // There was no old value. This becomes a normal recursive definition.
                    p      = value;
                    flavor = variable_flavor.f_recursive;
                }
                else
                {
                    // Paste the old and new values together in VALUE.
                    string val = "";
                    string tp  = "";
                    val = value;
                    if (v.recursive)
                    {
                        // The previous definition of the variable was recursive. The new value is the unexpanded old and new values.
                        flavor = variable_flavor.f_recursive;
                    }
                    else
                    {
                        /* The previous definition of the variable was simple. The new value comes from the old value, which was expanded
                         * when it was set; and from the expanded new value.  Allocate memory for the expansion as we may still need the rest of the
                         * buffer if we're looking at a target-specific variable.  */
                        val = tp = ExpandString(val);
                    }

                    p = alloc_value = v.value + " " + val;
                }
                break;
            }
            }

            /* If we are defining variables inside an $(eval ...), we might have a
             * different variable context pushed, not the global context (maybe we're inside a $(call ...) or something.  Since this function is only ever
             * invoked in places where we want to define globally visible variables, make sure we define this variable in the global set.  */

            v             = define_variable_in_set(varname, p, origin, (flavor == variable_flavor.f_recursive), (target_var == 1 ? current_variable_set_list : null));
            v.append      = append;
            v.conditional = conditional;
            return(v.special ? set_special_var(v) : v);
        }