diff --git a/.gitignore b/.gitignore index b55ce1e23..b587873d4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ Makefile autom4te.cache/ build/ command_list.txt +command_list_toc.txt confdefs.h config.h config.h.in diff --git a/Makefile.in b/Makefile.in index 937456332..1bca59912 100644 --- a/Makefile.in +++ b/Makefile.in @@ -11,7 +11,7 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # # @@ -160,11 +160,6 @@ HDR_FILES := $(HDR_FILES_SRC:.hdr.in=.hdr) # Use a pattern rule so that Make knows to only issue one invocation # per http://www.gnu.org/software/make/manual/make.html#Pattern-Intro -# Internalized scripts are currently disabled. -# For now, we just generate empty arrays. -# To generate them again, you would run this: -# ./internalize_scripts.py share/functions/*.fish share/completions/*.fish - # # Files containing documentation for external commands. @@ -775,7 +770,6 @@ distclean: clean clean: rm -f *.o doc.h doc.tmp doc_src/*.doxygen doc_src/*.cpp doc_src/*.o doc_src/commands.hdr - rm -f $(GENERATED_INTERN_SCRIPT_FILES) rm -f tests/tmp.err tests/tmp.out tests/tmp.status tests/foo.txt rm -f $(PROGRAMS) fish_tests key_reader rm -f command_list.txt command_list_toc.txt toc.txt diff --git a/builtin.cpp b/builtin.cpp index f0e918381..a97efdbf6 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -832,7 +832,8 @@ static int builtin_block(parser_t &parser, wchar_t **argv) } else { - block_t *block=parser.current_block; + size_t block_idx = 0; + block_t *block = parser.block_at_index(block_idx); event_blockage_t eb = {}; eb.typemask = type; @@ -841,20 +842,24 @@ static int builtin_block(parser_t &parser, wchar_t **argv) { case LOCAL: { - if (!block->outer) - block=0; + // If this is the outermost block, then we're global + if (block_idx + 1 >= parser.block_count()) + { + block = NULL; + } break; } case GLOBAL: { - block=0; + block=NULL; } case UNSET: { - while (block && - block->type() != FUNCTION_CALL && - block->type() != FUNCTION_CALL_NO_SHADOW) - block = block->outer; + while (block != NULL && block->type() != FUNCTION_CALL && block->type() != FUNCTION_CALL_NO_SHADOW) + { + // Set it in function scope + block = parser.block_at_index(++block_idx); + } } } if (block) @@ -1864,18 +1869,21 @@ static int builtin_function(parser_t &parser, wchar_t **argv) if (is_subshell) { - block_t *b = parser.current_block; - - while (b && (b->type() != SUBST)) - b = b->outer; - - if (b) + size_t block_idx = 0; + + /* Find the outermost substitution block */ + for (block_idx = 0; ; block_idx++) { - b=b->outer; + const block_t *b = parser.block_at_index(block_idx); + if (b == NULL || b->type() == SUBST) + break; } - if (b->job) + + /* Go one step beyond that, to get to the caller */ + const block_t *caller_block = parser.block_at_index(block_idx + 1); + if (caller_block != NULL && caller_block->job != NULL) { - job_id = b->job->job_id; + job_id = caller_block->job->job_id; } } @@ -2059,8 +2067,8 @@ static int builtin_function(parser_t &parser, wchar_t **argv) } } - parser.current_block->tok_pos = parser.get_pos(); - parser.current_block->skip = 1; + parser.current_block()->tok_pos = parser.get_pos(); + parser.current_block()->skip = 1; return STATUS_BUILTIN_OK; } @@ -2713,7 +2721,7 @@ static int builtin_status(parser_t &parser, wchar_t **argv) case STACK_TRACE: { - parser.stack_trace(parser.current_block, stdout_buffer); + parser.stack_trace(0, stdout_buffer); break; } @@ -2728,7 +2736,7 @@ static int builtin_status(parser_t &parser, wchar_t **argv) job_control_mode==JOB_CONTROL_INTERACTIVE?_(L"Only on interactive jobs"): (job_control_mode==JOB_CONTROL_NONE ? _(L"Never") : _(L"Always"))); - parser.stack_trace(parser.current_block, stdout_buffer); + parser.stack_trace(0, stdout_buffer); break; } } @@ -3400,19 +3408,19 @@ static int builtin_for(parser_t &parser, wchar_t **argv) } else { - parser.current_block->skip=1; + parser.current_block()->skip=1; } } return res; } /** - The begin builtin. Creates a nex block. + The begin builtin. Creates a new block. */ static int builtin_begin(parser_t &parser, wchar_t **argv) { parser.push_block(new scope_block_t(BEGIN)); - parser.current_block->tok_pos = parser.get_pos(); + parser.current_block()->tok_pos = parser.get_pos(); return proc_get_last_status(); } @@ -3424,7 +3432,7 @@ static int builtin_begin(parser_t &parser, wchar_t **argv) */ static int builtin_end(parser_t &parser, wchar_t **argv) { - if (!parser.current_block->outer) + if (! parser.block_at_index(1)) { append_format(stderr_buffer, _(L"%ls: Not inside of block\n"), @@ -3442,7 +3450,8 @@ static int builtin_end(parser_t &parser, wchar_t **argv) */ bool kill_block = true; - switch (parser.current_block->type()) + block_t * const current_block = parser.current_block(); + switch (current_block->type()) { case WHILE: { @@ -3450,13 +3459,13 @@ static int builtin_end(parser_t &parser, wchar_t **argv) If this is a while loop, we rewind the loop unless it's the last lap, in which case we continue. */ - if (!(parser.current_block->skip && (parser.current_block->loop_status != LOOP_CONTINUE))) + if (!(current_block->skip && (current_block->loop_status != LOOP_CONTINUE))) { - parser.current_block->loop_status = LOOP_NORMAL; - parser.current_block->skip = 0; + current_block->loop_status = LOOP_NORMAL; + current_block->skip = 0; kill_block = false; - parser.set_pos(parser.current_block->tok_pos); - while_block_t *blk = static_cast(parser.current_block); + parser.set_pos(current_block->tok_pos); + while_block_t *blk = static_cast(current_block); blk->status = WHILE_TEST_AGAIN; } @@ -3479,9 +3488,9 @@ static int builtin_end(parser_t &parser, wchar_t **argv) /* set loop variable to next element, and rewind to the beginning of the block. */ - for_block_t *fb = static_cast(parser.current_block); + for_block_t *fb = static_cast(current_block); wcstring_list_t &for_vars = fb->sequence; - if (parser.current_block->loop_status == LOOP_BREAK) + if (current_block->loop_status == LOOP_BREAK) { for_vars.clear(); } @@ -3492,18 +3501,18 @@ static int builtin_end(parser_t &parser, wchar_t **argv) for_vars.pop_back(); const wcstring &for_variable = fb->variable; env_set(for_variable, val.c_str(), ENV_LOCAL); - parser.current_block->loop_status = LOOP_NORMAL; - parser.current_block->skip = 0; + current_block->loop_status = LOOP_NORMAL; + current_block->skip = 0; kill_block = false; - parser.set_pos(parser.current_block->tok_pos); + parser.set_pos(current_block->tok_pos); } break; } case FUNCTION_DEF: { - function_def_block_t *fdb = static_cast(parser.current_block); + function_def_block_t *fdb = static_cast(current_block); function_data_t &d = fdb->function_data; if (d.name.empty()) @@ -3522,8 +3531,8 @@ static int builtin_end(parser_t &parser, wchar_t **argv) for the specified function */ - wchar_t *def = wcsndup(parser.get_buffer()+parser.current_block->tok_pos, - parser.get_job_pos()-parser.current_block->tok_pos); + wchar_t *def = wcsndup(parser.get_buffer()+current_block->tok_pos, + parser.get_job_pos()-current_block->tok_pos); d.definition = def; function_add(d, parser); @@ -3556,9 +3565,9 @@ static int builtin_else(parser_t &parser, wchar_t **argv) { bool block_ok = false; if_block_t *if_block = NULL; - if (parser.current_block != NULL && parser.current_block->type() == IF) + if (parser.current_block() != NULL && parser.current_block()->type() == IF) { - if_block = static_cast(parser.current_block); + if_block = static_cast(parser.current_block()); /* Ensure that we're past IF but not up to an ELSE */ if (if_block->if_expr_evaluated && ! if_block->else_evaluated) { @@ -3599,7 +3608,6 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv) int is_break = (wcscmp(argv[0],L"break")==0); int argc = builtin_count_args(argv); - block_t *b = parser.current_block; if (argc != 1) { @@ -3612,15 +3620,16 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv) return STATUS_BUILTIN_ERROR; } - - while ((b != 0) && - (b->type() != WHILE) && - (b->type() != FOR)) + /* Find the index of the enclosing for or while loop. Recall that incrementing loop_idx goes 'up' to outer blocks */ + size_t loop_idx; + for (loop_idx = 0; loop_idx < parser.block_count(); loop_idx++) { - b = b->outer; + const block_t *b = parser.block_at_index(loop_idx); + if (b->type() == WHILE || b->type() == FOR) + break; } - if (b == 0) + if (loop_idx >= parser.block_count()) { append_format(stderr_buffer, _(L"%ls: Not inside of loop\n"), @@ -3629,15 +3638,17 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv) return STATUS_BUILTIN_ERROR; } - b = parser.current_block; - while ((b->type() != WHILE) && - (b->type() != FOR)) + /* Skip blocks interior to the loop */ + size_t block_idx = loop_idx; + while (block_idx--) { - b->skip=1; - b = b->outer; + parser.block_at_index(block_idx)->skip = true; } - b->skip=1; - b->loop_status = is_break?LOOP_BREAK:LOOP_CONTINUE; + + /* Skip the loop itself */ + block_t *loop_block = parser.block_at_index(loop_idx); + loop_block->skip = true; + loop_block->loop_status = is_break ? LOOP_BREAK : LOOP_CONTINUE; return STATUS_BUILTIN_OK; } @@ -3666,8 +3677,6 @@ static int builtin_return(parser_t &parser, wchar_t **argv) int argc = builtin_count_args(argv); int status = proc_get_last_status(); - block_t *b = parser.current_block; - switch (argc) { case 1: @@ -3696,15 +3705,16 @@ static int builtin_return(parser_t &parser, wchar_t **argv) return STATUS_BUILTIN_ERROR; } - - while ((b != 0) && - (b->type() != FUNCTION_CALL && - b->type() != FUNCTION_CALL_NO_SHADOW)) + /* Find the function block */ + size_t function_block_idx; + for (function_block_idx = 0; function_block_idx < parser.block_count(); function_block_idx++) { - b = b->outer; + const block_t *b = parser.block_at_index(function_block_idx); + if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) + break; } - if (b == 0) + if (function_block_idx >= parser.block_count()) { append_format(stderr_buffer, _(L"%ls: Not inside of function\n"), @@ -3712,17 +3722,15 @@ static int builtin_return(parser_t &parser, wchar_t **argv) builtin_print_help(parser, argv[0], stderr_buffer); return STATUS_BUILTIN_ERROR; } - - b = parser.current_block; - while ((b->type() != FUNCTION_CALL && - b->type() != FUNCTION_CALL_NO_SHADOW)) + + /* Skip everything up to (and then including) the function block */ + for (size_t i=0; i < function_block_idx; i++) { + block_t *b = parser.block_at_index(i); b->mark_as_fake(); - b->skip=1; - b = b->outer; + b->skip = true; } - b->skip=1; - + parser.block_at_index(function_block_idx)->skip = true; return status; } @@ -3749,7 +3757,7 @@ static int builtin_switch(parser_t &parser, wchar_t **argv) else { parser.push_block(new switch_block_t(argv[1])); - parser.current_block->skip=1; + parser.current_block()->skip=1; res = proc_get_last_status(); } @@ -3766,7 +3774,7 @@ static int builtin_case(parser_t &parser, wchar_t **argv) int i; wchar_t *unescaped=0; - if (parser.current_block->type() != SWITCH) + if (parser.current_block()->type() != SWITCH) { append_format(stderr_buffer, _(L"%ls: 'case' command while not in switch block\n"), @@ -3775,8 +3783,8 @@ static int builtin_case(parser_t &parser, wchar_t **argv) return STATUS_BUILTIN_ERROR; } - parser.current_block->skip = 1; - switch_block_t *sb = static_cast(parser.current_block); + parser.current_block()->skip = 1; + switch_block_t *sb = static_cast(parser.current_block()); if (sb->switch_taken) { return proc_get_last_status(); @@ -3793,7 +3801,7 @@ static int builtin_case(parser_t &parser, wchar_t **argv) if (match) { - parser.current_block->skip = 0; + parser.current_block()->skip = 0; sb->switch_taken = true; break; } diff --git a/doc_src/eval.txt b/doc_src/eval.txt index 0227255a0..f8af8f666 100644 --- a/doc_src/eval.txt +++ b/doc_src/eval.txt @@ -8,7 +8,7 @@ \subsection eval-example Example -The folloing code will call the ls command. Note that \c fish does not +The following code will call the ls command. Note that \c fish does not support the use of environment variables as direct commands; \c eval can be used to work around this. diff --git a/doc_src/license.hdr b/doc_src/license.hdr index 64bab10f0..c07a94ad2 100644 --- a/doc_src/license.hdr +++ b/doc_src/license.hdr @@ -17,7 +17,7 @@ Version 2, June 1991
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.
@@ -995,7 +995,7 @@ Version 2, June 1991
 
 
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.
diff --git a/event.cpp b/event.cpp
index f6313fb0c..d2b219e7d 100644
--- a/event.cpp
+++ b/event.cpp
@@ -144,12 +144,15 @@ static int event_match(const event_t &classv, const event_t &instance)
 */
 static int event_is_blocked(const event_t &e)
 {
-    block_t *block;
+    const block_t *block;
     parser_t &parser = parser_t::principal_parser();
-    for (block = parser.current_block; block; block = block->outer)
+    
+    size_t idx = 0;
+    while ((block = parser.block_at_index(idx++)))
     {
         if (event_block_list_blocks_type(block->event_blocks, e.type))
             return true;
+        
     }
     return event_block_list_blocks_type(parser.global_event_blocks, e.type);
 }
diff --git a/exec.cpp b/exec.cpp
index b894ce690..6d7a19e88 100644
--- a/exec.cpp
+++ b/exec.cpp
@@ -580,7 +580,8 @@ static void exec_no_exec(parser_t &parser, const job_t *job)
             }
             else if (builtin_name == L"end")
             {
-                if (parser.current_block == NULL || parser.current_block->type() == TOP)
+                const block_t *block = parser.current_block();
+                if (block == NULL || block->type() == TOP)
                 {
                     fprintf(stderr, "Warning: not popping the root block\n");
                 }
diff --git a/fish.cpp b/fish.cpp
index ca5d50e90..57d268d72 100644
--- a/fish.cpp
+++ b/fish.cpp
@@ -12,7 +12,7 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */
 
 
diff --git a/fish_indent.cpp b/fish_indent.cpp
index c4d6d81cd..4560bb961 100644
--- a/fish_indent.cpp
+++ b/fish_indent.cpp
@@ -12,7 +12,7 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */
 
 
diff --git a/function.cpp b/function.cpp
index 5e75b3ba5..d10698745 100644
--- a/function.cpp
+++ b/function.cpp
@@ -189,11 +189,11 @@ void function_add(const function_data_t &data, const parser_t &parser)
 
     /* Create and store a new function */
     const wchar_t *filename = reader_current_filename();
-    
+
     int def_offset = -1;
-    if (parser.current_block != NULL)
+    if (parser.current_block() != NULL)
     {
-        def_offset = parser.line_number_of_character_at_offset(parser.current_block->tok_pos);
+        def_offset = parser.line_number_of_character_at_offset(parser.current_block()->tok_pos);
     }
     
     const function_map_t::value_type new_pair(data.name, function_info_t(data, filename, def_offset, is_autoload));
diff --git a/parser.cpp b/parser.cpp
index cd43b41a4..c93ae0713 100644
--- a/parser.cpp
+++ b/parser.cpp
@@ -328,7 +328,6 @@ parser_t::parser_t(enum parser_type_t type, bool errors) :
     current_tokenizer_pos(0),
     job_start_pos(0),
     eval_level(-1),
-    current_block(NULL),
     block_io(shared_ptr())
 {
 }
@@ -354,39 +353,36 @@ void parser_t::skip_all_blocks(void)
     if (s_principal_parser)
     {
         //write(2, "Cancelling blocks\n", strlen("Cancelling blocks\n"));
-        block_t *c = s_principal_parser->current_block;
-        while (c)
+        for (size_t i=0; i < s_principal_parser->block_count(); i++)
         {
-            c->skip = true;
-            //fprintf(stderr, "   Cancelled %p\n", c);
-            c = c->outer;
+            s_principal_parser->block_at_index(i)->skip = true;
         }
     }
 }
 
-void parser_t::push_block(block_t *newv)
+void parser_t::push_block(block_t *new_current)
 {
-    const enum block_type_t type = newv->type();
-    newv->src_lineno = parser_t::get_lineno();
-    newv->src_filename = parser_t::current_filename()?intern(parser_t::current_filename()):0;
-    
-    newv->outer = current_block;
-    if (current_block && current_block->skip)
-        newv->mark_as_fake();
+    const enum block_type_t type = new_current->type();
+    new_current->src_lineno = parser_t::get_lineno();
+    new_current->src_filename = parser_t::current_filename()?intern(parser_t::current_filename()):0;
+
+    const block_t *old_current = this->current_block();
+    if (old_current && old_current->skip)
+        new_current->mark_as_fake();
 
     /*
       New blocks should be skipped if the outer block is skipped,
       except TOP ans SUBST block, which open up new environments. Fake
       blocks should always be skipped. Rather complicated... :-(
     */
-    newv->skip=current_block?current_block->skip:0;
+    new_current->skip = old_current ? old_current->skip : 0;
 
     /*
       Type TOP and SUBST are never skipped
     */
     if (type == TOP || type == SUBST)
     {
-        newv->skip = 0;
+        new_current->skip = 0;
     }
 
     /*
@@ -394,27 +390,26 @@ void parser_t::push_block(block_t *newv)
     */
     if (type == FAKE || type == FUNCTION_DEF)
     {
-        newv->skip = 1;
+        new_current->skip = 1;
     }
 
-    newv->job = 0;
-    newv->loop_status=LOOP_NORMAL;
+    new_current->job = 0;
+    new_current->loop_status=LOOP_NORMAL;
+    
+    this->block_stack.push_back(new_current);
 
-    current_block = newv;
-
-    if ((newv->type() != FUNCTION_DEF) &&
-            (newv->type() != FAKE) &&
-            (newv->type() != TOP))
+    if ((new_current->type() != FUNCTION_DEF) &&
+            (new_current->type() != FAKE) &&
+            (new_current->type() != TOP))
     {
         env_push(type == FUNCTION_CALL);
-        newv->wants_pop_env = true;
+        new_current->wants_pop_env = true;
     }
 }
 
 void parser_t::pop_block()
 {
-    block_t *old = current_block;
-    if (!current_block)
+    if (block_stack.empty())
     {
         debug(1,
               L"function %s called on empty block stack.",
@@ -423,7 +418,8 @@ void parser_t::pop_block()
         return;
     }
 
-    current_block = current_block->outer;
+    block_t *old = block_stack.back();
+    block_stack.pop_back();
 
     if (old->wants_pop_env)
         env_pop();
@@ -443,6 +439,30 @@ const wchar_t *parser_t::get_block_desc(int block) const
     return _(UNKNOWN_BLOCK);
 }
 
+const block_t *parser_t::block_at_index(size_t idx) const
+{
+    /* 0 corresponds to the last element in our vector */
+    size_t count = block_stack.size();
+    return idx < count ? block_stack.at(count - idx - 1) : NULL;
+}
+
+block_t *parser_t::block_at_index(size_t idx)
+{
+    size_t count = block_stack.size();
+    return idx < count ? block_stack.at(count - idx - 1) : NULL;
+}
+
+const block_t *parser_t::current_block() const
+{
+    return block_stack.empty() ? NULL : block_stack.back();
+}
+
+block_t *parser_t::current_block()
+{
+    return block_stack.empty() ? NULL : block_stack.back();
+}
+
+
 /**
    Returns 1 if the specified command is a builtin that may not be used in a pipeline
 */
@@ -807,13 +827,15 @@ void parser_t::eval_args(const wchar_t *line, std::vector &args)
         proc_pop_interactive();
 }
 
-void parser_t::stack_trace(block_t *b, wcstring &buff) const
+void parser_t::stack_trace(size_t block_idx, wcstring &buff) const
 {
     /*
       Check if we should end the recursion
     */
-    if (!b)
+    if (block_idx >= this->block_count())
         return;
+    
+    const block_t *b = this->block_at_index(block_idx);
 
     if (b->type()==EVENT)
     {
@@ -908,7 +930,7 @@ void parser_t::stack_trace(block_t *b, wcstring &buff) const
     /*
       Recursively print the next block
     */
-    parser_t::stack_trace(b->outer, buff);
+    parser_t::stack_trace(block_idx + 1, buff);
 }
 
 /**
@@ -921,22 +943,19 @@ const wchar_t *parser_t::is_function() const
 {
     // PCA: Have to make this a string somehow
     ASSERT_IS_MAIN_THREAD();
-    wcstring result;
 
-    block_t *b = current_block;
-    while (1)
+    const wchar_t *result = NULL;
+    for (size_t block_idx = 0; block_idx < this->block_count(); block_idx++)
     {
-        if (!b)
-        {
-            return NULL;
-        }
+        const block_t *b = this->block_at_index(block_idx);
         if (b->type() == FUNCTION_CALL)
         {
             const function_block_t *fb = static_cast(b);
-            return fb->name.c_str();
+            result = fb->name.c_str();
+            break;
         }
-        b=b->outer;
     }
+    return result;
 }
 
 
@@ -974,21 +993,17 @@ const wchar_t *parser_t::current_filename() const
     ASSERT_IS_MAIN_THREAD();
     assert(this == &principal_parser());
 
-    block_t *b = current_block;
 
-    while (1)
+    for (size_t i=0; i < this->block_count(); i++)
     {
-        if (!b)
-        {
-            return reader_current_filename();
-        }
+        const block_t *b = this->block_at_index(i);
         if (b->type() == FUNCTION_CALL)
         {
             const function_block_t *fb = static_cast(b);
             return function_get_definition_file(fb->name);
         }
-        b=b->outer;
     }
+    return reader_current_filename();
 }
 
 /**
@@ -1129,7 +1144,7 @@ const wchar_t *parser_t::current_line()
     }
 
     free((void *)line);
-    parser_t::stack_trace(current_block, lineinfo);
+    parser_t::stack_trace(0, lineinfo);
 
     return lineinfo.c_str();
 }
@@ -1239,7 +1254,7 @@ job_t *parser_t::job_get_from_pid(int pid)
    \param j the job to which the process belongs to
    \param tok the tokenizer to read options from
    \param args the argument list to insert options into
-   \param args unskip whether we should ignore current_block->skip. Big hack because of our dumb handling of if statements.
+   \param args unskip whether we should ignore current_block()->skip. Big hack because of our dumb handling of if statements.
 */
 void parser_t::parse_job_argument_list(process_t *p,
                                        job_t *j,
@@ -1337,7 +1352,7 @@ void parser_t::parse_job_argument_list(process_t *p,
                 {
                     skip = 1;
                 }
-                else if (current_block->skip && ! unskip)
+                else if (current_block()->skip && ! unskip)
                 {
                     /*
                       If this command should be skipped, we do not expand the arguments
@@ -1345,12 +1360,12 @@ void parser_t::parse_job_argument_list(process_t *p,
                     skip=1;
 
                     /* But if this is in fact a case statement or an elseif statement, then it should be evaluated */
-                    block_type_t type = current_block->type();
+                    block_type_t type = current_block()->type();
                     if (type == SWITCH && args.at(0).completion == L"case" && p->type == INTERNAL_BUILTIN)
                     {
                         skip=0;
                     }
-                    else if (job_get_flag(j, JOB_ELSEIF) && ! job_should_skip_elseif(j, current_block))
+                    else if (job_get_flag(j, JOB_ELSEIF) && ! job_should_skip_elseif(j, current_block()))
                     {
                         skip=0;
                     }
@@ -1358,7 +1373,7 @@ void parser_t::parse_job_argument_list(process_t *p,
                 else
                 {
                     /* If this is an else if, and we should skip it, then don't expand any arguments */
-                    if (job_get_flag(j, JOB_ELSEIF) && job_should_skip_elseif(j, current_block))
+                    if (job_get_flag(j, JOB_ELSEIF) && job_should_skip_elseif(j, current_block()))
                     {
                         skip = 1;
                     }
@@ -1441,7 +1456,7 @@ void parser_t::parse_job_argument_list(process_t *p,
                   Otherwise, bogus errors may be the result. (Do check
                   that token is string, though)
                 */
-                if (current_block->skip && ! unskip)
+                if (current_block()->skip && ! unskip)
                 {
                     tok_next(tok);
                     if (tok_last_type(tok) != TOK_STRING)
@@ -1756,7 +1771,7 @@ int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
     bool unskip = false;    // Maybe we are an elseif inside an if block; if so we may want to evaluate this even if the if block is currently set to skip
     bool allow_bogus_command = false; // If we are an elseif that will not be executed, or an AND or OR that will have been short circuited, don't complain about non-existent commands
 
-    block_t *prev_block = current_block;
+    const block_t *prev_block = current_block();
     scoped_push tokenizer_pos_push(¤t_tokenizer_pos, tok_get_pos(tok));
 
     while (args.empty())
@@ -1909,11 +1924,11 @@ int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
             tok_next(tok);
             while_block_t *wb = NULL;
 
-            if ((current_block->type() != WHILE))
+            if ((current_block()->type() != WHILE))
             {
                 new_block = true;
             }
-            else if ((wb = static_cast(current_block))->status == WHILE_TEST_AGAIN)
+            else if ((wb = static_cast(current_block()))->status == WHILE_TEST_AGAIN)
             {
                 wb->status = WHILE_TEST_FIRST;
             }
@@ -1951,9 +1966,9 @@ int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
             const int else_pos = tok_get_pos(tok);
             /* See if we have any more arguments, that is, whether we're ELSE IF ... or just ELSE. */
             tok_next(tok);
-            if (tok_last_type(tok) == TOK_STRING && current_block->type() == IF)
+            if (tok_last_type(tok) == TOK_STRING && current_block()->type() == IF)
             {
-                const if_block_t *ib = static_cast(current_block);
+                const if_block_t *ib = static_cast(current_block());
 
                 /* If we've already encountered an else, complain */
                 if (ib->else_evaluated)
@@ -1994,7 +2009,7 @@ int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
             continue;
         }
 
-        if (use_function && (unskip || ! current_block->skip))
+        if (use_function && (unskip || ! current_block()->skip))
         {
             bool nxt_forbidden=false;
             wcstring forbid;
@@ -2008,9 +2023,8 @@ int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
               block scopes are pushed on function invocation changes,
               then this check will break.
             */
-            if ((current_block->type() == TOP) &&
-                    (current_block->outer) &&
-                    (current_block->outer->type() == FUNCTION_CALL))
+            const block_t *current = this->block_at_index(0), *parent = this->block_at_index(1);
+            if (current && parent && current->type() == TOP && parent->type() == FUNCTION_CALL)
                 is_function_call = 1;
 
             /*
@@ -2019,7 +2033,7 @@ int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
               may not be called, since that would mean an infinite
               recursion.
             */
-            if (is_function_call && !current_block->had_command)
+            if (is_function_call && !current->had_command)
             {
                 forbid = forbidden_function.empty() ? wcstring(L"") : forbidden_function.back();
                 if (forbid == nxt)
@@ -2066,7 +2080,7 @@ int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
               If we are not executing the current block, allow
               non-existent commands.
             */
-            if (current_block->skip && ! unskip)
+            if (current_block()->skip && ! unskip)
                 allow_bogus_command = true; //note this may already be true for other reasons
 
             if (allow_bogus_command)
@@ -2311,7 +2325,7 @@ int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
 
                 tok_set_pos(tok, (int)end_pos);
 
-                while (prev_block != current_block)
+                while (prev_block != this->current_block())
                 {
                     parser_t::pop_block();
                 }
@@ -2340,7 +2354,7 @@ int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
     {
         if (!is_new_block)
         {
-            current_block->had_command = true;
+            current_block()->had_command = true;
         }
     }
 
@@ -2349,7 +2363,7 @@ int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
         /*
           Make sure the block stack is consistent
         */
-        while (prev_block != current_block)
+        while (prev_block != current_block())
         {
             parser_t::pop_block();
         }
@@ -2383,7 +2397,8 @@ void parser_t::skipped_exec(job_t * j)
             }
             else if (wcscmp(p->argv0(), L"end")==0)
             {
-                if (!current_block->outer->skip)
+                const block_t *parent = this->block_at_index(1);
+                if (parent && ! parent->skip)
                 {
                     exec_job(*this, j);
                     return;
@@ -2392,10 +2407,10 @@ void parser_t::skipped_exec(job_t * j)
             }
             else if (wcscmp(p->argv0(), L"else")==0)
             {
-                if (current_block->type() == IF)
+                if (current_block()->type() == IF)
                 {
                     /* Evaluate this ELSE if the IF expression failed, and so has every ELSEIF (if any) expression thus far */
-                    const if_block_t *ib = static_cast(current_block);
+                    const if_block_t *ib = static_cast(current_block());
                     if (ib->if_expr_evaluated && ! ib->any_branch_taken)
                     {
                         exec_job(*this, j);
@@ -2405,7 +2420,7 @@ void parser_t::skipped_exec(job_t * j)
             }
             else if (wcscmp(p->argv0(), L"case")==0)
             {
-                if (current_block->type() == SWITCH)
+                if (current_block()->type() == SWITCH)
                 {
                     exec_job(*this, j);
                     return;
@@ -2679,7 +2694,7 @@ void parser_t::eval_job(tokenizer_t *tok)
                          || is_event \
                          || (!get_is_interactive()));
 
-            current_block->job = j;
+            current_block()->job = j;
 
             if (get_is_interactive())
             {
@@ -2714,27 +2729,27 @@ void parser_t::eval_job(tokenizer_t *tok)
                 {
                     t2 = get_time();
                     profile_item->cmd = j->command();
-                    profile_item->skipped=current_block->skip;
+                    profile_item->skipped=current_block()->skip;
                 }
 
                 /* If we're an ELSEIF, then we may want to unskip, if we're skipping because of an IF */
                 if (job_get_flag(j, JOB_ELSEIF))
                 {
-                    bool skip_elseif = job_should_skip_elseif(j, current_block);
+                    bool skip_elseif = job_should_skip_elseif(j, current_block());
 
                     /* Record that we're entering an elseif */
                     if (! skip_elseif)
                     {
                         /* We must be an IF block here */
-                        assert(current_block->type() == IF);
-                        static_cast(current_block)->is_elseif_entry = true;
+                        assert(current_block()->type() == IF);
+                        static_cast(current_block())->is_elseif_entry = true;
                     }
 
                     /* Record that in the block too. This is similar to what builtin_else does. */
-                    current_block->skip = skip_elseif;
+                    current_block()->skip = skip_elseif;
                 }
 
-                skip = skip || current_block->skip;
+                skip = skip || current_block()->skip;
                 skip = skip || job_get_flag(j, JOB_WILDCARD_ERROR);
                 skip = skip || job_get_flag(j, JOB_SKIP);
 
@@ -2763,9 +2778,9 @@ void parser_t::eval_job(tokenizer_t *tok)
                     profile_item->exec=(int)(t3-t2);
                 }
 
-                if (current_block->type() == WHILE)
+                if (current_block()->type() == WHILE)
                 {
-                    while_block_t *wb = static_cast(current_block);
+                    while_block_t *wb = static_cast(current_block());
                     switch (wb->status)
                     {
                         case WHILE_TEST_FIRST:
@@ -2779,9 +2794,9 @@ void parser_t::eval_job(tokenizer_t *tok)
                     }
                 }
 
-                if (current_block->type() == IF)
+                if (current_block()->type() == IF)
                 {
-                    if_block_t *ib = static_cast(current_block);
+                    if_block_t *ib = static_cast(current_block());
 
                     if (ib->skip)
                     {
@@ -2794,7 +2809,7 @@ void parser_t::eval_job(tokenizer_t *tok)
                         ib->any_branch_taken = if_result;
 
                         /* Don't execute if the expression failed */
-                        current_block->skip = ! if_result;
+                        current_block()->skip = ! if_result;
                         ib->if_expr_evaluated = true;
                     }
                     else if (ib->is_elseif_entry && ! ib->any_branch_taken)
@@ -2802,7 +2817,7 @@ void parser_t::eval_job(tokenizer_t *tok)
                         /* Maybe mark an ELSEIF branch as taken */
                         bool elseif_taken = (proc_get_last_status() == 0);
                         ib->any_branch_taken = elseif_taken;
-                        current_block->skip = ! elseif_taken;
+                        current_block()->skip = ! elseif_taken;
                         ib->is_elseif_entry = false;
                     }
                 }
@@ -2820,7 +2835,7 @@ void parser_t::eval_job(tokenizer_t *tok)
 
                 proc_set_last_status(1);
             }
-            current_block->job = 0;
+            current_block()->job = 0;
             break;
         }
 
@@ -3014,7 +3029,7 @@ int parser_t::eval(const wcstring &cmd_str, const io_chain_t &io, enum block_typ
     const wchar_t * const cmd = cmd_str.c_str();
     size_t forbid_count;
     int code;
-    const block_t *start_current_block = current_block;
+    const block_t *start_current_block = current_block();
 
     /* Record the current chain so we can put it back later */
     scoped_push block_io_push(&block_io, io);
@@ -3068,9 +3083,9 @@ int parser_t::eval(const wcstring &cmd_str, const io_chain_t &io, enum block_typ
 
     parser_t::pop_block();
 
-    while (start_current_block != current_block)
+    while (start_current_block != current_block())
     {
-        if (current_block == 0)
+        if (current_block() == NULL)
         {
             debug(0,
                   _(L"End of block mismatch. Program terminating."));
@@ -3085,7 +3100,7 @@ int parser_t::eval(const wcstring &cmd_str, const io_chain_t &io, enum block_typ
             //debug( 2, L"Status %d\n", proc_get_last_status() );
 
             debug(1,
-                  L"%ls", parser_t::get_block_desc(current_block->type()));
+                  L"%ls", parser_t::get_block_desc(current_block()->type()));
             debug(1,
                   BLOCK_END_ERR_MSG);
             fwprintf(stderr, L"%ls", parser_t::current_line());
@@ -3380,8 +3395,7 @@ block_t::block_t(block_type_t t) :
     src_filename(),
     src_lineno(),
     wants_pop_env(false),
-    event_blocks(),
-    outer(NULL)
+    event_blocks()
 {
 }
 
diff --git a/parser.h b/parser.h
index 878b2b574..8ee2fb82a 100644
--- a/parser.h
+++ b/parser.h
@@ -135,11 +135,6 @@ public:
     /** List of event blocks. */
     event_blockage_list_t event_blocks;
 
-    /**
-     Next outer block
-    */
-    block_t *outer;
-
     /** Destructor */
     virtual ~block_t();
 };
@@ -305,7 +300,6 @@ class parser_t
 {
 private:
     enum parser_type_t parser_type;
-    std::vector blocks;
 
     /** Whether or not we output errors */
     const bool show_errors;
@@ -336,6 +330,9 @@ private:
 
     /** The jobs associated with this parser */
     job_list_t my_job_list;
+    
+    /** The list of blocks, allocated with new. It's our responsibility to delete these */
+    std::vector block_stack;
 
     /**
        Keeps track of how many recursive eval calls have been made. Eval
@@ -385,9 +382,6 @@ public:
     /** Create a parser of the given type */
     parser_t(enum parser_type_t type, bool show_errors);
 
-    /** The current innermost block, allocated with new */
-    block_t *current_block;
-
     /** Global event blocks */
     event_blockage_list_t global_event_blocks;
 
@@ -450,6 +444,20 @@ public:
 
     /** Set the current position in the latest string of the tokenizer. */
     void set_pos(int p);
+    
+    /** Returns the block at the given index. 0 corresponds to the innermost block. Returns NULL when idx is at or equal to the number of blocks. */
+    const block_t *block_at_index(size_t idx) const;
+    block_t *block_at_index(size_t idx);
+    
+    /** Returns the current (innermost) block */
+    const block_t *current_block() const;
+    block_t *current_block();
+    
+    /** Count of blocks */
+    size_t block_count() const
+    {
+        return block_stack.size();
+    }
 
     /** Get the string currently parsed */
     const wchar_t *get_buffer() const;
@@ -542,7 +550,7 @@ public:
     /**
        Write a stack trace starting at the specified block to the specified wcstring
     */
-    void stack_trace(block_t *b, wcstring &buff) const;
+    void stack_trace(size_t block_idx, wcstring &buff) const;
 
     int get_block_type(const wchar_t *cmd) const;
     const wchar_t *get_block_command(int type) const;
diff --git a/reader.cpp b/reader.cpp
index 1506ae3e9..5186b787d 100644
--- a/reader.cpp
+++ b/reader.cpp
@@ -2755,14 +2755,11 @@ static void handle_end_loop()
     job_t *j;
     int stopped_jobs_count=0;
     int is_breakpoint=0;
-    block_t *b;
-    parser_t &parser = parser_t::principal_parser();
+    const parser_t &parser = parser_t::principal_parser();
 
-    for (b = parser.current_block;
-            b;
-            b = b->outer)
+    for (size_t i = 0; i < parser.block_count(); i++)
     {
-        if (b->type() == BREAKPOINT)
+        if (parser.block_at_index(i)->type() == BREAKPOINT)
         {
             is_breakpoint = 1;
             break;
diff --git a/share/completions/adb.fish b/share/completions/adb.fish
new file mode 100644
index 000000000..0eef3421f
--- /dev/null
+++ b/share/completions/adb.fish
@@ -0,0 +1,140 @@
+# Completions for Android adb command
+
+function __fish_adb_no_subcommand --description 'Test if adb has yet to be given the subcommand'
+	for i in (commandline -opc)
+		if contains -- $i connect disconnect devices push pull sync shell emu logcat install uninstall jdwp forward bugreport backup restore version help wait-for-device start-server kill-server remount reboot get-state get-serialno get-devpath status-window root usb tcpip ppp
+			return 1
+		end
+	end
+	return 0
+end
+
+function __fish_adb_get_devices --description 'Run adb devices and parse output'
+	# This seems reasonably portable for all the platforms adb runs on
+	set -l count (ps x | grep -c adb)
+	set -l TAB \t
+	# Don't run adb devices unless the server is already started - it takes a while to init
+	if [ $count -gt 1 ]
+		# The tail is to strip the header line, the sed is to massage the -l format
+		# into a simple "identifier  modelname" format which is what we want for complete
+		adb devices -l | tail -n +2 | sed -E -e "s/([^ ]+) +./\1$TAB/" -e "s/$TAB.*model:([^ ]+).*/$TAB\1/"
+	end
+end
+
+function __fish_adb_run_command --description 'Runs adb with any -s parameters already given on the command line'
+	set -l sopt
+	set -l sopt_is_next
+	set -l cmd (commandline -poc)
+	set -e cmd[1]
+	for i in $cmd
+		if test $sopt_is_next
+			set sopt -s $i
+			break
+		else
+			switch $i
+				case -s
+					set sopt_is_next 1
+			end
+		end
+	end
+
+	# If no -s option, see if there's a -d or -e instead
+	if test -z "$sopt"
+		if contains -- -d $cmd
+			set sopt '-d'
+		else if contains -- -e $cmd
+			set sopt '-e'
+		end
+	end
+
+	# adb returns CRLF (seemingly) so strip CRs
+	adb $sopt shell $argv | sed s/\r//
+end
+
+function __fish_adb_list_packages
+	__fish_adb_run_command pm list packages | sed s/package://
+end
+
+
+function __fish_adb_list_uninstallable_packages
+	# -3 doesn't exactly mean show uninstallable, but it's the closest you can get to with pm list
+	__fish_adb_run_command pm list packages -3 | sed s/package://
+end
+
+# Generic options, must come before command
+complete -n '__fish_adb_no_subcommand' -c adb -s s -x -a "(__fish_adb_get_devices)" -d 'Device to communicate with'
+complete -n '__fish_adb_no_subcommand' -c adb -s d -d 'Communicate with first USB device'
+complete -n '__fish_adb_no_subcommand' -c adb -s e -d 'Communicate with emulator'
+
+# Commands
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'connect' -d 'Connect to device'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'disconnect' -d 'Disconnect from device'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'devices' -d 'List all connected devices'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'push' -d 'Copy file to device'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'pull' -d 'Copy file from device'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'sync' -d 'Copy host->device only if changed'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'shell' -d 'Run remote shell [command]'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'emu' -d 'Run emulator console command'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'logcat' -d 'View device log'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'install' -d 'Install package'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'uninstall' -d 'Uninstall package'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'jdwp' -d 'List PIDs of processes hosting a JDWP transport'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'forward' -d 'Port forwarding'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'bugreport' -d 'Return bugreport information'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'backup' -d 'Perform device backup'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'restore' -d 'Restore device from backup'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'version' -d 'Show adb version'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'help' -d 'Show adb help'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'wait-for-device' -d 'Block until device is online'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'start-server' -d 'Ensure that there is a server running'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'kill-server' -d 'Kill the server if it is running'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'remount' -d 'Remounts the /system partition on the device read-write'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'reboot' -d 'Reboots the device, optionally into the bootloader or recovery program'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'get-state' -d 'Prints state of the device'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'get-serialno' -d 'Prints serial number of the device'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'get-devpath' -d 'Prints device path'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'status-window' -d 'Continuously print the device status'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'root' -d 'Restart the adbd daemon with root permissions'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'usb' -d 'Restart the adbd daemon listening on USB'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'tcpip' -d 'Restart the adbd daemon listening on TCP'
+complete -f -n '__fish_adb_no_subcommand' -c adb -a 'ppp' -d 'Run PPP over USB'
+
+# install options
+complete -n '__fish_seen_subcommand_from install' -c adb -s l -d 'Forward-lock the app'
+complete -n '__fish_seen_subcommand_from install' -c adb -s r -d 'Reinstall the app keeping its data'
+complete -n '__fish_seen_subcommand_from install' -c adb -s s -d 'Install on SD card instead of internal storage'
+complete -n '__fish_seen_subcommand_from install' -c adb -l 'algo' -d 'Algorithm name'
+complete -n '__fish_seen_subcommand_from install' -c adb -l 'key' -d 'Hex-encoded key'
+complete -n '__fish_seen_subcommand_from install' -c adb -l 'iv' -d 'Hex-encoded iv'
+
+# uninstall
+complete -n '__fish_seen_subcommand_from uninstall' -c adb -s k -d 'Keep the data and cache directories'
+complete -n '__fish_seen_subcommand_from uninstall' -c adb -f -u -a "(__fish_adb_list_uninstallable_packages)"
+
+# devices
+complete -n '__fish_seen_subcommand_from devices' -c adb -s l -d 'Also list device qualifiers'
+
+# disconnect
+complete -n '__fish_seen_subcommand_from disconnect' -c adb -x -a "(__fish_adb_get_devices)" -d 'Device to disconnect'
+
+# backup
+complete -n '__fish_seen_subcommand_from backup' -c adb -s f -d 'File to write backup data to'
+complete -n '__fish_seen_subcommand_from backup' -c adb -o 'apk' -d 'Enable backup of the .apks themselves'
+complete -n '__fish_seen_subcommand_from backup' -c adb -o 'noapk' -d 'Disable backup of the .apks themselves (default)'
+complete -n '__fish_seen_subcommand_from backup' -c adb -o 'obb' -d 'Enable backup of any installed apk expansion'
+complete -n '__fish_seen_subcommand_from backup' -c adb -o 'noobb' -d 'Disable backup of any installed apk expansion (default)'
+complete -n '__fish_seen_subcommand_from backup' -c adb -o 'shared' -d 'Enable backup of the device\'s shared storage / SD card contents'
+complete -n '__fish_seen_subcommand_from backup' -c adb -o 'noshared' -d 'Disable backup of the device\'s shared storage / SD card contents (default)'
+complete -n '__fish_seen_subcommand_from backup' -c adb -o 'all' -d 'Back up all installed applications'
+complete -n '__fish_seen_subcommand_from backup' -c adb -o 'system' -d 'Include system applications in -all (default)'
+complete -n '__fish_seen_subcommand_from backup' -c adb -o 'nosystem' -d 'Exclude system applications in -all'
+complete -n '__fish_seen_subcommand_from backup' -c adb -f -a "(__fish_adb_list_packages)" -d 'Package(s) to backup'
+
+# reboot
+complete -n '__fish_seen_subcommand_from reboot' -c adb -x -a 'bootloader recovery'
+
+# forward
+complete -n '__fish_seen_subcommand_from forward' -c adb -l 'list' -d 'List all forward socket connections'
+complete -n '__fish_seen_subcommand_from forward' -c adb -l 'no-rebind' -d 'Fails the forward if local is already forwarded'
+complete -n '__fish_seen_subcommand_from forward' -c adb -l 'remove' -d 'Remove a specific forward socket connection'
+complete -n '__fish_seen_subcommand_from forward' -c adb -l 'remove-all' -d 'Remove all forward socket connections'
diff --git a/share/config.fish b/share/config.fish
index 6b62a01c3..5eac39bf3 100644
--- a/share/config.fish
+++ b/share/config.fish
@@ -89,10 +89,12 @@ function __fish_reconstruct_path -d "Update PATH when fish_user_paths changes" -
 
 	set -e __fish_added_user_paths
 	for x in $fish_user_paths[-1..1]
-		if not contains $x $local_path
-			set local_path $x $local_path
+		if set -l idx (contains --index $x $local_path)
+			set -e local_path[$idx]
+		else
 			set -g __fish_added_user_paths $__fish_added_user_paths $x
 		end
+		set local_path $x $local_path
 	end
 	set -xg PATH $local_path
 end
diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish
index ba59c5fa8..0619a0972 100644
--- a/share/functions/__fish_git_prompt.fish
+++ b/share/functions/__fish_git_prompt.fish
@@ -371,6 +371,7 @@ function __fish_git_prompt --description "Prompt function for Git"
 	set -l informative_status
 
 	__fish_git_prompt_validate_chars
+	__fish_git_prompt_validate_colors
 
 	if test "true" = $inside_worktree
 		if test -n "$__fish_git_prompt_show_informative_status"
@@ -403,8 +404,6 @@ function __fish_git_prompt --description "Prompt function for Git"
 		end
 	end
 
-	__fish_git_prompt_validate_colors
-
 	set -l branch_color $___fish_git_prompt_color_branch
 	set -l branch_done  $___fish_git_prompt_color_branch_done
 	if test -n "$__fish_git_prompt_showcolorhints"
diff --git a/share/functions/__fish_print_svn_rev.fish b/share/functions/__fish_print_svn_rev.fish
index 7b8638dca..cbc3770a3 100644
--- a/share/functions/__fish_print_svn_rev.fish
+++ b/share/functions/__fish_print_svn_rev.fish
@@ -1,5 +1,5 @@
 function __fish_print_svn_rev --description 'Print svn revisions'
-	svn info | grep "Last Changed Rev" | cut --delimiter " " --fields 4
+	svn info | grep "Last Changed Rev" | cut -d " " -f 4
 	echo \{\tRevision at start of the date
 	echo HEAD\tLatest in repository
 	echo BASE\tBase rev of item\'s working copy
diff --git a/xdgmime.cpp b/xdgmime.cpp
index 39d0a1894..633d0b0a5 100644
--- a/xdgmime.cpp
+++ b/xdgmime.cpp
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA
  */
 
 #ifdef HAVE_CONFIG_H
diff --git a/xdgmime.h b/xdgmime.h
index b552be7b7..9c53bb11a 100644
--- a/xdgmime.h
+++ b/xdgmime.h
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA
  */
 
 
diff --git a/xdgmimealias.cpp b/xdgmimealias.cpp
index 05a5e3579..e556d4e64 100644
--- a/xdgmimealias.cpp
+++ b/xdgmimealias.cpp
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
diff --git a/xdgmimealias.h b/xdgmimealias.h
index 384d12769..13933642d 100644
--- a/xdgmimealias.h
+++ b/xdgmimealias.h
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
  */
 
 #ifndef __XDG_MIME_ALIAS_H__
diff --git a/xdgmimeglob.cpp b/xdgmimeglob.cpp
index 49d098bf0..4182f3c97 100644
--- a/xdgmimeglob.cpp
+++ b/xdgmimeglob.cpp
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
diff --git a/xdgmimeglob.h b/xdgmimeglob.h
index 7f2ea1d3f..136e49a39 100644
--- a/xdgmimeglob.h
+++ b/xdgmimeglob.h
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
  */
 
 #ifndef __XDG_MIME_GLOB_H__
diff --git a/xdgmimeint.cpp b/xdgmimeint.cpp
index a7847141a..7e8ea31ca 100644
--- a/xdgmimeint.cpp
+++ b/xdgmimeint.cpp
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
diff --git a/xdgmimeint.h b/xdgmimeint.h
index 33116df1d..5e825dab3 100644
--- a/xdgmimeint.h
+++ b/xdgmimeint.h
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
  */
 
 #ifndef __XDG_MIME_INT_H__
diff --git a/xdgmimemagic.cpp b/xdgmimemagic.cpp
index 8bcca9488..aecca8822 100644
--- a/xdgmimemagic.cpp
+++ b/xdgmimemagic.cpp
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
diff --git a/xdgmimemagic.h b/xdgmimemagic.h
index 61e9d20db..1a747583a 100644
--- a/xdgmimemagic.h
+++ b/xdgmimemagic.h
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
  */
 
 #ifndef __XDG_MIME_MAGIC_H__
diff --git a/xdgmimeparent.cpp b/xdgmimeparent.cpp
index 3e51c295c..d0fa617dc 100644
--- a/xdgmimeparent.cpp
+++ b/xdgmimeparent.cpp
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
diff --git a/xdgmimeparent.h b/xdgmimeparent.h
index 9baf44a3e..ca6e85267 100644
--- a/xdgmimeparent.h
+++ b/xdgmimeparent.h
@@ -21,8 +21,8 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
  */
 
 #ifndef __XDG_MIME_PARENT_H__