// if at exit of function, idx < 0, the line could not be parsed
        private Tuple <int, int> parse_relative_part(string l, relative_pos part, ref int idx)
        {
            if (part.is_end_of_string)
            {
                // return remainder of the string
                int at = idx;
                idx = l.Length;
                return(new Tuple <int, int>(at, l.Length - at));
            }

            int start = -1, end = -1;

            if (part.start >= 0)
            {
                start = part.start;
            }
            else
            {
                if (idx >= l.Length)
                {
                    // passed the end of string
                    return(new Tuple <int, int>(-1, -1));
                }

                start = l.IndexOf(part.start_str, idx);
                idx   = start >= 0 ? start + part.start_str.Length : -1;
                if (start >= 0)
                {
                    start += part.start_str.Length;
                }
            }

            if (idx >= 0)
            {
                if (part.len >= 0)
                {
                    end = start + part.len;
                    idx = end;
                }
                else
                {
                    end = l.IndexOf(part.end_str, idx);
                    idx = end >= 0 ? end + part.end_str.Length : -1;
                }
            }

            return(new Tuple <int, int>(start, end - start));
        }
        // if at exit of function, idx < 0, the line could not be parsed
        private Tuple<int, int> parse_relative_part(string l, relative_pos part, ref int idx)
        {
            if (part.is_end_of_string) {
                // return remainder of the string
                int at = idx;
                idx = l.Length;
                return new Tuple<int, int>(at, l.Length - at);
            }

            int start = -1, end = -1;
            if (part.start >= 0)
                start = part.start;
            else {
                if ( idx >= l.Length)
                    // passed the end of string
                    return new Tuple<int, int>(-1, -1);

                start = l.IndexOf(part.start_str, idx);
                idx = start >= 0 ? start + part.start_str.Length : -1;
                if (start >= 0)
                    start += part.start_str.Length;
            }

            if ( idx >= 0)
                if (part.len >= 0) {
                    end = start + part.len;
                    idx = end;
                } else {
                    end = l.IndexOf(part.end_str, idx);
                    idx = end >= 0 ? end + part.end_str.Length : -1;
                }

            return new Tuple<int, int>(start, end - start);
        }