{"id":14593,"date":"2026-09-14T01:00:00","date_gmt":"2026-09-14T05:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=14593"},"modified":"2026-09-05T11:43:43","modified_gmt":"2026-09-05T15:43:43","slug":"two-ways-to-write-portable-software","status":"publish","type":"post","link":"http:\/\/www.both.org\/?p=14593","title":{"rendered":"Two ways to write portable software"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"14593\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">    <\/span>\r\n<\/div><\/div>\n<p class=\"wp-block-paragraph\">Linux runs on pretty much everything these days. And because it runs everywhere, you might be content to assume \u201call the world runs Linux.\u201d<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But when you\u2019re writing an open source app, you shouldn\u2019t make those assumptions. Your app might be <em>intended<\/em> to run on Linux today, but your app will enjoy more popularity if it can also run on other Unix-like systems like BSD, or on non-Unix systems like FreeDOS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You could aim for the \u201clowest common denominator\u201d and only use the standard C library. But that\u2019s boring. These programs can only scroll from the bottom of the screen. What if your program could use platform-specific features like colors and windows?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s look at two ways to write portable programs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Method 1: Check at compile-time<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s say you wanted to write a number-guessing game, like \u201cguess the secret number from 1 to 10.\u201d That\u2019s a pretty simple program: generate a random secret number, and prompt the user to make a guess. At each guess, let the user know if their guess was too high or too low, and keep going until they guess the right number.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you\u2019re using the standard C library, you can generate a pseudo-random number using the <code>rand<\/code> function. This is guaranteed to work on every system, because the random numbers are generated through software. To use it, you first need to <em>seed<\/em> the random number generator with some value, such as the current time, then every call to <code>rand<\/code> gives you a new pseudo-random value between 0 and some \u201cmaximum\u201d value. You can do a little math to make that a number between 1 and 10:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>   srand(time(NULL));\n    secret = rand() % 10 + 1;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On Linux, you can get a better random value by using the <code>getrandom<\/code> system call. Instead of returning a random number, <code>getrandom<\/code> actually fills a variable with random <em>bits<\/em>, and that gives you a number:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>   unsigned char n;\n    getrandom(&amp;n, sizeof(unsigned char), GRND_NONBLOCK);\n    secret = n % 10 + 1;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But if you use <code>getrandom<\/code>, the program will only work on Linux. Other operating systems may not have this system call, or they may define it differently. Instead, if you wanted to write a program that worked for both Linux and non-Linux systems, you\u2019ll need a way to get the <em>compiler<\/em> to do the work of adding the right code for you at <em>compile-time<\/em>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can do that using the <code>#if<\/code> statement. This is actually a <em>pre-processor<\/em> directive, so it gets used <em>before<\/em> the code is compiled, which makes it a great way to conditionally include or exclude code when you compile your program.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Compilers define a \u201cmacro\u201d or \u201cconstant\u201d that define what system you are compiling on. Linux systems use <code>__linux__<\/code>. Other operating systems use something similar; check your compiler\u2019s documentation for what values it defines on your system. For example, DOS compilers usually define <code>__DOS__<\/code> or <code>__MSDOS__<\/code> because MS-DOS was the most popular DOS in the 1980s and 1990s.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can check if this constant is \u201cset\u201d on your system by using <code>#if defined<\/code> with the macro name in parentheses. You can also use <code>#elif<\/code> to mean \u201celse if,\u201d <code>#else<\/code> as \u201celse,\u201d and <code>#endif<\/code> to end the \u201cif\u201d tests. I recommend checking for specific value or operating system to include system-specific stuff, and using an <code>#else<\/code> to provide some generic \u201cworks on everything\u201d solution. Here\u2019s an example, using a simple \u201cguess the number\u201d game:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;stdio.h&gt;\n\n#if defined(__linux__)\n#include &lt;sys\/random.h&gt;\n#else\n#include &lt;stdlib.h&gt;                    \/* rand *\/\n#include &lt;time.h&gt;                      \/* time *\/\n#endif\n\nint main()\n{\n    int secret, guess;\n\n#if defined(__linux__)\n    unsigned char n;\n    getrandom(&amp;n, sizeof(unsigned char), GRND_NONBLOCK);\n    secret = n % 10 + 1;               \/* 1 to 10 *\/\n#else\n    srand(time(NULL));\n    secret = rand() % 10 + 1;          \/* 1 to 10 *\/\n#endif\n\n    puts(\"Guess the number from 1 to 10:\");\n\n    do {\n        fputs(\"Your guess? \", stdout);\n        scanf(\"%d\", &amp;guess);\n\n        if (guess &lt; secret) { puts(\"Too low\"); }\n        if (guess &gt; secret) { puts(\"Too high\"); }\n    } while (guess != secret);\n\n    puts(\"That's right!\");\n    return 0;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note that the source uses <code>#if<\/code> to check if the program is being compiled on Linux; if it is, the program uses the Linux-specific <code>getrandom<\/code> system call. For all other operating systems (such as FreeDOS) the program uses the <code>rand<\/code> function from the standard C library.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That means you can compile <em>one<\/em> source file to work on <em>many<\/em> different systems. As an example, I compiled this program on Linux using GCC, and on FreeDOS using two compilers (the Open Watcom C compiler, and an older C compiler called BCC) to show that the program works as expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>linux$ gcc -Wall -o rand rand.c\n\nlinux$ .\/rand\nGuess the number from 1 to 10:\nYour guess? 5\nToo low\nYour guess? 8\nToo low\nYour guess? 10\nToo high\nYour guess? 9\nThat's right!<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">and:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>A:\\RAND&gt;wcl -q rand.c\n\nA:\\RAND&gt;rand\nGuess the number from 1 to 10:\nYour guess? 5\nToo high\nYour guess? 3\nToo high\nYour guess? 1\nThat's right!\n\nA:\\RAND&gt;bcc -ansi -o rand.com rand.c\n\nA:\\RAND&gt;rand.com\nGuess the number from 1 to 10:\nYour guess? 5\nToo high\nYour guess? 3\nToo high\nYour guess? 1\nToo low\nYour guess? 2\nThat's right!<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Method 2: Isolate the system-specific stuff<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If your program is more complicated than this, then <code>#if<\/code> probably won\u2019t solve all of your portability problems. As an example, maybe your program uses a <em>text-based interface<\/em> like <em>curses<\/em> (or <em>ncurses<\/em>). That\u2019s a big dependency that could make it difficult to compile your program on another system. For example, <em>conio<\/em> on DOS operating systems is similar <em>but different<\/em>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this case, it will be better to split up some your source files, so you can isolate any \u201csystem-specific\u201d stuff to separate files.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s use a simple example: a turn-based \u201csim\u201d game where the game prints the current \u201cstatus,\u201d then you make a choice, and the game iterates the next version of the sim. In a game like this, you might put the \u201ccore\u201d parts of the game in a file called <code>main.c<\/code>, the game logic in <code>game.c<\/code>, and any \u201cscreen control\u201d in a file like <code>screen.c<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With this solution, you can now write <em>separate<\/em> versions of the <code>screen.c<\/code> source file: one that uses <em>curses<\/em> (for Linux) and one that uses <em>conio<\/em> (on DOS). The function interfaces are the same: <code>start_screen<\/code> to set up the colors and create text windows, <code>end_screen<\/code> to clean up after itself. And you might create functions that the game can use, like <code>status<\/code> to print the sim\u2019s status and <code>menu<\/code> to prompt the user. Function <em>primitives<\/em> for these can be defined in <code>screen.h<\/code>, which will be the same for Linux or DOS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With these assumptions, the main game could be quite simple:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;stdio.h&gt;\n\n#include \"game.h\"\n#include \"screen.h\"\n\nint main()\n{\n    if (start_screen() == 0) {\n        puts(\"cannot initialize screen\");\n        return 1;\n    }\n\n    if (start_game() == 0) {\n        puts(\"cannot start game\");\n        end_screen();\n        return 2;\n    }\n\n    do {\n        next_year();\n        status();\n    } while (menu() &gt; 0);\n\n    end_game();\n    end_screen();\n\n    return 0;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There\u2019s nothing platform-specific in this source file; this will compile equally well on all platforms. The same is true of <code>game.c<\/code>, if it only contains game logic. It\u2019s only the <code>screen.c<\/code> file that has any system-dependent features in it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I wrote a simple <code>screen.c<\/code> for DOS and Linux to demonstrate this. I don\u2019t think I need to share the source code here, but you can learn about <em>conio<\/em> and <em>curses<\/em> programming in these articles, also at Both.org: <a href=\"https:\/\/www.both.org\/?p=7495\">a gentle introduction to ncurses<\/a> or <a href=\"https:\/\/www.both.org\/?p=12021\">write directly to the screen with DOS conio<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On DOS, <code>start_screen<\/code> would probably initialize the <em>conio<\/em> library and set up a few text windows, <code>end_screen<\/code> would return the screen to normal mode, <code>status<\/code> would display the game\u2019s current stats in a text window, and <code>menu<\/code> would use another text window to present a menu and prompt for an action.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"533\" height=\"400\" src=\"http:\/\/www.both.org\/wp-content\/uploads\/2026\/08\/game_dos.png\" alt=\"a text-based game showing two windows, in color\" class=\"wp-image-14591\"\/><figcaption class=\"wp-element-caption\">A sim game, using conio on FreeDOS<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">On Linux (or some other Unix-like operating system), <code>start_screen<\/code> and <code>end_screen<\/code> would instead use the <em>curses<\/em> or <em>ncurses<\/em> library to check that the terminal is large enough to play the game, and define some text windows. The <code>status<\/code> function would use a text window to show the sim\u2019s stats, while <code>menu<\/code> would show a menu in a different text window and let the user select an action.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"533\" height=\"368\" src=\"http:\/\/www.both.org\/wp-content\/uploads\/2026\/08\/game_linux.png\" alt=\"a text-based game showing two windows, in black and white\" class=\"wp-image-14592\"\/><figcaption class=\"wp-element-caption\">The same sim, using curses on Linux<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">With a little extra creativity, you could write a different <code>screen.c<\/code> source file to support a graphical environment. I\u2019ll leave that up to you.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Make it flexible<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Not every Unix-like system is Linux. If you\u2019re writing a Linux program today, it might run on something else tomorrow. So it\u2019s important for open source developers to always keep an eye to how to write programs that will run on as many systems as possible.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Make sure your programs will compile across different programs.<\/p>\n","protected":false},"author":33,"featured_media":2949,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":"","_members_access_role":[],"_members_access_error":""},"categories":[5,610,150,642],"tags":[267,91,152],"class_list":["post-14593","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux","category-portability","category-programming","category-software","tag-freedos","tag-linux","tag-programming"],"modified_by":"Jim Hall","_links":{"self":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/14593","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=14593"}],"version-history":[{"count":1,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/14593\/revisions"}],"predecessor-version":[{"id":14595,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/14593\/revisions\/14595"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/2949"}],"wp:attachment":[{"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=14593"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=14593"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=14593"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}