How can I make GDB print a string literally (without escaping)? - gdb

I have a big, long string that I want to capture to a file. I can use logging to get most of the way there:
set logging on
set logging file gdb.log
…but if I use p or x/s to print the string, quotes and junk are all escaped. How can I get the string as-is?

For a really large string you can also use:
(gdb) set variable $s = MY_STRING
(gdb) dump binary memory FILE $s $s + (size_t)strlen($s)
which can be easily adapted to handle buffers with null bytes. Also the content of FILE would never contain anything other than the string.

Ah, I totally forgot about printf:
printf "%s\n", some_string

Related

Why isn't this regex executing?

I'm attempting to convert my personal wiki from Foswiki to Markdown files and then to a JAMstack deployment. Foswiki uses flat files and stores metadata in the following format:
%META:TOPICINFO{author="TeotiNathaniel" comment="reprev" date="1571215308" format="1.1" reprev="13" version="14"}%
I want to use a git repo for versioning and will worry about linking that to article metatada later. At this point I simply want to convert these blocks to something that looks like this:
---
author: Teoti Nathaniel
revdate: 1539108277
---
After a bit of tweaking I have constructed the following regex:
author\=\['"\]\(\\w\+\)\['"\]\(\?\:\.\*\)date\=\['"\]\(\\w\+\)\['"\]
According to regex101 this works and my two capture groups contain the desired results. Attempting to actually run it:
perl -0777 -pe 's/author\=\['"\]\(\\w\+\)\['"\]\(\?\:\.\*\)date\=\['"\]\(\\w\+\)\['"\]/author: $1\nrevdate: $2/gms' somefile.txt
gets me only this:
>
My previous attempt (which breaks if the details aren't in a specific order) looked like this and executed correctly:
perl -0777 -pe 's/%META:TOPICINFO\{author="(.*)"\ date="(.*)"\ format="(.*)"\ (.*)\}\%/author:$1 \nrevdate:$2/gms' somefile.txt
I think that this is an escape character problem but can't figure it out. I even went and found this tool to make sure that they are correct.
Brute-forcing my way to understanding here is feeling both inefficient and frustrating, so I'm asking the community for help.
The first major problem is that you're trying to use a single quote (') in the program, when the program is being passed to the shell in single quotes.
Escape any instance of ' in the program by using '\''. You could also use \x27 if the quote happens to be a single double-quoted string literal or regex literal (as is the case of every instance in your program).
perl -0777pe's/author=['\''"].../.../gs'
perl -0777pe's/author=[\x27"].../.../gs'
I would try to break it down into a clean data structure then process it. By seperating the data processing to printing, you can modifiy to add extra data later. It also makes it far more readable. Please see the example below
#!/usr/bin/env perl
use strict;
use warnings;
## yaml to print the data, not required for operation
use YAML::XS qw(Dump);
my $yaml;
my #lines = '%META:TOPICINFO{author="TeotiNathaniel" comment="reprev" date="1571215308" format="1.1" reprev="13" version="14"}%';
for my $str (#lines )
{
### split line into component parts
my ( $type , $subject , $data ) = $str =~ /\%(.*?):(.*?)\{(.*)\}\%/;
## break data in {} into a hash
my %info = map( split(/=/), split(/\s+/, $data) );
## strip quotes if any exist
s/^"(.*)"$/$1/ for values %info;
#add to data structure
$yaml->{$type}{$subject} = \%info;
}
## yaml to print the data, not required for operation
print Dump($yaml);
## loop data and print
for my $t (keys %{ $yaml } ) {
for my $s (keys %{ $yaml->{$t} } ) {
print "-----------\n";
print "author: ".$yaml->{$t}{$s}{"author"}."\n";
print "date: ".$yaml->{$t}{$s}{"date"}."\n";
}
}
Ok, I kept fooling around with it by reducing the execution to a single term and expanding. I soon got to here:
$ perl -0777 -pe 's/author=['\"]\(\\w\+\)['"](?:.*)date=\['\"\]\(\\w\+\)\['\"\]/author\: \$1\\nrevdate\: \$2/gms' somefile.txt
Unmatched [ in regex; marked by <-- HERE in m/author=["](\w+)["](?:.*)date=\["](\w+)[ <-- HERE \"\]/ at -e line 1.
This eventually got me to here:
perl -0777 -pe 's/author=['\"]\(\\w\+\)['"](?:.*)date=['\"]\(\\w\+\)['\"]/\nauthor\ $1\nrevdate\:$2\n/gms' somefile.txt
Which produces a messy output but works. (Note: Output is proof-of-concept and this can now be used within a Python script to programattically generate Markdown metadata.
Thanks for being my rubber duckie, StackOverflow. Hopefully this is useful to someone, somewhere, somewhen.

gdb user-defined functions: how to pass complex argument?

I want to examine several QString variables,
so I found macros for this in Internet:
define printqstring
printf "(QString)0x%x (length=%i): \"",&$arg0,$arg0.d->size
set $i=0
while $i < ($arg0).d->size
set $c=$arg0.d->data[$i++]
if $c < 32 || $c > 127
printf "\\u0x%04x", $c
else
printf "%c", (char)$c
end
end
printf "\"\n"
end
But when I try to use it, I got such error:
(gdb) printqstring ((MyWidget *)0xd98cb0)->caption_
A syntax error in expression, near `,((MyWidget.d->size'.
if I try to use commands from macros by hand, they work fine:
(gdb) printf "(QString)0x%x (length=%i): \"",&((MyWidget *)0xd98cb0)->caption_,((MyWidget *)0xd98cb0)->caption_.d->size
(QString)0xd98ccc (length=3)
So how can I pass such complex argument to gdb macros?
Unfortunately gdb always divides input to a user-defined function at any space character, even if that character is inside parentheses or something like that.
So you can just make sure you don't use any spaces in the argument you want to pass:
(gdb) printqstring ((MyWidget*)0xd98cb0)->caption_
^~~ removed space
I don't know of any good way to make this more convenient and allow spaces.

bash substitutions don't appear to work when matching newline characters

I have an executable file called test.script containing this simple bash script:
#!/bin/bash
temp=${1//$'\n'/}
output=${temp//$'\r'/}
printf "$output" > output.txt
When I run
sudo ./test.script "^\r\r\n\n\r\n\r\n\r\n\r\n\n\r\n\rHello World\n\n\r\r\r\n\n\r\r\r$"
in the same directory as test.script, I expect to end up with an output.txt looking like this:
^Hello World$
but when I take a look I instead see this:
^
Hello World
$
Clearly I have a misunderstanding about regex in bash.
Please explain to me what I am missing, then show me how to write the bash so that all newline characters are removed from the string before said string is written to a file. Thanks in advance.
You can "fix" your script like this (although I must say this isn't typical usage of printf):
#!/bin/bash
temp=${1//'\n'/}
output=${temp//'\r'/}
printf "$output"
The argument to your script $1 doesn't contain real newlines or carriage returns, which is what $'\n' and $'\r' are for. Instead, it looks like you just want to remove the literal strings '\n' and '\r'.
To elaborate on my point about printf, normally two (or more) arguments are passed: the format specifier and the variables that are to be inserted. For example, to print a single string you would use something like printf '%s' "$output". In your script, the variable $output is being treated as the format specifer; you're relying on printf to expand your \n and \r into newlines and carriage returns.
You're not actually using regular expressions here by the way; the syntax ${var//match/replace} is a substring replacement, where // means that all occurrences of the substring match in $var are replaced. As you haven't specified anything to replace the substring with, the substring is replaced with nothing (i.e. removed).

Regexp doesn't work for specific special characters in Perl

I can't get rid of the special character ¤ and ❤ in a string:
$word = 'cɞi¤r$c❤u¨s';
$word =~ s/[^a-zöäåA-ZÖÄÅ]//g;
printf "$word\n";
On the second line I try to remove any non alphabetic characters from the string $word. I would expect to get the word circus printed out but instead I get:
ci�rc�us
The öäå and ÖÄÅ in the expression are just normal characters in the Swedish alphabet that I need included.
If the characters are in your source code, be sure to use utf8. If they are being read from a file, binmode $FILEHANDLE, ':utf8'.
Be sure to read perldoc perlunicode.
Short answer: add use utf8; to make sure your literal string in the source code are interepreted as utf8, that includes the content of the test string, and the content of the regexp.
Long answer:
#!/usr/bin/env perl
use warnings;
use Encode;
my $word = 'cɞi¤r$c❤u¨s';
foreach my $char (split //, $word) {
print ord($char) . Encode::encode_utf8(":$char ");
}
my $allowed_chars = 'a-zöäåA-ZÖÄÅ';
print "\n";
foreach my $char (split //, $allowed_chars) {
print ord($char) . Encode::encode_utf8(":$char ");
}
print "\n";
$word =~ s/[^$allowed_chars]//g;
printf Encode::encode_utf8("$word\n");
Executing it without utf8:
$ perl utf8_regexp.pl
99:c 201:É 158: 105:i 194:Â 164:¤ 114:r 36:$ 99:c 226:â 157: 164:¤ 117:u 194:Â 168:¨ 115:s
97:a 45:- 122:z 195:Ã 182:¶ 195:Ã 164:¤ 195:Ã 165:¥ 65:A 45:- 90:Z 195:Ã 150: 195:Ã 132: 195:Ã 133:
ci¤rc¤us
Executing it with utf8:
$ perl -Mutf8 utf8_regexp.pl
99:c 606:ɞ 105:i 164:¤ 114:r 36:$ 99:c 10084:❤ 117:u 168:¨ 115:s
97:a 45:- 122:z 246:ö 228:ä 229:å 65:A 45:- 90:Z 214:Ö 196:Ä 197:Å
circus
Explanation:
The non-ascii characters that you are typing into your source code are represented by one than more byte. Since your input is utf8 encoded. In a pure ascii or latin-1 terminal the characters would've been one byte.
When not using utf8 module, perl thinks that each and every byte you are inputting is a separate character, like you can see when doing the splitting and printing each and every individual character. When using the utf8 module, it treats the combination of several bytes as one character correctly according to the rules of utf8 encoding.
As you can see by coinscidence, some of the bytes that the swedish characters are made up of match with some of the bytes that some of the characters in your test string are made up of, and they are kept. Namely: the ö which in utf8 consists of 195:Ã 164:¤ - The 164 ends up as one of the characters you allow and it passes thru.
The solution is to tell perl that your strings are supposed to be considered as utf-8.
The encode_utf8 calls are in place to avoid warnings about wide characters being printed to the terminal. As always you need to decode input, and encode output according to the character encoding that input or output is supposed to handle/operate in.
Hope this made it clearer.
As pointed out by choroba, adding this in the beginning of the perl script solves it:
use utf8;
binmode(STDOUT, ":utf8");
where use utf8 lets you use the special characters correctly in the regular expression and binmode(STDOUT, ":utf8") lets you output the special characters correctly on the shell.

Perl, regex, extract data from a line

Im trying to extract part of a line with perl
use strict;
use warnings;
# Set path for my.txt and extract datadir
my #myfile = "C:\backups\MySQL\my.txt";
my #datadir = "";
open READMYFILE, #myfile or die "Error, my.txt not found.\n";
while (<READMYFILE>) {
# Read file and extract DataDir path
if (/C:\backups/gi) {
push #datadir, $_;
}
}
# ensure the path was found
print #datadir . " \n";
Basically at first im trying to set the location of the my.txt file. Next im trying to read it and pull part of the line with regex. The error Im getting is:
Unrecognized escape \m passed through
at 1130.pl line 17.
I took a look at How can I grab multiple lines after a matching line in Perl? to get an idea of how to read a file and match a line within it, however im not 100% sure I'm doing this right or in the best way. I also seem to produce the error:
Error, my.txt not found.
But the file does exist in the folder C:\backups\MySQL\
When Perl sees the string "C:\backups\MySQL\my.txt" it tries to parse any escape sequences, such as \n. But when it sees \m in \my.txt, it's an unrecognized escape sequence, hence the error.
One way to fix this is to properly escape your backslashes: "C:\\backups\\MySQL\\my.txt". Another way to fix this is to use single quotes instead of double quotes: 'C:\backups\MySQL\my.txt'. Yet another way is to use the q() construct: q(C:\backups\MySQL\my.txt).
Since there are several problems I'll put comments on the changes I've made in the code below.
use strict;
use warnings;
# For pretty dumping of arrays and what not.
use Data::Dumper;
# Use single quotes so you don't have to worry about escaping '\'s.
# Use a scalar ($) instead of an array(#) for storing the string.
my $myfile = 'C:\backups\MySQL\my.txt';
# No need to initialize the array.
my #datadir;
# I believe using a scalar is preferred for file handles.
# $! will contain the error if we couldn't open the file.
open(my $readmyfile, $myfile) or die "error opening: $!";
while (<$readmyfile>) {
# You must escape '\'s by doubling them.
# If you are just testing to see if the line contains 'c:\backups' you do not
# need /g for the regex. /g is for repeating matches
if (/C:\\backups/i) {
push(#datadir, $_);
}
}
# Data::Dumper would be better for dumping the array for debugging.
# Dumper wants a reference to the array.
print Dumper(\#datadir);
Update:
If you're referring to the output from Data::Dumper, it's just there for a pretty representation of the array. If you need a specifically formatted output you'll have to code it. A start would be:
print "$_\n" for (#datadir);
Use forward slashes instead of backslahes
Shouldn't you be using $myfile instead of #myfile? The latter gives you an array, and since you're referencing it in scalar context, it's getting dereferenced (so it's actually trying to open a "file" called something like ARRAY(0xdeadbeef) instead of the actual filename).
The file is not being found because you are passing an array to open when it's expecting a scalar, so I'd guess that the array is being evaluated in a scalar context instead of as a list so you're actually telling perl to try opening the file named '1' instead of your 'my.txt' file.
Try something like this instead:
my $a = 'filename';
open FH, $a or die "Error, could not open $a: $!";
...
As other people have said, part of the issue is using " " rather than ' ' type of quoting.
I try always to use ' ' unless I know I need to include an escape or interpolate a variable.
Here are a number of pitfalls
use 5.10.0 ;
use warnings ;
say "file is c:\mydir" ;
say "please pay $100 ";
say "on VMS the system directory is sys$system" ;
say "see you #5 ";
With double quotes
Unrecognized escape \m passed through at (eval 1) line 2.
Possible unintended interpolation of #5 in string at (eval 1) line 5.
file is c:mydir
Use of uninitialized value $100 in concatenation (.) or string at (eval 1) line 3.
please pay
Use of uninitialized value $system in concatenation (.) or string at (eval 1) line 4.
on VMS the system directory is sys
see you
With single quotes
file is c:\mydir
please pay $100
on VMS the system directory is sys$system
see you #5