Programming in C#, Java, and god knows what not

Fixing Git Author Name and Email

After moving Mercurial repository to Git you might want to update user names and emails.

The first step would be to see the all the names:

git log --format='%an <%ae>' | git log --format='%cn <%ce>' | sort | uniq

With this information in hand, we can adjust names with filter-branch:

git filter-branch --force --commit-filter '
    OLD_NAME="^^unknown^^"
    NEW_NAME="^^My name^^"
    NEW_EMAIL="^^myemail@example.com^^"
    if [ "$GIT_AUTHOR_NAME" = "$OLD_NAME" ]; then
        GIT_AUTHOR_NAME="$NEW_NAME"
        GIT_AUTHOR_EMAIL="$NEW_EMAIL"
    fi
    if [ "$GIT_COMMITTER_NAME" = "$OLD_NAME" ]; then
        GIT_COMMITTER_NAME="$NEW_NAME"
        GIT_COMMITTER_EMAIL="$NEW_EMAIL"
    fi
    git commit-tree "$@";
' --tag-name-filter cat -- --branches --tags --all

git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin

Using TLS 1.3 from .NET 4.0 Application

Due to ubiquitous support for .NET 4.0 on all Windows platforms (including Windows 10) out-of-box, I still keep most of my freeware apps on it. Yes, I lose some new fancy features but not dealing with .NET Framework download makes it really convenient. But there is one thing that proved to be a problem - TLS 1.3.

When I changed my web server to use only TLS 1.2 and above, built-in upgrade suddenly stopped working. Digging a bit showed reason was that .NET 4.0 supported only TLS 1.0 by default and my web server where upgrades were located required TLS 1.2 at the minimum.

For the latest .NET versions, updating to a higher TLS is easy enough:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13
                                     | SecurityProtocolType.Tls12
                                     | SecurityProtocolType.Tls11
                                     | SecurityProtocolType.Tls;

But Tls11, Tls12, and Tls13 enums are not part of .NET 4.0. However, because .NET is so closely integrated with Windows, sometime it’s worth asking it directly - by specifying enum’s numerical value:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)12288
                                     | (SecurityProtocolType)3072
                                     | (SecurityProtocolType)768
                                     | SecurityProtocolType.Tls;

If you run this code before making the first HTTP request, suddenly you are not limited to the SSL and the ancient TLS anymore.

As this code still requires a bit of error checking, I finally ended up with the function below:

try { //try TLS 1.3
    ServicePointManager.SecurityProtocol = (SecurityProtocolType)12288
                                         | (SecurityProtocolType)3072
                                         | (SecurityProtocolType)768
                                         | SecurityProtocolType.Tls;
} catch (NotSupportedException) {
    try { //try TLS 1.2
        ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072
                                             | (SecurityProtocolType)768
                                             | SecurityProtocolType.Tls;
    } catch (NotSupportedException) {
        try { //try TLS 1.1
            ServicePointManager.SecurityProtocol = (SecurityProtocolType)768
                                                 | SecurityProtocolType.Tls;
        } catch (NotSupportedException) { //TLS 1.0
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
        }
    }
}

This code ensures the highest TLS is supported even from the poor old .NET 4.0.

Unique Machine ID

Every once in a while I need to uniquely identify a machine. I don’t case about its name or properties. I just want something unique to key off and ideally I want its format to be the same on both Windows and Linux.

On Linux you can use content of /etc/machine-id. On pretty much all distributions I’ve tried this file will be generated on install and will contain UUID. For Windows, one must look in Registry. Under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid Windows will keep their unique identifier also in UUID format (albeit with dashes this time).


PS: Note these values can be manually changed by user so you cannot really depend on them for security purposes (e.g. generating licenses). But for machine identification they’ll do.

Adding Space Before Equals in Git Prompt

I like using Git-Prompt to decorate my bash prompt. It’s nice, simple, and it would be perfect if it didn’t put equals next to branch name. Not sure what I’m talking about?

If you have modified files, Git-Prompt will make sure to let you know by writing master %= in prompt line. If you want to quickly copy branch name, you simply double click it and only master is selected. However, when there are no modifications to Git tree, output will be master=. Notice the lack of space. When you double click on this, both branch name and equals sign get copied. What I want is to have a space no matter whether branch is modified or not.

Cause for this issue can be found in the following line

local gitstring="$c$b^^${f:+$z$f}^^$r$p"

This makes it for that extra flags get space. If there are no flags, there is no space. Solution is simple - just add space always thus changing line like this:

local gitstring="$c$b$z$f$r$p"

Or, if you like to do it programmatically:

sed -i 's/local gitstring=.*/local gitstring=$c$b$z$f$r$p/' ~/.git-prompt.sh

PS: If you are wondering, this is how my prompt setup looks:

LC_COLLATE=C

if [ -f ~/.git-prompt.sh ]; then
    GIT_PS1_SHOWDIRTYSTATE=true
    GIT_PS1_SHOWSTASHSTATE=true
    GIT_PS1_SHOWUNTRACKEDFILES=true
    GIT_PS1_SHOWUPSTREAM="auto"
    GIT_PS1_STATESEPARATOR=" "
    GIT_PS1_DESCRIBE_STYLE="default"
    GIT_PS1_SHOWCOLORHINTS=true
    GIT_PS1_HIDE_IF_PWD_IGNORED=
    . ~/.git-prompt.sh
fi

function ps1_timer_start {
    PS1_TIMER=${PS1_TIMER:-$SECONDS}
}

function ps1_timer_stop {
    PS1_TIMER_VALUE=$(($SECONDS-$PS1_TIMER))
    if [[ $PS1_TIMER_VALUE -eq 0 ]]; then
        PS1_TIMER_VALUE=""
    elif [[ $PS1_TIMER_VALUE -lt 60 ]]; then
        PS1_TIMER_VALUE=" ${PS1_TIMER_VALUE}s"
    else
        PS1_TIMER_VALUE=" $((PS1_TIMER_VALUE / 60))m$((PS1_TIMER_VALUE % 60))s"
    fi
  unset PS1_TIMER
}

trap 'ps1_timer_start' DEBUG
PROMPT_COMMAND=ps1_timer_stop

PS1='\[\e[36m\]\n\u@\h\[\e[0m\] \w\[\e[34m\]$PS1_TIMER_VALUE\[\e[37m\] \[\e[36m\]\[\e[7m\]`__git_ps1 " %s "`\[\e[0m\]\n\[\e[36m\]\\$\[\e[0m\] '

Bimil failing with FontFamilyNotFound

While Bimil is primarily Windows application, I use it regularly on Linux. However, when I tried running it on freshly installed Linux Mint 19.3, I was greeted with a quick crash.

Running it from console did shine a bit more light onto the situation as the following line was quite noticeable: [ERROR] FATAL UNHANDLED EXCEPTION: System.ArgumentException: The requested FontFamily could not be found [GDI+ status: FontFamilyNotFound].

As mono is Windows Forms application written in C#, running it on Linux requires a few packages extra. Most of them are actually taken care of with the installation of mono-complete. However, in Linux mint, I found one other dependency I was not aware of - Microsoft fonts.

Solution?

sudo apt-get install ttf-mscorefonts-installer

Gray Background for Read-Only QLineEdit

Setting a text box (aka QLineEdit) to read-only in QT doesn’t make its background gray as one would normally expect. Background remains as is with user confused as to why text cannot be changed. Classic Windows solution of graying out background seems much better to me and I decided to replicate the same.

My requirements for this were just two. It had to work correctly under both Windows and Linux. And it couldn’t use static color but follow the overall theme.

The first idea was to use background role.

ui->lineEdit->autoFillBackground(true);
ui->lineEdit->setBackgroundRole(QPalette::Window);

And, even with background auto-filling set, this doesn’t work.

As I was after the same color as window background, setting the whole text box transparent seemed at least plausible.

ui->lineEdit->setStyleSheet("background: transparent;");

This sort-of worked but with only top border-line visible instead of full rectangle. Passable but ugly.

After a few more false starts, I finally found code that fulfills both requirements.

QPalette readOnlyPalette = ui->lineEdit->palette();
readOnlyPalette.setColor(QPalette::Base, ui->lineEdit->palette().color(QPalette::Window));
ui->lineEdit->setPalette(readOnlyPalette);

Essentially, we modify palete and set it’s color to what Window would use. A bit more involved than just setting read-only property but I guess not too difficult either.

Copy to Clipboard in QT

The first time I tried to implement clipboard copy in QT all seemed easy. Just call setText on QClipboard object.

QClipboard* clipboard = QApplication::clipboard();
clipboard->setText(text);

Really simple and it doesn’t really work. Well, actually, it depends. If you run this under Windows, it works perfectly. If you run this under Linux, it works until you try to paste into Terminal window. Once you do, you will surprisingly see that clipboard used for Terminal isn’t the same clipboard as for the rest of system. But then you read documentation again and learn a bit about selection clipboard.

QClipboard* clipboard = QApplication::clipboard();

clipboard->setText(text, QClipboard::Clipboard);

if (clipboard->supportsSelection()) {
    clipboard->setText(text, QClipboard::Selection);
}

And now you have a fool-proof setup for both Linux and Windows, right? Well, you’ll notice that in Linux some copy operations are simply ignored. You believe you copied text but the old clipboard content gets pasted. So you copy again just to be sure and it works. Since failure is sporadic at best, you might even convince something is wrong with you keyboard.

However, the issue is actually in timing of clipboard requests. If your code proceeds immediately with other tasks Linux (actually it’s X11 problem but let’s blame Linux ;)) might not recognize the new clipboard content. What you need is a bare minimum pause to be sure system had chance to take control and store the new clipboard information.

QClipboard* clipboard = QApplication::clipboard();

clipboard->setText(text, QClipboard::Clipboard);

if (clipboard->supportsSelection()) {
    clipboard->setText(text, QClipboard::Selection);
}

#if defined(Q_OS_LINUX)
    QThread::msleep(1); //workaround for copied text not being available...
#endif

And now this code finally works for both Linux and Windows.

Ubuntu 19.10 Fails to Install Deb File

Illustration

As I upgraded to Ubuntu 19.10, I went to install a few of my Linux applications and immediately got Failed to install file: not supported error. Debian packages that worked for me with 19.04 were suddenly causing the issue.

I traced this to my control file:

Package:        Bimil
Version:        MAJOR.MINOR
Architecture:   all
Maintainer:     Josip Medved <jmedved@jmedved.com>
Homepage:       https://www.medo64.com/bimil/
Description:    Password manager.
                A small password manager compatible with Password Safe file format.
Section:        misc
Priority:       optional
Depends:        mono-complete (>=4.2), gnupg2

While this worked fine for Ubuntu 19.04, in Ubuntu 19.10, one has to have Build-Depends value too:

Package:        Bimil
Version:        MAJOR.MINOR
Architecture:   all
Maintainer:     Josip Medved <jmedved@jmedved.com>
Homepage:       https://www.medo64.com/bimil/
Description:    Password manager.
                A small password manager compatible with Password Safe file format.
Section:        misc
Priority:       optional
Depends:        mono-complete (>=4.2), gnupg2
^^Build-Depends:  mono-complete (>=4.2)^^

Once I rebuilt package with it, my .deb files worked once again.

SIGFAULT in QVector size()

Illustration

After a long time, I started playing with C++ again - this time in the form of QT. Since I learned C++ a while ago, my code was a bit rustic and probably not following all the newest recommendations, but hey, it worked. Until I decided to change it from using QVector<std::shared_ptr<FolderItem>> to QVector<FolderItem*>. Suddenly I kept getting SIGFAULT in the most innocuous method: size().

As Google is my crutch, I did a quick search for this and found absolutely nothing useful - just other people having the same issue. After finally looking at my code, I figured why - because I was asking the wrong question. Of course there was nothing wrong with size() method in QVector class used by millions. It was me all along.

I had _folder variable in main window that I never initialized and I used it to call its method which in turn used QVector.size(). Forgetting to initialize this variable if you’re using shared_ptr is nothing - it simply ends up being null pointer and I had check for those. Once I changed to raw pointer, I lost that protection and uninitialized variable did what uninitialized variable in C++ does - pointed to “something”. Executing function “somewhere” in memory is what brought SIGFAULT - not the poor size() method.

It was all solved by something I used to do reflexively: initialize any variable declared in C++ to some value - even if that value is nullptr.

.NET Core 3.0

.NET Conf 2019 had one significant news this year - .NET Core 3.0.

The biggest feature for me is self-contained executable. It’s not ideal as actual package is unpacked upon the first execution but it’s definitely a step in the right direction.

Other feature I am excited about is HTTP/2 combined with TLS 1.3. Not only performant but also secure. Frankly, this almost makes me yearn for making my own CMS again. Almost. :)

Finally there is a support for serial port under Linux. Considering how basic serial port support is for any OS, it’s hard to believe it took until 3.0 to finally do it. Speaking of hardware support, there is also a Raspberry Pi GPIO support in a separate repository.

Conference also brought C# 8.0 to light of day. I will probably write more about it later but suffice it to say there are many small but exciting features. I can already see nullable reference types being either really accepted or universally hated. I am more in the former camp but time will tell. :)

You can watch keynote yourself with many more videos to follow.