2004-09-14 Michael Chastain <mec.gnu@mindspring.com>
[deliverable/binutils-gdb.git] / binutils / objcopy.c
index 25890176ba9a49dfd760255ac57d4de75cc0a8ff..7fd671107fa5854779e2fddf01b3dac8dea861f5 100644 (file)
@@ -1,6 +1,6 @@
 /* objcopy.c -- copy object file from input to output, optionally massaging it.
    Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003
+   2001, 2002, 2003, 2004
    Free Software Foundation, Inc.
 
    This file is part of GNU Binutils.
@@ -28,6 +28,7 @@
 #include "budbg.h"
 #include "filenames.h"
 #include "fnmatch.h"
+#include "elf-bfd.h"
 #include <sys/stat.h>
 
 /* A list of symbols to explicitly strip out, or to keep.  A linked
@@ -148,6 +149,10 @@ static bfd_vma pad_to;
 /* Use alternate machine code?  */
 static int use_alt_mach_code = 0;
 
+/* Output BFD flags user wants to set or clear */
+static flagword bfd_flags_to_set;
+static flagword bfd_flags_to_clear;
+
 /* List of sections to add.  */
 struct section_add
 {
@@ -181,7 +186,7 @@ static bfd_boolean change_leading_char = FALSE;
 /* Whether to remove the leading character from global symbol names.  */
 static bfd_boolean remove_leading_char = FALSE;
 
-/* Whether to permit wildcard in symbol comparasion.  */
+/* Whether to permit wildcard in symbol comparison.  */
 static bfd_boolean wildcard = FALSE;
 
 /* List of symbols to strip, keep, localize, keep-global, weaken,
@@ -237,7 +242,11 @@ enum command_line_switch
     OPTION_PREFIX_ALLOC_SECTIONS,
     OPTION_FORMATS_INFO,
     OPTION_ADD_GNU_DEBUGLINK,
-    OPTION_ONLY_KEEP_DEBUG
+    OPTION_ONLY_KEEP_DEBUG,
+    OPTION_READONLY_TEXT,
+    OPTION_WRITABLE_TEXT,
+    OPTION_PURE,
+    OPTION_IMPURE
   };
 
 /* Options to handle if running as "strip".  */
@@ -295,6 +304,7 @@ static struct option copy_options[] =
   {"format", required_argument, 0, 'F'}, /* Obsolete */
   {"gap-fill", required_argument, 0, OPTION_GAP_FILL},
   {"help", no_argument, 0, 'h'},
+  {"impure", no_argument, 0, OPTION_IMPURE},
   {"info", no_argument, 0, OPTION_FORMATS_INFO},
   {"input-format", required_argument, 0, 'I'}, /* Obsolete */
   {"input-target", required_argument, 0, 'I'},
@@ -316,6 +326,8 @@ static struct option copy_options[] =
   {"prefix-sections", required_argument, 0, OPTION_PREFIX_SECTIONS},
   {"prefix-alloc-sections", required_argument, 0, OPTION_PREFIX_ALLOC_SECTIONS},
   {"preserve-dates", no_argument, 0, 'p'},
+  {"pure", no_argument, 0, OPTION_PURE},
+  {"readonly-text", no_argument, 0, OPTION_READONLY_TEXT},
   {"redefine-sym", required_argument, 0, OPTION_REDEFINE_SYM},
   {"redefine-syms", required_argument, 0, OPTION_REDEFINE_SYMS},
   {"remove-leading-char", no_argument, 0, OPTION_REMOVE_LEADING_CHAR},
@@ -337,6 +349,7 @@ static struct option copy_options[] =
   {"weaken-symbol", required_argument, 0, 'W'},
   {"weaken-symbols", required_argument, 0, OPTION_WEAKEN_SYMBOLS},
   {"wildcard", no_argument, 0, 'w'},
+  {"writable-text", no_argument, 0, OPTION_WRITABLE_TEXT},
   {0, no_argument, 0, 0}
 };
 
@@ -364,6 +377,7 @@ extern unsigned long          bfd_external_machine;
 
 /* Forward declarations.  */
 static void setup_section (bfd *, asection *, void *);
+static void setup_bfd_headers (bfd *, bfd *);
 static void copy_section (bfd *, asection *, void *);
 static void get_sections (bfd *, asection *, void *);
 static int compare_section_lma (const void *, const void *);
@@ -391,12 +405,13 @@ copy_usage (FILE *stream, int exit_status)
   -g --strip-debug                 Remove all debugging symbols & sections\n\
      --strip-unneeded              Remove all symbols not needed by relocations\n\
   -N --strip-symbol <name>         Do not copy symbol <name>\n\
+     --only-keep-debug             Strip everything but the debug information\n\
   -K --keep-symbol <name>          Only copy symbol <name>\n\
   -L --localize-symbol <name>      Force symbol <name> to be marked as a local\n\
   -G --keep-global-symbol <name>   Localize all symbols except <name>\n\
   -W --weaken-symbol <name>        Force symbol <name> to be marked as a weak\n\
      --weaken                      Force all global symbols to be marked as weak\n\
-  -w --wildcard                    Permit wildcard in symbol comparasion\n\
+  -w --wildcard                    Permit wildcard in symbol comparison\n\
   -x --discard-all                 Remove all non-global symbols\n\
   -X --discard-locals              Remove any compiler-generated symbols\n\
   -i --interleave <number>         Only copy one out of every <number> bytes\n\
@@ -433,6 +448,10 @@ copy_usage (FILE *stream, int exit_status)
      --keep-global-symbols <file>  -G for all symbols listed in <file>\n\
      --weaken-symbols <file>       -W for all symbols listed in <file>\n\
      --alt-machine-code <index>    Use alternate machine code for output\n\
+     --writable-text               Mark the output text as writable\n\
+     --readonly-text               Make the output text write protected\n\
+     --pure                        Mark the output file as demand paged\n\
+     --impure                      Mark the output file as impure\n\
      --prefix-symbols <prefix>     Add <prefix> to start of every symbol name\n\
      --prefix-sections <prefix>    Add <prefix> to start of every section name\n\
      --prefix-alloc-sections <prefix>\n\
@@ -464,9 +483,10 @@ strip_usage (FILE *stream, int exit_status)
   -s --strip-all                   Remove all symbol and relocation information\n\
   -g -S -d --strip-debug           Remove all debugging symbols & sections\n\
      --strip-unneeded              Remove all symbols not needed by relocations\n\
+     --only-keep-debug             Strip everything but the debug information\n\
   -N --strip-symbol=<name>         Do not copy symbol <name>\n\
   -K --keep-symbol=<name>          Only copy symbol <name>\n\
-  -w --wildcard                    Permit wildcard in symbol comparasion\n\
+  -w --wildcard                    Permit wildcard in symbol comparison\n\
   -x --discard-all                 Remove all non-global symbols\n\
   -X --discard-locals              Remove any compiler-generated symbols\n\
   -v --verbose                     List all object files modified\n\
@@ -758,7 +778,7 @@ is_strip_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
        return FALSE;
     }
 
-  return strip_symbols == STRIP_NONDEBUG ? TRUE : FALSE;
+  return FALSE;
 }
 
 /* Choose which symbol entries to copy; put the result in OSYMS.
@@ -871,7 +891,7 @@ filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms,
        keep = (strip_symbols != STRIP_DEBUG
                && strip_symbols != STRIP_UNNEEDED
                && ! convert_debugging);
-      else if (bfd_get_section (sym)->comdat)
+      else if (bfd_coff_get_comdat_section (abfd, bfd_get_section (sym)))
        /* COMDAT sections store special information in local
           symbols, so we cannot risk stripping any of them.  */
        keep = 1;
@@ -1066,26 +1086,10 @@ add_redefine_syms_file (const char *filename)
   free (buf);
 }
 
-/* Keep only every `copy_byte'th byte in MEMHUNK, which is *SIZE bytes long.
-   Adjust *SIZE.  */
-
-static void
-filter_bytes (char *memhunk, bfd_size_type *size)
-{
-  char *from = memhunk + copy_byte, *to = memhunk, *end = memhunk + *size;
-
-  for (; from < end; from += interleave)
-    *to++ = *from;
+/* Copy object file IBFD onto OBFD.
+   Returns TRUE upon success, FALSE otherwise.  */
 
-  if (*size % interleave > (bfd_size_type) copy_byte)
-    *size = (*size / interleave) + 1;
-  else
-    *size /= interleave;
-}
-
-/* Copy object file IBFD onto OBFD.  */
-
-static void
+static bfd_boolean
 copy_object (bfd *ibfd, bfd *obfd)
 {
   bfd_vma start;
@@ -1102,13 +1106,13 @@ copy_object (bfd *ibfd, bfd *obfd)
   if (ibfd->xvec->byteorder != obfd->xvec->byteorder
       && ibfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN
       && obfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN)
-    {
-      fatal (_("Unable to change endianness of input file(s)"));
-      return;
-    }
+    fatal (_("Unable to change endianness of input file(s)"));
 
   if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
-    RETURN_NONFATAL (bfd_get_filename (obfd));
+    {
+      bfd_nonfatal (bfd_get_filename (obfd));
+      return FALSE;
+    }
 
   if (verbose)
     printf (_("copy from %s(%s) to %s(%s)\n"),
@@ -1125,11 +1129,19 @@ copy_object (bfd *ibfd, bfd *obfd)
      need to be set for a core file.  */
   if (bfd_get_format (obfd) != bfd_core)
     {
+      flagword flags;
+
+      flags = bfd_get_file_flags (ibfd);
+      flags |= bfd_flags_to_set;
+      flags &= ~bfd_flags_to_clear;
+      flags &= bfd_applicable_file_flags (obfd);
+
       if (!bfd_set_start_address (obfd, start)
-         || !bfd_set_file_flags (obfd,
-                                 (bfd_get_file_flags (ibfd)
-                                  & bfd_applicable_file_flags (obfd))))
-       RETURN_NONFATAL (bfd_get_filename (ibfd));
+         || !bfd_set_file_flags (obfd, flags))
+       {
+         bfd_nonfatal (bfd_get_filename (ibfd));
+         return FALSE;
+       }
     }
 
   /* Copy architecture of input file to output file.  */
@@ -1147,13 +1159,15 @@ copy_object (bfd *ibfd, bfd *obfd)
          non_fatal (_("Warning: Output file cannot represent architecture %s"),
                     bfd_printable_arch_mach (bfd_get_arch (ibfd),
                                              bfd_get_mach (ibfd)));
-         status = 1;
-         return;
+         return FALSE;
        }
     }
 
   if (!bfd_set_format (obfd, bfd_get_format (ibfd)))
-    RETURN_NONFATAL (bfd_get_filename (ibfd));
+    {
+      bfd_nonfatal (bfd_get_filename (ibfd));
+      return FALSE;
+    }
 
   if (isympp)
     free (isympp);
@@ -1165,6 +1179,8 @@ copy_object (bfd *ibfd, bfd *obfd)
      any output is done.  Thus, we traverse all sections multiple times.  */
   bfd_map_over_sections (ibfd, setup_section, obfd);
 
+  setup_bfd_headers (ibfd, obfd);
+
   if (add_sections != NULL)
     {
       struct section_add *padd;
@@ -1179,12 +1195,14 @@ copy_object (bfd *ibfd, bfd *obfd)
            {
              non_fatal (_("can't create section `%s': %s"),
                       padd->name, bfd_errmsg (bfd_get_error ()));
-             status = 1;
-             return;
+             return FALSE;
            }
 
          if (! bfd_set_section_size (obfd, padd->section, padd->size))
-           RETURN_NONFATAL (bfd_get_filename (obfd));
+           {
+             bfd_nonfatal (bfd_get_filename (obfd));
+             return FALSE;
+           }
 
          pset = find_section_list (padd->name, FALSE);
          if (pset != NULL)
@@ -1196,23 +1214,32 @@ copy_object (bfd *ibfd, bfd *obfd)
            flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DATA;
 
          if (! bfd_set_section_flags (obfd, padd->section, flags))
-           RETURN_NONFATAL (bfd_get_filename (obfd));
+           {
+             bfd_nonfatal (bfd_get_filename (obfd));
+             return FALSE;
+           }
 
          if (pset != NULL)
            {
              if (pset->change_vma != CHANGE_IGNORE)
                if (! bfd_set_section_vma (obfd, padd->section,
                                           pset->vma_val))
-                 RETURN_NONFATAL (bfd_get_filename (obfd));
+                 {
+                   bfd_nonfatal (bfd_get_filename (obfd));
+                   return FALSE;
+                 }
 
              if (pset->change_lma != CHANGE_IGNORE)
                {
                  padd->section->lma = pset->lma_val;
-                 
+
                  if (! bfd_set_section_alignment
                      (obfd, padd->section,
                       bfd_section_alignment (obfd, padd->section)))
-                   RETURN_NONFATAL (bfd_get_filename (obfd));
+                   {
+                     bfd_nonfatal (bfd_get_filename (obfd));
+                     return FALSE;
+                   }
                }
            }
        }
@@ -1224,7 +1251,16 @@ copy_object (bfd *ibfd, bfd *obfd)
        (obfd, gnu_debuglink_filename);
 
       if (gnu_debuglink_section == NULL)
-       RETURN_NONFATAL (gnu_debuglink_filename);
+       {
+         bfd_nonfatal (gnu_debuglink_filename);
+         return FALSE;
+       }
+    }
+
+  if (bfd_count_sections (obfd) == 0)
+    {
+      non_fatal (_("there are no sections to be copied!"));
+      return FALSE;
     }
 
   if (gap_fill_set || pad_to_set)
@@ -1314,12 +1350,18 @@ copy_object (bfd *ibfd, bfd *obfd)
   dhandle = NULL;
   symsize = bfd_get_symtab_upper_bound (ibfd);
   if (symsize < 0)
-    RETURN_NONFATAL (bfd_get_filename (ibfd));
+    {
+      bfd_nonfatal (bfd_get_filename (ibfd));
+      return FALSE;
+    }
 
   osympp = isympp = xmalloc (symsize);
   symcount = bfd_canonicalize_symtab (ibfd, isympp);
   if (symcount < 0)
-    RETURN_NONFATAL (bfd_get_filename (ibfd));
+    {
+      bfd_nonfatal (bfd_get_filename (ibfd));
+      return FALSE;
+    }
 
   if (convert_debugging)
     dhandle = read_debugging_info (ibfd, isympp, symcount);
@@ -1364,7 +1406,7 @@ copy_object (bfd *ibfd, bfd *obfd)
       if (! write_debugging_info (obfd, dhandle, &symcount, &osympp))
        {
          status = 1;
-         return;
+         return FALSE;
        }
     }
 
@@ -1381,7 +1423,10 @@ copy_object (bfd *ibfd, bfd *obfd)
        {
          if (! bfd_set_section_contents (obfd, padd->section, padd->contents,
                                          0, padd->size))
-           RETURN_NONFATAL (bfd_get_filename (obfd));
+           {
+             bfd_nonfatal (bfd_get_filename (obfd));
+             return FALSE;
+           }
        }
     }
 
@@ -1389,7 +1434,10 @@ copy_object (bfd *ibfd, bfd *obfd)
     {
       if (! bfd_fill_in_gnu_debuglink_section
          (obfd, gnu_debuglink_section, gnu_debuglink_filename))
-       RETURN_NONFATAL (gnu_debuglink_filename);
+       {
+         bfd_nonfatal (gnu_debuglink_filename);
+         return FALSE;
+       }
     }
 
   if (gap_fill_set || pad_to_set)
@@ -1425,7 +1473,10 @@ copy_object (bfd *ibfd, bfd *obfd)
 
                  if (! bfd_set_section_contents (obfd, osections[i], buf,
                                                  off, now))
-                   RETURN_NONFATAL (bfd_get_filename (obfd));
+                   {
+                     bfd_nonfatal (bfd_get_filename (obfd));
+                     return FALSE;
+                   }
 
                  left -= now;
                  off += now;
@@ -1448,18 +1499,17 @@ copy_object (bfd *ibfd, bfd *obfd)
       non_fatal (_("%s: error copying private BFD data: %s"),
                 bfd_get_filename (obfd),
                 bfd_errmsg (bfd_get_error ()));
-      status = 1;
-      return;
+      return FALSE;
     }
 
   /* Switch to the alternate machine code.  We have to do this at the
      very end, because we only initialize the header when we create
      the first section.  */
-  if (use_alt_mach_code != 0)
-    {
-      if (!bfd_alt_mach_code (obfd, use_alt_mach_code))
-       non_fatal (_("unknown alternate machine code, ignored"));
-    }
+  if (use_alt_mach_code != 0
+      && ! bfd_alt_mach_code (obfd, use_alt_mach_code))
+    non_fatal (_("unknown alternate machine code, ignored"));
+
+  return TRUE;
 }
 
 #undef MKDIR
@@ -1506,6 +1556,7 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
       bfd *last_element;
       struct stat buf;
       int stat_status = 0;
+      bfd_boolean delete = TRUE;
 
       /* Create an output file for this member.  */
       output_name = concat (dir, "/",
@@ -1547,7 +1598,7 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
        RETURN_NONFATAL (output_name);
 
       if (bfd_check_format (this_element, bfd_object))
-       copy_object (this_element, output_bfd);
+       delete = ! copy_object (this_element, output_bfd);
 
       if (!bfd_close (output_bfd))
        {
@@ -1556,22 +1607,30 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target)
          status = 1;
        }
 
-      if (preserve_dates && stat_status == 0)
-       set_times (output_name, &buf);
+      if (delete)
+       {
+         unlink (output_name);
+         status = 1;
+       }
+      else
+       {
+         if (preserve_dates && stat_status == 0)
+           set_times (output_name, &buf);
 
-      /* Open the newly output file and attach to our list.  */
-      output_bfd = bfd_openr (output_name, output_target);
+         /* Open the newly output file and attach to our list.  */
+         output_bfd = bfd_openr (output_name, output_target);
 
-      l->obfd = output_bfd;
+         l->obfd = output_bfd;
 
-      *ptr = output_bfd;
-      ptr = &output_bfd->next;
+         *ptr = output_bfd;
+         ptr = &output_bfd->next;
 
-      last_element = this_element;
+         last_element = this_element;
 
-      this_element = bfd_openr_next_archived_file (ibfd, last_element);
+         this_element = bfd_openr_next_archived_file (ibfd, last_element);
 
-      bfd_close (last_element);
+         bfd_close (last_element);
+       }
     }
   *ptr = NULL;
 
@@ -1635,7 +1694,9 @@ copy_file (const char *input_filename, const char *output_filename,
   else if (bfd_check_format_matches (ibfd, bfd_object, &obj_matching))
     {
       bfd *obfd;
+      bfd_boolean delete;
     do_copy:
+
       /* bfd_get_target does not return the correct value until
          bfd_check_format succeeds.  */
       if (output_target == NULL)
@@ -1645,13 +1706,19 @@ copy_file (const char *input_filename, const char *output_filename,
       if (obfd == NULL)
        RETURN_NONFATAL (output_filename);
 
-      copy_object (ibfd, obfd);
+      delete = ! copy_object (ibfd, obfd);
 
       if (!bfd_close (obfd))
        RETURN_NONFATAL (output_filename);
 
       if (!bfd_close (ibfd))
        RETURN_NONFATAL (input_filename);
+
+      if (delete)
+       {
+         unlink (output_filename);
+         status = 1;
+       }
     }
   else
     {
@@ -1744,6 +1811,32 @@ find_section_rename (bfd * ibfd ATTRIBUTE_UNUSED, sec_ptr isection,
   return old_name;
 }
 
+/* Once each of the sections is copied, we may still need to do some
+   finalization work for private section headers.  Do that here.  */
+
+static void
+setup_bfd_headers (bfd *ibfd, bfd *obfd)
+{
+  const char *err;
+
+  /* Allow the BFD backend to copy any private data it understands
+     from the input section to the output section.  */
+  if (! bfd_copy_private_header_data (ibfd, obfd))
+    {
+      err = _("private header data");
+      goto loser;
+    }
+
+  /* All went well.  */
+  return;
+
+loser:
+  non_fatal (_("%s: error in %s: %s"),
+            bfd_get_filename (ibfd),
+            err, bfd_errmsg (bfd_get_error ()));
+  status = 1;
+}
+
 /* Create a section in OBFD with the same
    name and attributes as ISECTION in IBFD.  */
 
@@ -1846,6 +1939,13 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
 
   if (p != NULL && p->set_flags)
     flags = p->flags | (flags & (SEC_HAS_CONTENTS | SEC_RELOC));
+  else if (strip_symbols == STRIP_NONDEBUG && (flags & SEC_ALLOC) != 0)
+    {
+      flags &= ~(SEC_HAS_CONTENTS | SEC_LOAD);
+      if (obfd->xvec->flavour == bfd_target_elf_flavour)
+       elf_section_type (osection) = SHT_NOBITS;
+    }
+
   if (!bfd_set_section_flags (obfd, osection, flags))
     {
       err = _("flags");
@@ -1914,7 +2014,7 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
     return;
 
   osection = isection->output_section;
-  size = bfd_get_section_size_before_reloc (isection);
+  size = bfd_get_section_size (isection);
 
   if (size == 0 || osection == 0)
     return;
@@ -1966,11 +2066,10 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
        }
 
       bfd_set_reloc (obfd, osection, relcount == 0 ? NULL : relpp, relcount);
+      if (relcount == 0)
+       free (relpp);
     }
 
-  isection->_cooked_size = isection->_raw_size;
-  isection->reloc_done = TRUE;
-
   if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS
       && bfd_get_section_flags (obfd, osection) & SEC_HAS_CONTENTS)
     {
@@ -1980,7 +2079,18 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg)
        RETURN_NONFATAL (bfd_get_filename (ibfd));
 
       if (copy_byte >= 0)
-       filter_bytes (memhunk, &size);
+       {
+         /* Keep only every `copy_byte'th byte in MEMHUNK.  */
+         char *from = (char *) memhunk + copy_byte;
+         char *to = memhunk;
+         char *end = (char *) memhunk + size;
+
+         for (; from < end; from += interleave)
+           *to++ = *from;
+
+         size = (size + interleave - 1 - copy_byte) / interleave;
+         osection->lma /= interleave;
+       }
 
       if (!bfd_set_section_contents (obfd, osection, memhunk, 0, size))
        RETURN_NONFATAL (bfd_get_filename (obfd));
@@ -2051,9 +2161,9 @@ compare_section_lma (const void *arg1, const void *arg2)
     return -1;
 
   /* Sort sections with the same LMA by size.  */
-  if ((*sec1)->_raw_size > (*sec2)->_raw_size)
+  if (bfd_get_section_size (*sec1) > bfd_get_section_size (*sec2))
     return 1;
-  else if ((*sec1)->_raw_size < (*sec2)->_raw_size)
+  else if (bfd_get_section_size (*sec1) < bfd_get_section_size (*sec2))
     return -1;
 
   return 0;
@@ -2782,6 +2892,26 @@ copy_main (int argc, char *argv[])
          prefix_alloc_sections_string = optarg;
          break;
 
+       case OPTION_READONLY_TEXT:
+         bfd_flags_to_set |= WP_TEXT;
+         bfd_flags_to_clear &= ~WP_TEXT;
+         break;
+
+       case OPTION_WRITABLE_TEXT:
+         bfd_flags_to_clear |= WP_TEXT;
+         bfd_flags_to_set &= ~WP_TEXT;
+         break;
+
+       case OPTION_PURE:
+         bfd_flags_to_set |= D_PAGED;
+         bfd_flags_to_clear &= ~D_PAGED;
+         break;
+
+       case OPTION_IMPURE:
+         bfd_flags_to_clear |= D_PAGED;
+         bfd_flags_to_set &= ~D_PAGED;
+         break;
+
        case 0:
          /* We've been given a long option.  */
          break;
This page took 0.033455 seconds and 4 git commands to generate.