1 /* Dump-to-file commands, for GDB, the GNU debugger.
3 Copyright (C) 2002-2017 Free Software Foundation, Inc.
5 Contributed by Red Hat.
7 This file is part of GDB.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include "cli/cli-decode.h"
24 #include "cli/cli-cmds.h"
26 #include "completer.h"
29 #include "readline/readline.h"
31 #include "cli/cli-utils.h"
33 #include "filestuff.h"
34 #include "common/byte-vector.h"
36 static gdb::unique_xmalloc_ptr
<char>
37 scan_expression (const char **cmd
, const char *def
)
39 if ((*cmd
) == NULL
|| (**cmd
) == '\0')
40 return gdb::unique_xmalloc_ptr
<char> (xstrdup (def
));
46 end
= (*cmd
) + strcspn (*cmd
, " \t");
47 exp
= savestring ((*cmd
), end
- (*cmd
));
48 (*cmd
) = skip_spaces_const (end
);
49 return gdb::unique_xmalloc_ptr
<char> (exp
);
54 static gdb::unique_xmalloc_ptr
<char>
55 scan_filename (const char **cmd
, const char *defname
)
57 gdb::unique_xmalloc_ptr
<char> filename
;
60 /* FIXME: Need to get the ``/a(ppend)'' flag from somewhere. */
66 error (_("Missing filename."));
67 filename
.reset (xstrdup (defname
));
71 /* FIXME: should parse a possibly quoted string. */
74 (*cmd
) = skip_spaces_const (*cmd
);
75 end
= *cmd
+ strcspn (*cmd
, " \t");
76 filename
.reset (savestring ((*cmd
), end
- (*cmd
)));
77 (*cmd
) = skip_spaces_const (end
);
79 gdb_assert (filename
!= NULL
);
81 return gdb::unique_xmalloc_ptr
<char> (tilde_expand (filename
.get ()));
84 static gdb_bfd_ref_ptr
85 bfd_openr_or_error (const char *filename
, const char *target
)
87 gdb_bfd_ref_ptr
ibfd (gdb_bfd_openr (filename
, target
));
89 error (_("Failed to open %s: %s."), filename
,
90 bfd_errmsg (bfd_get_error ()));
92 if (!bfd_check_format (ibfd
.get (), bfd_object
))
93 error (_("'%s' is not a recognized file format."), filename
);
98 static gdb_bfd_ref_ptr
99 bfd_openw_or_error (const char *filename
, const char *target
, const char *mode
)
101 gdb_bfd_ref_ptr obfd
;
103 if (*mode
== 'w') /* Write: create new file */
105 obfd
= gdb_bfd_openw (filename
, target
);
107 error (_("Failed to open %s: %s."), filename
,
108 bfd_errmsg (bfd_get_error ()));
109 if (!bfd_set_format (obfd
.get (), bfd_object
))
110 error (_("bfd_openw_or_error: %s."), bfd_errmsg (bfd_get_error ()));
112 else if (*mode
== 'a') /* Append to existing file. */
113 { /* FIXME -- doesn't work... */
114 error (_("bfd_openw does not work with append."));
117 error (_("bfd_openw_or_error: unknown mode %s."), mode
);
122 static struct cmd_list_element
*dump_cmdlist
;
123 static struct cmd_list_element
*append_cmdlist
;
124 static struct cmd_list_element
*srec_cmdlist
;
125 static struct cmd_list_element
*ihex_cmdlist
;
126 static struct cmd_list_element
*verilog_cmdlist
;
127 static struct cmd_list_element
*tekhex_cmdlist
;
128 static struct cmd_list_element
*binary_dump_cmdlist
;
129 static struct cmd_list_element
*binary_append_cmdlist
;
132 dump_command (char *cmd
, int from_tty
)
134 printf_unfiltered (_("\"dump\" must be followed by a subcommand.\n\n"));
135 help_list (dump_cmdlist
, "dump ", all_commands
, gdb_stdout
);
139 append_command (char *cmd
, int from_tty
)
141 printf_unfiltered (_("\"append\" must be followed by a subcommand.\n\n"));
142 help_list (dump_cmdlist
, "append ", all_commands
, gdb_stdout
);
146 dump_binary_file (const char *filename
, const char *mode
,
147 const bfd_byte
*buf
, ULONGEST len
)
151 gdb_file_up file
= gdb_fopen_cloexec (filename
, mode
);
152 status
= fwrite (buf
, len
, 1, file
.get ());
154 perror_with_name (filename
);
158 dump_bfd_file (const char *filename
, const char *mode
,
159 const char *target
, CORE_ADDR vaddr
,
160 const bfd_byte
*buf
, ULONGEST len
)
164 gdb_bfd_ref_ptr
obfd (bfd_openw_or_error (filename
, target
, mode
));
165 osection
= bfd_make_section_anyway (obfd
.get (), ".newsec");
166 bfd_set_section_size (obfd
.get (), osection
, len
);
167 bfd_set_section_vma (obfd
.get (), osection
, vaddr
);
168 bfd_set_section_alignment (obfd
.get (), osection
, 0);
169 bfd_set_section_flags (obfd
.get (), osection
, (SEC_HAS_CONTENTS
172 osection
->entsize
= 0;
173 if (!bfd_set_section_contents (obfd
.get (), osection
, buf
, 0, len
))
174 warning (_("writing dump file '%s' (%s)"), filename
,
175 bfd_errmsg (bfd_get_error ()));
179 dump_memory_to_file (const char *cmd
, const char *mode
, const char *file_format
)
187 gdb::unique_xmalloc_ptr
<char> filename
= scan_filename (&cmd
, NULL
);
189 /* Find the low address. */
190 if (cmd
== NULL
|| *cmd
== '\0')
191 error (_("Missing start address."));
192 gdb::unique_xmalloc_ptr
<char> lo_exp
= scan_expression (&cmd
, NULL
);
194 /* Find the second address - rest of line. */
195 if (cmd
== NULL
|| *cmd
== '\0')
196 error (_("Missing stop address."));
199 lo
= parse_and_eval_address (lo_exp
.get ());
200 hi
= parse_and_eval_address (hi_exp
);
202 error (_("Invalid memory address range (start >= end)."));
205 /* FIXME: Should use read_memory_partial() and a magic blocking
207 gdb::byte_vector
buf (count
);
208 read_memory (lo
, buf
.data (), count
);
210 /* Have everything. Open/write the data. */
211 if (file_format
== NULL
|| strcmp (file_format
, "binary") == 0)
212 dump_binary_file (filename
.get (), mode
, buf
.data (), count
);
214 dump_bfd_file (filename
.get (), mode
, file_format
, lo
, buf
.data (), count
);
218 dump_memory_command (char *cmd
, const char *mode
)
220 dump_memory_to_file (cmd
, mode
, "binary");
224 dump_value_to_file (const char *cmd
, const char *mode
, const char *file_format
)
229 gdb::unique_xmalloc_ptr
<char> filename
= scan_filename (&cmd
, NULL
);
231 /* Find the value. */
232 if (cmd
== NULL
|| *cmd
== '\0')
233 error (_("No value to %s."), *mode
== 'a' ? "append" : "dump");
234 val
= parse_and_eval (cmd
);
236 error (_("Invalid expression."));
238 /* Have everything. Open/write the data. */
239 if (file_format
== NULL
|| strcmp (file_format
, "binary") == 0)
240 dump_binary_file (filename
.get (), mode
, value_contents (val
),
241 TYPE_LENGTH (value_type (val
)));
246 if (VALUE_LVAL (val
))
248 vaddr
= value_address (val
);
253 warning (_("value is not an lval: address assumed to be zero"));
256 dump_bfd_file (filename
.get (), mode
, file_format
, vaddr
,
257 value_contents (val
),
258 TYPE_LENGTH (value_type (val
)));
263 dump_value_command (char *cmd
, const char *mode
)
265 dump_value_to_file (cmd
, mode
, "binary");
269 dump_srec_memory (char *args
, int from_tty
)
271 dump_memory_to_file (args
, FOPEN_WB
, "srec");
275 dump_srec_value (char *args
, int from_tty
)
277 dump_value_to_file (args
, FOPEN_WB
, "srec");
281 dump_ihex_memory (char *args
, int from_tty
)
283 dump_memory_to_file (args
, FOPEN_WB
, "ihex");
287 dump_ihex_value (char *args
, int from_tty
)
289 dump_value_to_file (args
, FOPEN_WB
, "ihex");
293 dump_verilog_memory (char *args
, int from_tty
)
295 dump_memory_to_file (args
, FOPEN_WB
, "verilog");
299 dump_verilog_value (char *args
, int from_tty
)
301 dump_value_to_file (args
, FOPEN_WB
, "verilog");
305 dump_tekhex_memory (char *args
, int from_tty
)
307 dump_memory_to_file (args
, FOPEN_WB
, "tekhex");
311 dump_tekhex_value (char *args
, int from_tty
)
313 dump_value_to_file (args
, FOPEN_WB
, "tekhex");
317 dump_binary_memory (char *args
, int from_tty
)
319 dump_memory_to_file (args
, FOPEN_WB
, "binary");
323 dump_binary_value (char *args
, int from_tty
)
325 dump_value_to_file (args
, FOPEN_WB
, "binary");
329 append_binary_memory (char *args
, int from_tty
)
331 dump_memory_to_file (args
, FOPEN_AB
, "binary");
335 append_binary_value (char *args
, int from_tty
)
337 dump_value_to_file (args
, FOPEN_AB
, "binary");
342 void (*func
) (char *cmd
, const char *mode
);
347 call_dump_func (struct cmd_list_element
*c
, char *args
, int from_tty
)
349 struct dump_context
*d
= (struct dump_context
*) get_cmd_context (c
);
351 d
->func (args
, d
->mode
);
355 add_dump_command (const char *name
,
356 void (*func
) (char *args
, const char *mode
),
360 struct cmd_list_element
*c
;
361 struct dump_context
*d
;
363 c
= add_cmd (name
, all_commands
, NULL
, descr
, &dump_cmdlist
);
364 c
->completer
= filename_completer
;
365 d
= XNEW (struct dump_context
);
368 set_cmd_context (c
, d
);
369 c
->func
= call_dump_func
;
371 c
= add_cmd (name
, all_commands
, NULL
, descr
, &append_cmdlist
);
372 c
->completer
= filename_completer
;
373 d
= XNEW (struct dump_context
);
376 set_cmd_context (c
, d
);
377 c
->func
= call_dump_func
;
379 /* Replace "Dump " at start of docstring with "Append " (borrowed
380 from [deleted] deprecated_add_show_from_set). */
381 if ( c
->doc
[0] == 'W'
387 c
->doc
= concat ("Append ", c
->doc
+ 6, (char *)NULL
);
390 /* Opaque data for restore_section_callback. */
391 struct callback_data
{
392 CORE_ADDR load_offset
;
393 CORE_ADDR load_start
;
397 /* Function: restore_section_callback.
399 Callback function for bfd_map_over_sections.
400 Selectively loads the sections into memory. */
403 restore_section_callback (bfd
*ibfd
, asection
*isec
, void *args
)
405 struct callback_data
*data
= (struct callback_data
*) args
;
406 bfd_vma sec_start
= bfd_section_vma (ibfd
, isec
);
407 bfd_size_type size
= bfd_section_size (ibfd
, isec
);
408 bfd_vma sec_end
= sec_start
+ size
;
409 bfd_size_type sec_offset
= 0;
410 bfd_size_type sec_load_count
= size
;
413 /* Ignore non-loadable sections, eg. from elf files. */
414 if (!(bfd_get_section_flags (ibfd
, isec
) & SEC_LOAD
))
417 /* Does the section overlap with the desired restore range? */
418 if (sec_end
<= data
->load_start
419 || (data
->load_end
> 0 && sec_start
>= data
->load_end
))
421 /* No, no useable data in this section. */
422 printf_filtered (_("skipping section %s...\n"),
423 bfd_section_name (ibfd
, isec
));
427 /* Compare section address range with user-requested
428 address range (if any). Compute where the actual
429 transfer should start and end. */
430 if (sec_start
< data
->load_start
)
431 sec_offset
= data
->load_start
- sec_start
;
432 /* Size of a partial transfer. */
433 sec_load_count
-= sec_offset
;
434 if (data
->load_end
> 0 && sec_end
> data
->load_end
)
435 sec_load_count
-= sec_end
- data
->load_end
;
438 gdb::byte_vector
buf (size
);
439 if (!bfd_get_section_contents (ibfd
, isec
, buf
.data (), 0, size
))
440 error (_("Failed to read bfd file %s: '%s'."), bfd_get_filename (ibfd
),
441 bfd_errmsg (bfd_get_error ()));
443 printf_filtered ("Restoring section %s (0x%lx to 0x%lx)",
444 bfd_section_name (ibfd
, isec
),
445 (unsigned long) sec_start
,
446 (unsigned long) sec_end
);
448 if (data
->load_offset
!= 0 || data
->load_start
!= 0 || data
->load_end
!= 0)
449 printf_filtered (" into memory (%s to %s)\n",
450 paddress (target_gdbarch (),
451 (unsigned long) sec_start
452 + sec_offset
+ data
->load_offset
),
453 paddress (target_gdbarch (),
454 (unsigned long) sec_start
+ sec_offset
455 + data
->load_offset
+ sec_load_count
));
457 puts_filtered ("\n");
459 /* Write the data. */
460 ret
= target_write_memory (sec_start
+ sec_offset
+ data
->load_offset
,
461 &buf
[sec_offset
], sec_load_count
);
463 warning (_("restore: memory write failed (%s)."), safe_strerror (ret
));
467 restore_binary_file (const char *filename
, struct callback_data
*data
)
469 gdb_file_up file
= gdb_fopen_cloexec (filename
, FOPEN_RB
);
472 /* Get the file size for reading. */
473 if (fseek (file
.get (), 0, SEEK_END
) == 0)
475 len
= ftell (file
.get ());
477 perror_with_name (filename
);
480 perror_with_name (filename
);
482 if (len
<= data
->load_start
)
483 error (_("Start address is greater than length of binary file %s."),
486 /* Chop off "len" if it exceeds the requested load_end addr. */
487 if (data
->load_end
!= 0 && data
->load_end
< len
)
488 len
= data
->load_end
;
489 /* Chop off "len" if the requested load_start addr skips some bytes. */
490 if (data
->load_start
> 0)
491 len
-= data
->load_start
;
494 ("Restoring binary file %s into memory (0x%lx to 0x%lx)\n",
496 (unsigned long) (data
->load_start
+ data
->load_offset
),
497 (unsigned long) (data
->load_start
+ data
->load_offset
+ len
));
499 /* Now set the file pos to the requested load start pos. */
500 if (fseek (file
.get (), data
->load_start
, SEEK_SET
) != 0)
501 perror_with_name (filename
);
503 /* Now allocate a buffer and read the file contents. */
504 gdb::byte_vector
buf (len
);
505 if (fread (buf
.data (), 1, len
, file
.get ()) != len
)
506 perror_with_name (filename
);
508 /* Now write the buffer into target memory. */
509 len
= target_write_memory (data
->load_start
+ data
->load_offset
,
512 warning (_("restore: memory write failed (%s)."), safe_strerror (len
));
516 restore_command (char *args_in
, int from_tty
)
518 struct callback_data data
;
521 const char *args
= args_in
;
523 if (!target_has_execution
)
526 data
.load_offset
= 0;
530 /* Parse the input arguments. First is filename (required). */
531 gdb::unique_xmalloc_ptr
<char> filename
= scan_filename (&args
, NULL
);
532 if (args
!= NULL
&& *args
!= '\0')
534 static const char binary_string
[] = "binary";
536 /* Look for optional "binary" flag. */
537 if (startswith (args
, binary_string
))
540 args
+= strlen (binary_string
);
541 args
= skip_spaces_const (args
);
543 /* Parse offset (optional). */
544 if (args
!= NULL
&& *args
!= '\0')
545 data
.load_offset
= binary_flag
?
546 parse_and_eval_address (scan_expression (&args
, NULL
).get ()) :
547 parse_and_eval_long (scan_expression (&args
, NULL
).get ());
548 if (args
!= NULL
&& *args
!= '\0')
550 /* Parse start address (optional). */
552 parse_and_eval_long (scan_expression (&args
, NULL
).get ());
553 if (args
!= NULL
&& *args
!= '\0')
555 /* Parse end address (optional). */
556 data
.load_end
= parse_and_eval_long (args
);
557 if (data
.load_end
<= data
.load_start
)
558 error (_("Start must be less than end."));
564 printf_filtered ("Restore file %s offset 0x%lx start 0x%lx end 0x%lx\n",
565 filename
.get (), (unsigned long) data
.load_offset
,
566 (unsigned long) data
.load_start
,
567 (unsigned long) data
.load_end
);
571 restore_binary_file (filename
.get (), &data
);
575 /* Open the file for loading. */
576 gdb_bfd_ref_ptr
ibfd (bfd_openr_or_error (filename
.get (), NULL
));
578 /* Process the sections. */
579 bfd_map_over_sections (ibfd
.get (), restore_section_callback
, &data
);
584 srec_dump_command (char *cmd
, int from_tty
)
586 printf_unfiltered (_("\"dump srec\" must be followed by a subcommand.\n"));
587 help_list (srec_cmdlist
, "dump srec ", all_commands
, gdb_stdout
);
591 ihex_dump_command (char *cmd
, int from_tty
)
593 printf_unfiltered (_("\"dump ihex\" must be followed by a subcommand.\n"));
594 help_list (ihex_cmdlist
, "dump ihex ", all_commands
, gdb_stdout
);
598 verilog_dump_command (char *cmd
, int from_tty
)
600 printf_unfiltered (_("\"dump verilog\" must be followed by a subcommand.\n"));
601 help_list (verilog_cmdlist
, "dump verilog ", all_commands
, gdb_stdout
);
605 tekhex_dump_command (char *cmd
, int from_tty
)
607 printf_unfiltered (_("\"dump tekhex\" must be followed by a subcommand.\n"));
608 help_list (tekhex_cmdlist
, "dump tekhex ", all_commands
, gdb_stdout
);
612 binary_dump_command (char *cmd
, int from_tty
)
614 printf_unfiltered (_("\"dump binary\" must be followed by a subcommand.\n"));
615 help_list (binary_dump_cmdlist
, "dump binary ", all_commands
, gdb_stdout
);
619 binary_append_command (char *cmd
, int from_tty
)
621 printf_unfiltered (_("\"append binary\" must be followed by a subcommand.\n"));
622 help_list (binary_append_cmdlist
, "append binary ", all_commands
,
626 extern initialize_file_ftype _initialize_cli_dump
; /* -Wmissing-prototypes */
629 _initialize_cli_dump (void)
631 struct cmd_list_element
*c
;
633 add_prefix_cmd ("dump", class_vars
, dump_command
,
634 _("Dump target code/data to a local file."),
635 &dump_cmdlist
, "dump ",
638 add_prefix_cmd ("append", class_vars
, append_command
,
639 _("Append target code/data to a local file."),
640 &append_cmdlist
, "append ",
644 add_dump_command ("memory", dump_memory_command
, "\
645 Write contents of memory to a raw binary file.\n\
646 Arguments are FILE START STOP. Writes the contents of memory within the\n\
647 range [START .. STOP) to the specified FILE in raw target ordered bytes.");
649 add_dump_command ("value", dump_value_command
, "\
650 Write the value of an expression to a raw binary file.\n\
651 Arguments are FILE EXPRESSION. Writes the value of EXPRESSION to\n\
652 the specified FILE in raw target ordered bytes.");
654 add_prefix_cmd ("srec", all_commands
, srec_dump_command
,
655 _("Write target code/data to an srec file."),
656 &srec_cmdlist
, "dump srec ",
660 add_prefix_cmd ("ihex", all_commands
, ihex_dump_command
,
661 _("Write target code/data to an intel hex file."),
662 &ihex_cmdlist
, "dump ihex ",
666 add_prefix_cmd ("verilog", all_commands
, verilog_dump_command
,
667 _("Write target code/data to a verilog hex file."),
668 &verilog_cmdlist
, "dump verilog ",
672 add_prefix_cmd ("tekhex", all_commands
, tekhex_dump_command
,
673 _("Write target code/data to a tekhex file."),
674 &tekhex_cmdlist
, "dump tekhex ",
678 add_prefix_cmd ("binary", all_commands
, binary_dump_command
,
679 _("Write target code/data to a raw binary file."),
680 &binary_dump_cmdlist
, "dump binary ",
684 add_prefix_cmd ("binary", all_commands
, binary_append_command
,
685 _("Append target code/data to a raw binary file."),
686 &binary_append_cmdlist
, "append binary ",
690 add_cmd ("memory", all_commands
, dump_srec_memory
, _("\
691 Write contents of memory to an srec file.\n\
692 Arguments are FILE START STOP. Writes the contents of memory\n\
693 within the range [START .. STOP) to the specified FILE in srec format."),
696 add_cmd ("value", all_commands
, dump_srec_value
, _("\
697 Write the value of an expression to an srec file.\n\
698 Arguments are FILE EXPRESSION. Writes the value of EXPRESSION\n\
699 to the specified FILE in srec format."),
702 add_cmd ("memory", all_commands
, dump_ihex_memory
, _("\
703 Write contents of memory to an ihex file.\n\
704 Arguments are FILE START STOP. Writes the contents of memory within\n\
705 the range [START .. STOP) to the specified FILE in intel hex format."),
708 add_cmd ("value", all_commands
, dump_ihex_value
, _("\
709 Write the value of an expression to an ihex file.\n\
710 Arguments are FILE EXPRESSION. Writes the value of EXPRESSION\n\
711 to the specified FILE in intel hex format."),
714 add_cmd ("memory", all_commands
, dump_verilog_memory
, _("\
715 Write contents of memory to a verilog hex file.\n\
716 Arguments are FILE START STOP. Writes the contents of memory within\n\
717 the range [START .. STOP) to the specified FILE in verilog hex format."),
720 add_cmd ("value", all_commands
, dump_verilog_value
, _("\
721 Write the value of an expression to a verilog hex file.\n\
722 Arguments are FILE EXPRESSION. Writes the value of EXPRESSION\n\
723 to the specified FILE in verilog hex format."),
726 add_cmd ("memory", all_commands
, dump_tekhex_memory
, _("\
727 Write contents of memory to a tekhex file.\n\
728 Arguments are FILE START STOP. Writes the contents of memory\n\
729 within the range [START .. STOP) to the specified FILE in tekhex format."),
732 add_cmd ("value", all_commands
, dump_tekhex_value
, _("\
733 Write the value of an expression to a tekhex file.\n\
734 Arguments are FILE EXPRESSION. Writes the value of EXPRESSION\n\
735 to the specified FILE in tekhex format."),
738 add_cmd ("memory", all_commands
, dump_binary_memory
, _("\
739 Write contents of memory to a raw binary file.\n\
740 Arguments are FILE START STOP. Writes the contents of memory\n\
741 within the range [START .. STOP) to the specified FILE in binary format."),
742 &binary_dump_cmdlist
);
744 add_cmd ("value", all_commands
, dump_binary_value
, _("\
745 Write the value of an expression to a raw binary file.\n\
746 Arguments are FILE EXPRESSION. Writes the value of EXPRESSION\n\
747 to the specified FILE in raw target ordered bytes."),
748 &binary_dump_cmdlist
);
750 add_cmd ("memory", all_commands
, append_binary_memory
, _("\
751 Append contents of memory to a raw binary file.\n\
752 Arguments are FILE START STOP. Writes the contents of memory within the\n\
753 range [START .. STOP) to the specified FILE in raw target ordered bytes."),
754 &binary_append_cmdlist
);
756 add_cmd ("value", all_commands
, append_binary_value
, _("\
757 Append the value of an expression to a raw binary file.\n\
758 Arguments are FILE EXPRESSION. Writes the value of EXPRESSION\n\
759 to the specified FILE in raw target ordered bytes."),
760 &binary_append_cmdlist
);
762 c
= add_com ("restore", class_vars
, restore_command
, _("\
763 Restore the contents of FILE to target memory.\n\
764 Arguments are FILE OFFSET START END where all except FILE are optional.\n\
765 OFFSET will be added to the base address of the file (default zero).\n\
766 If START and END are given, only the file contents within that range\n\
767 (file relative) will be restored to target memory."));
768 c
->completer
= filename_completer
;
769 /* FIXME: completers for other commands. */