menu

Top 1 deals for Aba Software Software

40
Followers

Get all the best deals that Aba Software has to offer right here on BitsDuJour. Click quick, these discounts don't last long, and we update daily! We have deals on Aba Search and Replace, .

If you Follow Aba Software, you'll get emails when deals go live!

Visit the Aba Software website.

Aba Search and Replace Screenshot
Aba Search and Replace (PC) Discount
for PC

Aba Search and Replace

Easily Perform Global Search and Replace Operations

30% Off
 

Vendor Pulse

The latest news, straight from our vendors.

  • May 2 at 1:31pm
    The age of fast food

    An analogy came to my mind this winter: vibecoding is fast food. You can cook at home (write the code yourself), you can go to a good restaurant (hire a programmer), or you can buy yourself a hamburger (use AI tools).

    A Popeyes restaurant in Brno in February

    AI works in the short term, but in the long term, it's as unsustainable and unhealthy as eating fast food for every meal. It can quickly generate a lot of code that nobody fully understands. As an extreme example: Gregory Terzian, a Servo developer, called the web browser generated by Cursor's AI agents "a uniquely bad design that could never support anything resembling a real-world web engine".

    There certainly are many options that lie between using an AI agent to build a whole product and using no AI at all. Just like restaurants that order ready-to-cook chicken breasts instead of buying whole birds with heads and feet intact, you can carefully use AI for boring tasks. In many real projects, there are one-shot scripts, long configuration files, boilerplate, prototypes, or bureaucratic reports. If you can avoid or abstract out these tasks, it's wonderful, but sometimes you just can't, either for legal reasons or because of corporate bureaucracy. AI is fine for these tasks.

    However, please treat AI output as a disposable, low-quality attempt, not like a piece of wisdom sent to you from heaven:

    • Please don't paste LLM output in conversations. If I ask you about something, then I consider you smarter than an LLM, which I could have asked myself.
    • Don't assume that AI could quickly improve code that somebody else planned and built for several months. Again, it suggests that AI is smarter than the person, which is insulting to them.
    • Don't send for review a huge AI-generated pull request without a clear aim. AI agents tend to do more than you asked for, so they start to "correct" the code and often introduce subtle errors in the process. Prefer a focused, controlled change that is easier to review.
    • Use AI for code review, but note that it cannot replace a human reviewer. AI is great at finding errors that a human easily misses (such as typos, inconsistent naming, or coding standard violations). In contrast, a human reviewer understands the context and business logic, which AI cannot do. Many AI-generated suggestions are incorrect or excessive.
    • Please know that you can always run the same prompt again with different results, try a different LLM, search the Web, or even consult a book. Don't trust LLMs blindly, especially for factual information. Hallucinations are still a problem.

    Unfortunately, the world in general is not getting smarter in recent years. The overuse of AI fits into this trend. We now have AI-generated music and AI-generated books that were predicted by George Orwell in his novel "1984". When I first read about AI agents, I couldn't help but remember an old Helloween's song:

    Sometimes, when he's feeling bored, he's calling it a day
    He's got his computers and they do it their own way

    Do we live in a dystopia now? Yes and no. It was possible to build a whole software product cheaply before; it was called outsourcing twenty years ago. For the $20'000 that Anthropic spent on vibecoding a C compiler, they could have hired somebody in a very poor country, which would end up with a similar result, just not so fast.

    Great software still takes time, effort, and consideration. It's possible that the AI mania will make us value artisanal things more. If everything could be generated, then what is left to us is our ingenuity.

    Just like my previous blog post on the subject, this one was written without any help from LLMs. I'll continue to "cook" delicious software for you.

  • Mar 8 at 2:14pm
    Regex for phone numbers

    Phone numbers in the US and Canada consist of ten digits: a three-digit area code, a three-digit central office code, and a four-digit line number. These components may be separated by dashes, spaces, or dots; the area code is often written in parentheses:

    619-555-0167
    (619) 555-0167
    619.555.0167
    

    There are some limits on the area code:

    • the area code cannot start with 0 or 1 (these digits are used for long-distance calls);
    • the N11 codes are used for special services (most people remember 911 for emergencies);
    • 988 is a suicide crisis helpline.

    Taking these restrictions into account, we can put together this regular expression to match a 10-digit US phone number:

    ^[ \t]*\(?(?!988)[2-9](?!11)\d\d\)?[ .-]?\d{3}[ .-]?\d{4}[ \t]*$
    

    We use a negative lookahead here to skip the N11 codes and 988, so this regex is a bit more fool-proof than other solutions floating on the Internet and regurgitated by LLMs. For example, it won't match 123-456-7890 or 911-123-4567.

    The [ \t]* part skips optional whitespace before or after the phone number. The regex starts with ^ and ends with $, so it will match the whole string or a line (in multi-line mode). If you want to find phone numbers in the text rather than verify that the string is a phone number, please use \b to match word boundaries:

    \b\(?(?!988)[2-9](?!11)\d\d\)?[ .-]?\d{3}[ .-]?\d{4}\b
    

    Another useful addition to this regex is allowing the initial +1 or 1 for long-distance calling:

    ^[ \t]*(?:\+1 ?|1 ?)?\(?(?!988)[2-9](?!11)\d\d\)?[ .-]?\d{3}[ .-]?\d{4}[ \t]*$
    

    Now the regex also matches:

    +1 619-555-0167
    +16195550167
    1 619-555-0167
    
    Regex for US phone numbers

    International phone numbers consist of a plus sign, a country code (1 to 3 digits), and the national phone number. The total phone number length is limited to a maximum of 15 digits. The minimum length is not specified in the standard, but I found Niue, an island country with a three-digit country code and four-digit national numbers. Five digits are more common, see Tokelau, Tonga, Solomon Islands, or Vanuatu. This gives us the following regex:

    ^[ \t]*\+(?:\d\)?[ .-]?\(?){6,14}\d[ \t]*$
    

    We first match the plus sign, then a digit. After each digit, there can be a closing parenthesis, then a space, dot, or dash, then an opening parenthesis. The last digit is matched separately; the total number of digits is from 7 to 15.

    Restricting the phone number length for each country would be complicated and the numbering plans change sometimes, so I recommend allowing some freedom here.

    The regular expressions above also don't try to balance parentheses; for example, +1 (214 555-0198 would match, but this is rarely a problem.

    Finally, if you want to match either an international phone number or a US/Canadian number, you can combine both regexes:

    ^[ \t]*(?:(?:\+|011 ?)(\d\)?[ .-]?\(?){6,14}\d|\(?(?!988)[2-9](?!11)\d\d\)?[ .-]?\d{3}[ .-]?\d{4})[ \t]*$
    

    In this combined regex, we also allow international phone numbers to start with 011 instead of the plus.

  • Jan 18 at 3:32pm
    On coding with LLMs

    It looks like we are near the peak of the AI mania. The usefulness and the limitations of this technology are clear now. What we see now are attempts to overstretch the idea and apply it where it will never work, much like with blockchain and cryptocurrencies several years ago.

    The ubiquitous AI startups will fail and we will see a decline in stock indexes, as well as a re-evaluation of AI strategies in many companies. I'm not excited about new AI agents, nor scared of becoming irrelevant; I'm bored now. Users are sick of tasteless drawings, trying-to-be-helpful AI summaries, and chat windows popping up from every toaster.

    Please don't get me wrong: there are a lot of areas where LLMs are truly useful. I'm glad that I don't have to write tedious unit tests fully by hand anymore. It's great that you can quickly toss together a throw-away script, translate a function from one programming language to another, or generate something as a starting point when coding in a language that you barely know. The problems arise when you perceive this "something" as a finished product.

    An old book (Code Complete by Steve McConnell) says that writing and reviewing code takes around 30% of an average developer's time. The rest is spent in discussions, replying to emails, reading documentation, troubleshooting, learning, or even personal stuff. The data come from a 1964 research and the proportion certainly depends on the person and the company, but any developer can confirm that they are not working with source code 100% of their time. If we assume the 30% estimate is correct and you can speed up coding by 20%, you can only expect a 5% reduction in the project duration (see Amdahl's law).

    So the promises of creating a complete product in a weekend are unrealistic. Programming is hard. I spend hours debugging UI issues or checking the assembler output and this is my own project coded by hand, so I understand it quite well. If I were vibecoding, it would take more time to find the root cause.

    In my day job, I never worked on a project where I would need to generate a lot of mediocre code quickly with an LLM. The usual story goes like this: two Californian guys, working 24/7, create a product that suddenly becomes popular. The code is a mess; they hit scalability limits, so they hire a Central European like me because a developer from the Bay Area with the same skills would cost them more; at the same, the salary is higher than what I could earn in my region. So we both tolerate the cultural differences, wild accents, and crazy time zones.

    I gradually debug the issues, make their code better organized and faster. For one company, I managed to make it six times faster. This does not mean that the founders were bad developers; they just cut some corners when creating the first version. But at least, the code was written by a human.

    Many new founders today vibecode, so I think that I will be able to do this job until I die or lose interest. We will see a lot of weird class hierarchies, logical mistakes patched with special case handling, and buggy snippets that the LLM ripped off from GitHub or StackOverflow.

    Less young people will be able to solve technical problems because they will rely on AI when studying. And if you got used to asking ChatGPT for solutions to simple problems, you will never learn how to troubleshoot complex or novel issues that AI cannot help you with. Cognitive skills require training and practice just like physical ones.

    Besides that, prompting is a skill on its own. Sometimes it's easier to write code than to explain verbally in every detail how it should work, especially if English is not your first language.

    I would say that vibecoding only adds another layer of abstraction above high-level programming languages. If your project contains a lot of repetitive AI-generated code, it's a sign that you may benefit from designing a library, a framework, or a DSL (a domain-specific language). Metaprogramming techniques can help in some cases, too.

    As Noam Chomsky noted almost three years ago, LLMs are not a replacement for human mind; they are useful, but cannot think in the way that we do. The mental health impact of AI is under active study now. IMHO, chatting with a video card chip is not something natural for a human being and it certainly creates some information overload. Overall, LLMs don't make software engineering much simpler; they even add complexity when used incorrectly.

    I may add some AI features to Aba Search and Replace in future, but it will never become a privacy-invasive AI agent and your files will always stay on your computer. When developing the app, I only use a locally-running LLM for proofreading texts. No vibecoding, no AI slop. As an experiment, this blog post was written without any help from AI.

  • Jan 11 at 2:33pm
    When msvc::musttail attribute silently fails

    Python developers recently reported a 15% speedup when using the new MSVC musttail attribute to create a threaded interpreter. Unfortunately, I found that MSVC does not always generate a tail call when you use this attribute, which can potentially lead to stack overflow when interpreting a complex program.

    Background

    The musttail attribute, also supported by clang, forces the compiler to generate a tail call for a return statement that calls another function. So instead of a CALL instruction to call this function followed by a RET to return from the current function, it emits a JMP instruction to jump to the next function without creating a new stack frame. MSVC recently added support for this technique, which is useful for p-code interpreters.

    P-code interpreters are traditionally coded as a giant switch statement inside a loop:

    for (each instruction in pcode) {
        switch (opcode) {
            case ADD:
                // do the addition
                break;
            case MUL:
                // do the multiplication
                break;
            ...
        }
    }
    

    The switch statement compiles to an indirect jump. All p-code instructions go through this jump, which makes it hard for the processor to predict the next branch.

    If musttail is supported by your compiler, you can create a function for each p-code instruction and link these functions via a dispatch table:

    function DoAdd(INSTR * instr) {
        // Do the addition
        // ...
        // Move to the next p-code instruction
        instr++;
        return dispatch_table[GetOpcode(instr)](instr); // musttail
    }
    
    function DoMul(INSTR * instr) {
        // Do the multiplication
        // ...
        // Move to the next p-code instruction
        instr++;
        return dispatch_table[GetOpcode(instr)](instr); // musttail
    }
    
    dispatch_table = {
        ADD: DoAdd,
        MUL: DoMul,
        ...
    };
    

    This compiles to an indirect jump in the epilogue of each function. When running your interpreter, the branch predictor will save the branch history separately for each p-code instruction, e.g. if your p-code usually runs MUL after ADD, the processor will remember this. That's why threaded code is usually faster.

    The MSVC problem

    The newly added [[msvc::musttail]] attribute is ignored when the function is moderately complex (so that it saves non-volatile registers on stack) and it has multiple returns (some of them without a tail call):

    void __declspec(noinline) increment(int x) {
        printf("%d\n", x + 1);
    }
    
    void incrementIfPositive(int x) {
        DWORD64 a = GetTickCount64();
        DWORD64 b = GetTickCount64();
        DWORD64 c = GetTickCount64();
        if (c == 0) {
            return;
        }
    
    [[msvc::musttail]]
        return increment(x + (int)(b - a + c / 2));
    }
    

    This is a made-up example, but a similar early return happens in a real interpreter when handling an exception. Assembly output:

    ; 18   :     if (a == 0) {
    
      test  rax, rax
      je    SHORT $LN1@incrementIfPositive
    
    ...
    
    ; 25   :     [[msvc::musttail]]
    ; 26   :     return increment(x + (int)(b - a + c / 2));
    
      shr  rax, 1
      lea  ecx, DWORD PTR [rbx+42]
      sub  eax, edi
      add  ecx, eax
      call ?increment@@YAXH@Z
      mov  rbx, QWORD PTR [rsp+48]
    $LN1@incrementIfPositive:
    
    ; 27   : }
    
      add  rsp, 32          ; 00000020H
      pop  rdi
      ret  0
    ?incrementIfPositive@@YAXH@Z ENDP
    

    Despite the [[msvc::musttail]] attribute, the Visual C++ compiler generates a call to the increment function. I think it’s because the function epilogue is quite long (with add rsp, 32 and pop rdi instructions), so the compiler does not want to duplicate it for the if (c == 0) case. Instead, the compiler generates a conditional jump to $LN1@incrementIfPositive when c == 0, but this prevents the musttail optimization.

    Visual C++ also does not produce any compilation error (as it should do according to the documentation), but just ignores the musttail attribute and generates the call instruction instead of jmp / rex_jmp.

    A workaround that I found is to create a useless handle_exception function and call it instead of returning early:

    int g_x;
    
    void __declspec(noinline) handle_exception(int x) {
        // Do something to avoid optimizing out this function
        g_x = x;
    }
    
    void incrementIfPositive(int x) {
        DWORD64 a = GetTickCount64();
        if (a == 0) {
            return handle_exception(x);
        }
        // The rest of the code is the same
        // ...
    

    Assembly output in this case:

    ; 18   :     if (a == 0) {
    
      test  rax, rax
      jne  SHORT $LN2@incrementIfPositive
    
    ; 27   : }
    
      add  rsp, 32
      pop  rdi
    
    ; 19   :         return handle_exception(x);
    
      jmp  ?handle_exception@@YAXH@Z
    
    ...
    
    ; 24   : 
    ; 25   :     [[msvc::musttail]]
    ; 26   :     return increment(x + (int)(b - a + c / 2));
    
      shr  rax, 1
      lea  ecx, DWORD PTR [rbx+42]
      sub  eax, edi
      add  ecx, eax
      mov  rbx, QWORD PTR [rsp+48]
    
    ; 27   : }
    
      add  rsp, 32
      pop  rdi
    
    ; 24   : 
    ; 25   :     [[msvc::musttail]]
    ; 26   :     return increment(x + (int)(b - a + c / 2));
    
      jmp  ?increment@@YAXH@Z
    ?incrementIfPositive@@YAXH@Z ENDP
    

    Here, a tail call is correctly generated. Unfortunately, this workaround does not help if you have more than one early return from the function. Even if you create several useless functions, it still won't work.

    Conclusion

    I encountered this problem when trying to apply the musttail optimization to the regular expression engine in Aba Search and Replace. I don't know if it affects the Python interpreter, but I reported the bug to Microsoft and it's under their consideration now.

  • Dec 21 2025 at 4:38am
    Mnemonics for hidden controls

    As you may know, you can switch between Windows controls using mnemonics. If you see an underlined character in any Windows application, you can press Alt + this character to set focus to this control or to click a button. For example, in this window, you can press:

    • Alt+D to go to the Decoded JSON Web Token field,
    • Alt+C to click the Copy button, or
    • Alt+F to uncheck the Format JSON option.
    Aba Search and Replace window

    The underlines become visible when you press the Alt key.

    However, if a control is hidden in Win32 (using the ShowWindow function), the mnemonic for it still works. You can still press a hidden button if you know its mnemonic. Imagine you hide a button for unauthorized users, but these users can press it using the Alt+character keyboard shortcut, so this behavior can even create a security vulnerability.

    If your application shows some controls and hides others depending on the state, you may have two controls with the same mnemonic. If one of them is hidden, it may be activated instead of the visible control with the same mnemonic. This happened to me when testing the previous version of Aba Search and Replace.

    The fix is easy: you need to disable a control when you hide it and enable it when you show it. Each ShowWindow call should be paired with an EnableWindow call; you could create a small function that will call both and use it everywhere instead of ShowWindow.

    This quirk applies only to the classic Win32 applications. I've tested with Windows Forms, WPF, and WinUI 3, and you cannot press a hidden button using its mnemonic there. Win32 provides an extra flexibility: you can create a mnemonic that does not correspond to any visible control, but this flexibility is usually not needed and can cause confusion.

BitsDuJour is for People who Love Software
Every day we review great Mac & PC apps, and get you discounts up to 100%
Follow Us
© Copyright 2026 BitsDuJour LLC. Code & Design. All Rights Reserved. Privacy Policy