This is the mail archive of the gdb-patches@sourceware.cygnus.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[rfa/cli] Tighten add_set_enum_cmd() interface


Hello,

The attatched changes the add_set_enum_cmd() interface from:

  struct cmd_list_element *
  add_set_enum_cmd (char *name,
                  enum command_class class,
!                 char *enumlist[],
!                 char **var,
                  char *doc,
                  struct cmd_list_element **list)

to:

  struct cmd_list_element *
  add_set_enum_cmd (char *name,
                  enum command_class class,
!                 const char *enumlist[],
!                 const char **var,
                  char *doc,
                  struct cmd_list_element **list)
 
ie, the two arrays become constant.  Two reasons for putting forward
this:

	o	It better reflects reality.

		In all uses of add_set_enum_cmd()
		the ENUMLIST was either
		explicitly (arm-tdep.c) or implicitly
		being cast from a ``const char *''.

	o	It flushes out a memory
		leak in infrun.c.

	o	It simplifies the way
		the follow fork command
		is handled.

I think I've covered all needed changes.  Apart from infrun.c, the
changes were mechanical.

	Andrew

PS: Besides it fixes a hack I added to arch-utils.c:``set architecture''
:-)
Wed Jun  7 15:13:04 2000  Andrew Cagney  <cagney@b1.cygnus.com>

	* command.h (add_set_enum_cmd): Make ``enumlist'' and ``var''
 	constant char pointers.
	(complete_on_enum): Change ``enumlist'' to a constant char
 	pointer.
	(struct cmd_list_element): Ditto for member ``enums''.
	* command.c (complete_on_enum, add_set_enum_cmd,
 	do_setshow_command): Update.

	* infrun.c (follow_fork_mode_ask, follow_fork_mode_parent,
 	follow_fork_mode_both, follow_fork_mode_child): New.  Use to
 	construct the follow_fork_mode_kind_names.
	(set_follow_fork_mode_command): Delete function.
	(_initialize_infrun): Update.
	(follow_inferior_fork): Do not strdup follow_fork_mode_string.
  	Use follow_fork_mode_* variables directly instead.  Call
 	internal_error instead of error when unimplemented "ask" mode.

	* infrun.c (scheduler_enums, scheduler_mode, schedlock_off,
 	schedlock_on, schedlock_step): Update.
	* serial.c (serial_logbase, logbase_hex, logbase_octal,
 	logbase_ascii, logbase_enums): Update.
	* remote.c (packet_support_enums, packet_support_auto,
 	packet_enable, packet_disable, struct packet_config): Update.
	* arch-utils.c (initialize_current_architecture,
 	set_architecture_string): Update.
	(endian_big, endian_little, endian_auto, endian_enum,
 	set_endian_string): Update.
	* i386-tdep.c (valid_flavors, att_flavor, intel_flavor,
 	disassembly_flavor): Update.
	* mips-tdep.c (size_enums, size_64, size_32, size_auto,
 	mips_stack_argsize_string, mips_saved_regsize_string): Update.
	* arm-tdep.c (disassembly_flavor, valid_flavors): Update.
	(_initialize_arm_tdep): Ditto.

Index: gdb/arch-utils.c
===================================================================
RCS file: /cvs/src/src/gdb/arch-utils.c,v
retrieving revision 1.7
diff -p -r1.7 arch-utils.c
*** arch-utils.c	2000/06/07 04:38:02	1.7
--- arch-utils.c	2000/06/07 06:59:15
*************** generic_register_convertible_not (num)
*** 249,265 ****
  int target_byte_order = TARGET_BYTE_ORDER_DEFAULT;
  int target_byte_order_auto = 1;
  
! static char endian_big[] = "big";
! static char endian_little[] = "little";
! static char endian_auto[] = "auto";
! static char *endian_enum[] =
  {
    endian_big,
    endian_little,
    endian_auto,
    NULL,
  };
! static char *set_endian_string;
  
  /* Called by ``show endian''.  */
  
--- 249,265 ----
  int target_byte_order = TARGET_BYTE_ORDER_DEFAULT;
  int target_byte_order_auto = 1;
  
! static const char endian_big[] = "big";
! static const char endian_little[] = "little";
! static const char endian_auto[] = "auto";
! static const char *endian_enum[] =
  {
    endian_big,
    endian_little,
    endian_auto,
    NULL,
  };
! static const char *set_endian_string;
  
  /* Called by ``show endian''.  */
  
*************** enum set_arch { set_arch_auto, set_arch_
*** 352,358 ****
  
  int target_architecture_auto = 1;
  
! char *set_architecture_string;
  
  /* Old way of changing the current architecture. */
  
--- 352,358 ----
  
  int target_architecture_auto = 1;
  
! const char *set_architecture_string;
  
  /* Old way of changing the current architecture. */
  
*************** initialize_current_architecture (void)
*** 585,591 ****
         of ``const char *''.  We just happen to know that the casts are
         safe. */
      c = add_set_enum_cmd ("architecture", class_support,
! 			  (char **) arches, (char **) &set_architecture_string,
  			  "Set architecture of target.",
  			  &setlist);
      c->function.sfunc = set_architecture;
--- 585,591 ----
         of ``const char *''.  We just happen to know that the casts are
         safe. */
      c = add_set_enum_cmd ("architecture", class_support,
! 			  arches, &set_architecture_string,
  			  "Set architecture of target.",
  			  &setlist);
      c->function.sfunc = set_architecture;
Index: gdb/arm-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/arm-tdep.c,v
retrieving revision 1.6
diff -p -r1.6 arm-tdep.c
*** arm-tdep.c	2000/05/16 03:03:13	1.6
--- arm-tdep.c	2000/06/07 06:59:23
*************** static char * arm_register_name_strings[
*** 52,61 ****
  char **arm_register_names = arm_register_name_strings;
  
  /* Valid register name flavors.  */
! static char **valid_flavors;
  
  /* Disassembly flavor to use. Default to "std" register names. */
! static char *disassembly_flavor;
  static int current_option;	/* Index to that option in the opcodes table. */
  
  /* This is used to keep the bfd arch_info in sync with the disassembly
--- 52,61 ----
  char **arm_register_names = arm_register_name_strings;
  
  /* Valid register name flavors.  */
! static const char **valid_flavors;
  
  /* Disassembly flavor to use. Default to "std" register names. */
! static const char *disassembly_flavor;
  static int current_option;	/* Index to that option in the opcodes table. */
  
  /* This is used to keep the bfd arch_info in sync with the disassembly
*************** _initialize_arm_tdep (void)
*** 2037,2043 ****
    struct ui_file *stb;
    long length;
    struct cmd_list_element *new_cmd;
!   const char *setname, *setdesc, **regnames;
    int numregs, i, j;
    static char *helptext;
  
--- 2037,2045 ----
    struct ui_file *stb;
    long length;
    struct cmd_list_element *new_cmd;
!   const char *setname;
!   const char *setdesc;
!   const char **regnames;
    int numregs, i, j;
    static char *helptext;
  
*************** The valid values are:\n");
*** 2059,2071 ****
    for (i = 0; i < num_flavor_options; i++)
      {
        numregs = get_arm_regnames (i, &setname, &setdesc, &regnames);
!       valid_flavors[i] = (char *) setname;
        fprintf_unfiltered (stb, "%s - %s\n", setname,
  			  setdesc);
        /* Copy the default names (if found) and synchronize disassembler. */
        if (!strcmp (setname, "std"))
  	{
!           disassembly_flavor = (char *) setname;
            current_option = i;
  	  for (j = 0; j < numregs; j++)
              arm_register_names[j] = (char *) regnames[j];
--- 2061,2073 ----
    for (i = 0; i < num_flavor_options; i++)
      {
        numregs = get_arm_regnames (i, &setname, &setdesc, &regnames);
!       valid_flavors[i] = setname;
        fprintf_unfiltered (stb, "%s - %s\n", setname,
  			  setdesc);
        /* Copy the default names (if found) and synchronize disassembler. */
        if (!strcmp (setname, "std"))
  	{
!           disassembly_flavor = setname;
            current_option = i;
  	  for (j = 0; j < numregs; j++)
              arm_register_names[j] = (char *) regnames[j];
Index: gdb/command.c
===================================================================
RCS file: /cvs/src/src/gdb/command.c,v
retrieving revision 1.12
diff -p -r1.12 command.c
*** command.c	2000/06/06 12:13:53	1.12
--- command.c	2000/06/07 06:59:25
*************** add_set_cmd (char *name,
*** 315,322 ****
  struct cmd_list_element *
  add_set_enum_cmd (char *name,
  		  enum command_class class,
! 		  char *enumlist[],
! 		  char **var,
  		  char *doc,
  		  struct cmd_list_element **list)
  {
--- 315,322 ----
  struct cmd_list_element *
  add_set_enum_cmd (char *name,
  		  enum command_class class,
! 		  const char *enumlist[],
! 		  const char **var,
  		  char *doc,
  		  struct cmd_list_element **list)
  {
*************** complete_on_cmdlist (list, text, word)
*** 1470,1486 ****
     "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
  
  char **
! complete_on_enum (enumlist, text, word)
!      char **enumlist;
!      char *text;
!      char *word;
  {
    char **matchlist;
    int sizeof_matchlist;
    int matches;
    int textlen = strlen (text);
    int i;
!   char *name;
  
    sizeof_matchlist = 10;
    matchlist = (char **) xmalloc (sizeof_matchlist * sizeof (char *));
--- 1470,1485 ----
     "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
  
  char **
! complete_on_enum (const char *enumlist[],
! 		  char *text,
! 		  char *word)
  {
    char **matchlist;
    int sizeof_matchlist;
    int matches;
    int textlen = strlen (text);
    int i;
!   const char *name;
  
    sizeof_matchlist = 10;
    matchlist = (char **) xmalloc (sizeof_matchlist * sizeof (char *));
*************** do_setshow_command (arg, from_tty, c)
*** 1667,1673 ****
  	    int i;
  	    int len;
  	    int nmatches;
! 	    char *match = NULL;
  	    char *p;
  
  	    /* if no argument was supplied, print an informative error message */
--- 1666,1672 ----
  	    int i;
  	    int len;
  	    int nmatches;
! 	    const char *match = NULL;
  	    char *p;
  
  	    /* if no argument was supplied, print an informative error message */
*************** do_setshow_command (arg, from_tty, c)
*** 1715,1721 ****
  	    if (nmatches > 1)
  	      error ("Ambiguous item \"%s\".", arg);
  
! 	    *(char **) c->var = match;
  	  }
  	  break;
  	default:
--- 1714,1720 ----
  	    if (nmatches > 1)
  	      error ("Ambiguous item \"%s\".", arg);
  
! 	    *(const char **) c->var = match;
  	  }
  	  break;
  	default:
Index: gdb/command.h
===================================================================
RCS file: /cvs/src/src/gdb/command.h,v
retrieving revision 1.9
diff -p -r1.9 command.h
*** command.h	2000/06/04 00:41:09	1.9
--- command.h	2000/06/07 06:59:27
*************** struct cmd_list_element
*** 199,205 ****
      var_types var_type;
  
      /* Pointer to NULL terminated list of enumerated values (like argv).  */
!     char **enums;
  
      /* Pointer to command strings of user-defined commands */
      struct command_line *user_commands;
--- 199,205 ----
      var_types var_type;
  
      /* Pointer to NULL terminated list of enumerated values (like argv).  */
!     const char **enums;
  
      /* Pointer to command strings of user-defined commands */
      struct command_line *user_commands;
*************** extern struct cmd_list_element *add_info
*** 274,280 ****
  
  extern char **complete_on_cmdlist (struct cmd_list_element *, char *, char *);
  
! extern char **complete_on_enum (char **enumlist, char *, char *);
  
  extern void delete_cmd (char *, struct cmd_list_element **);
  
--- 274,280 ----
  
  extern char **complete_on_cmdlist (struct cmd_list_element *, char *, char *);
  
! extern char **complete_on_enum (const char *enumlist[], char *, char *);
  
  extern void delete_cmd (char *, struct cmd_list_element **);
  
*************** extern struct cmd_list_element *add_set_
*** 294,301 ****
  
  extern struct cmd_list_element *add_set_enum_cmd (char *name,
  						  enum command_class class,
! 						  char *enumlist[],
! 						  char **var,
  						  char *doc,
  						  struct cmd_list_element **list);
  
--- 294,301 ----
  
  extern struct cmd_list_element *add_set_enum_cmd (char *name,
  						  enum command_class class,
! 						  const char *enumlist[],
! 						  const char **var,
  						  char *doc,
  						  struct cmd_list_element **list);
  
Index: gdb/i386-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/i386-tdep.c,v
retrieving revision 1.12
diff -p -r1.12 i386-tdep.c
*** i386-tdep.c	2000/06/07 04:38:02	1.12
--- i386-tdep.c	2000/06/07 06:59:30
*************** int i386_register_virtual_size[MAX_NUM_R
*** 75,89 ****
  
  /* This is the variable the is set with "set disassembly-flavor",
     and its legitimate values. */
! static char att_flavor[] = "att";
! static char intel_flavor[] = "intel";
! static char *valid_flavors[] =
  {
    att_flavor,
    intel_flavor,
    NULL
  };
! static char *disassembly_flavor = att_flavor;
  
  static void i386_print_register (char *, int, int);
  
--- 75,89 ----
  
  /* This is the variable the is set with "set disassembly-flavor",
     and its legitimate values. */
! static const char att_flavor[] = "att";
! static const char intel_flavor[] = "intel";
! static const char *valid_flavors[] =
  {
    att_flavor,
    intel_flavor,
    NULL
  };
! static const char *disassembly_flavor = att_flavor;
  
  static void i386_print_register (char *, int, int);
  
Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.14
diff -p -r1.14 infrun.c
*** infrun.c	2000/05/22 09:02:23	1.14
--- infrun.c	2000/06/07 06:59:43
*************** pending_follow;
*** 434,450 ****
     follow-fork-mode.) */
  static int follow_vfork_when_exec;
  
! static char *follow_fork_mode_kind_names[] =
  {
    /* ??rehrauer: The "both" option is broken, by what may be a 10.20
       kernel problem.  It's also not terribly useful without a GUI to
       help the user drive two debuggers.  So for now, I'm disabling the
       "both" option. */
!   /* "parent", "child", "both", "ask" */
!   "parent", "child", "ask", NULL
  };
  
! static char *follow_fork_mode_string = NULL;
  
  
  static void
--- 434,458 ----
     follow-fork-mode.) */
  static int follow_vfork_when_exec;
  
! static const char follow_fork_mode_ask[] = "ask";
! static const char follow_fork_mode_both[] = "both";
! static const char follow_fork_mode_child[] = "child";
! static const char follow_fork_mode_parent[] = "parent";
! 
! static const char *follow_fork_mode_kind_names[] =
  {
+   follow_fork_mode_ask,
    /* ??rehrauer: The "both" option is broken, by what may be a 10.20
       kernel problem.  It's also not terribly useful without a GUI to
       help the user drive two debuggers.  So for now, I'm disabling the
       "both" option. */
!   /* follow_fork_mode_both, */
!   follow_fork_mode_child,
!   follow_fork_mode_parent,
!   NULL
  };
  
! static const char *follow_fork_mode_string = follow_fork_mode_parent;
  
  
  static void
*************** follow_inferior_fork (int parent_pid, in
*** 455,477 ****
    int followed_child = 0;
  
    /* Which process did the user want us to follow? */
!   char *follow_mode =
!     savestring (follow_fork_mode_string, strlen (follow_fork_mode_string));
  
    /* Or, did the user not know, and want us to ask? */
!   if (STREQ (follow_fork_mode_string, "ask"))
      {
!       char requested_mode[100];
! 
!       free (follow_mode);
!       error ("\"ask\" mode NYI");
!       follow_mode = savestring (requested_mode, strlen (requested_mode));
      }
  
    /* If we're to be following the parent, then detach from child_pid.
       We're already following the parent, so need do nothing explicit
       for it. */
!   if (STREQ (follow_mode, "parent"))
      {
        followed_parent = 1;
  
--- 463,481 ----
    int followed_child = 0;
  
    /* Which process did the user want us to follow? */
!   const char *follow_mode = follow_fork_mode_string;
  
    /* Or, did the user not know, and want us to ask? */
!   if (follow_fork_mode_string == "ask")
      {
!       internal_error ("follow_inferior_fork: \"ask\" mode not implemented");
!       /* follow_mode = follow_fork_mode_...; */
      }
  
    /* If we're to be following the parent, then detach from child_pid.
       We're already following the parent, so need do nothing explicit
       for it. */
!   if (follow_mode == follow_fork_mode_parent)
      {
        followed_parent = 1;
  
*************** follow_inferior_fork (int parent_pid, in
*** 496,502 ****
  
    /* If we're to be following the child, then attach to it, detach
       from inferior_pid, and set inferior_pid to child_pid. */
!   else if (STREQ (follow_mode, "child"))
      {
        char child_pid_spelling[100];	/* Arbitrary length. */
  
--- 500,506 ----
  
    /* If we're to be following the child, then attach to it, detach
       from inferior_pid, and set inferior_pid to child_pid. */
!   else if (follow_mode == follow_fork_mode_child)
      {
        char child_pid_spelling[100];	/* Arbitrary length. */
  
*************** follow_inferior_fork (int parent_pid, in
*** 558,564 ****
  
    /* If we're to be following both parent and child, then fork ourselves,
       and attach the debugger clone to the child. */
!   else if (STREQ (follow_mode, "both"))
      {
        char pid_suffix[100];	/* Arbitrary length. */
  
--- 562,568 ----
  
    /* If we're to be following both parent and child, then fork ourselves,
       and attach the debugger clone to the child. */
!   else if (follow_mode == follow_fork_mode_both)
      {
        char pid_suffix[100];	/* Arbitrary length. */
  
*************** follow_inferior_fork (int parent_pid, in
*** 614,621 ****
  
    pending_follow.fork_event.saw_parent_fork = 0;
    pending_follow.fork_event.saw_child_fork = 0;
- 
-   free (follow_mode);
  }
  
  static void
--- 618,623 ----
*************** resume_cleanups (void *ignore)
*** 757,767 ****
    normal_stop ();
  }
  
! static char schedlock_off[] = "off";
! static char schedlock_on[] = "on";
! static char schedlock_step[] = "step";
! static char *scheduler_mode = schedlock_off;
! static char *scheduler_enums[] =
  {
    schedlock_off,
    schedlock_on,
--- 759,769 ----
    normal_stop ();
  }
  
! static const char schedlock_off[] = "off";
! static const char schedlock_on[] = "on";
! static const char schedlock_step[] = "step";
! static const char *scheduler_mode = schedlock_off;
! static const char *scheduler_enums[] =
  {
    schedlock_off,
    schedlock_on,
*************** discard_inferior_status (struct inferior
*** 4133,4152 ****
    free_inferior_status (inf_status);
  }
  
- static void
- set_follow_fork_mode_command (char *arg, int from_tty,
- 			      struct cmd_list_element *c)
- {
-   if (!STREQ (arg, "parent") &&
-       !STREQ (arg, "child") &&
-       !STREQ (arg, "both") &&
-       !STREQ (arg, "ask"))
-     error ("follow-fork-mode must be one of \"parent\", \"child\", \"both\" or \"ask\".");
- 
-   if (follow_fork_mode_string != NULL)
-     free (follow_fork_mode_string);
-   follow_fork_mode_string = savestring (arg, strlen (arg));
- }
  
  static void
  build_infrun (void)
--- 4135,4140 ----
*************** By default, the debugger will follow the
*** 4308,4315 ****
  			&setlist);
  /*  c->function.sfunc = ; */
    add_show_from_set (c, &showlist);
- 
-   set_follow_fork_mode_command ("parent", 0, NULL);
  
    c = add_set_enum_cmd ("scheduler-locking", class_run,
  			scheduler_enums,	/* array of string names */
--- 4296,4301 ----
Index: gdb/mips-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/mips-tdep.c,v
retrieving revision 1.11
diff -p -r1.11 mips-tdep.c
*** mips-tdep.c	2000/05/28 01:12:28	1.11
--- mips-tdep.c	2000/06/07 06:59:54
*************** struct frame_extra_info
*** 62,72 ****
     overridden dynamically.  Establish an enum/array for managing
     them. */
  
! static char size_auto[] = "auto";
! static char size_32[] = "32";
! static char size_64[] = "64";
  
! static char *size_enums[] = {
    size_auto,
    size_32,
    size_64,
--- 62,72 ----
     overridden dynamically.  Establish an enum/array for managing
     them. */
  
! static const char size_auto[] = "auto";
! static const char size_32[] = "32";
! static const char size_64[] = "64";
  
! static const char *size_enums[] = {
    size_auto,
    size_32,
    size_64,
*************** struct gdbarch_tdep
*** 143,149 ****
  #define MIPS_DEFAULT_SAVED_REGSIZE MIPS_REGSIZE
  #endif
  
! static char *mips_saved_regsize_string = size_auto;
  
  #define MIPS_SAVED_REGSIZE (mips_saved_regsize())
  
--- 143,149 ----
  #define MIPS_DEFAULT_SAVED_REGSIZE MIPS_REGSIZE
  #endif
  
! static const char *mips_saved_regsize_string = size_auto;
  
  #define MIPS_SAVED_REGSIZE (mips_saved_regsize())
  
*************** mips_saved_regsize ()
*** 191,197 ****
  
  #define MIPS_STACK_ARGSIZE (mips_stack_argsize ())
  
! static char *mips_stack_argsize_string = size_auto;
  
  static unsigned int
  mips_stack_argsize (void)
--- 191,197 ----
  
  #define MIPS_STACK_ARGSIZE (mips_stack_argsize ())
  
! static const char *mips_stack_argsize_string = size_auto;
  
  static unsigned int
  mips_stack_argsize (void)
Index: gdb/remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.12
diff -p -r1.12 remote.c
*** remote.c	2000/06/04 00:41:09	1.12
--- remote.c	2000/06/07 07:00:09
*************** enum packet_detect
*** 526,542 ****
  
  struct packet_config
    {
!     char *state;
      char *name;
      char *title;
      enum packet_detect detect;
      enum packet_support support;
    };
  
! static char packet_support_auto[] = "auto";
! static char packet_enable[] = "enable";
! static char packet_disable[] = "disable";
! static char *packet_support_enums[] =
  {
    packet_support_auto,
    packet_enable,
--- 526,542 ----
  
  struct packet_config
    {
!     const char *state;
      char *name;
      char *title;
      enum packet_detect detect;
      enum packet_support support;
    };
  
! static const char packet_support_auto[] = "auto";
! static const char packet_enable[] = "enable";
! static const char packet_disable[] = "disable";
! static const char *packet_support_enums[] =
  {
    packet_support_auto,
    packet_enable,
Index: gdb/serial.c
===================================================================
RCS file: /cvs/src/src/gdb/serial.c,v
retrieving revision 1.3
diff -p -r1.3 serial.c
*** serial.c	2000/05/16 03:03:13	1.3
--- serial.c	2000/06/07 07:00:09
*************** static struct ui_file *serial_logfp = NU
*** 50,61 ****
  
  static struct serial_ops *serial_interface_lookup (char *);
  static void serial_logchar (struct ui_file *stream, int ch_type, int ch, int timeout);
! static char logbase_hex[] = "hex";
! static char logbase_octal[] = "octal";
! static char logbase_ascii[] = "ascii";
! static char *logbase_enums[] =
  {logbase_hex, logbase_octal, logbase_ascii, NULL};
! static char *serial_logbase = logbase_ascii;
  
  
  
--- 50,61 ----
  
  static struct serial_ops *serial_interface_lookup (char *);
  static void serial_logchar (struct ui_file *stream, int ch_type, int ch, int timeout);
! static const char logbase_hex[] = "hex";
! static const char logbase_octal[] = "octal";
! static const char logbase_ascii[] = "ascii";
! static const char *logbase_enums[] =
  {logbase_hex, logbase_octal, logbase_ascii, NULL};
! static const char *serial_logbase = logbase_ascii;
  
  
  

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]