How can I access a symbol from the linker script in my Ada code? - ada

I am building my Ada/SPARK project using GNAT and I am using a linker script. Here is an excerpt:
SECTIONS
{
.code :
{
. = ALIGN(0x4);
*(.text.section1)
_end_of_section1 = .;
*(.text.section2)
...
}
}
The symbol _end_of_section1 is the address between the two sections. I'd like to be able to access this in my Ada code. I know it's possible in C using extern char _end_of_section1[];. Is it possible to do something like this in Ada? If not, is there some other way to get this address in the code?

You can import a linker symbol by using the Importand Link_Name aspects (see also RM B.1):
main.adb (updated on 25-jan)
with System.Storage_Elements;
with System.Address_Image;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
package SSE renames System.Storage_Elements;
package Storage_Element_IO is
new Ada.Text_IO.Modular_IO (SSE.Storage_Element);
use Storage_Element_IO;
Start_Symbol : aliased SSE.Storage_Element
with Import, Link_Name => "_start";
Start_Symbol_Addr : constant System.Address := Start_Symbol'Address;
begin
Put ("Address : ");
Put (System.Address_Image (Start_Symbol_Addr));
New_Line;
Put ("Value : ");
Put (Start_Symbol, Base => 16);
New_Line;
end Main;
output
$ ./obj/main
Address : 0000000000403300
Value : 16#F3#
output (objdump)
$ objdump -d -M intel ./obj/main | grep -A5 "<_start>"
0000000000403300 <_start>:
403300: f3 0f 1e fa endbr64
403304: 31 ed xor ebp,ebp
403306: 49 89 d1 mov r9,rdx
403309: 5e pop rsi
40330a: 48 89 e2 mov rdx,rsp
...

Related

64-bit ELF yielding unexplainable results

Can someone explain why the following code yields different results on the second printf if I comment the first printf line or not, in 64 bits?
/* gcc -O0 -o test test.c */
#include <stdio.h>
#include <stdlib.h>
int main() {
char a[20] = {0};
char b = 'a';
int count=-1;
// printf("%.16llx %.16llx\n", a, &b);
printf("%x\n", *(a+count));
return 0;
}
I get the following results for the second printf:
commented: 0
uncommented: 61
Thanks in advance!
iansus
Can someone explain why the following code yields different results on the second printf if I comment the first printf line or not
Your program uses a[-1], and thus exhibits undefined behavior. Anything can happen, and figuring out exactly why one or the other thing happenes is pointless.
The precise reason is that you are reading memory that gets written to by the first printf (when commented in).
I get a different result (which is expected with undefined behavior):
// with first `printf` commented out:
ffffffff
// with it commented in:
00007fffffffdd20 00007fffffffdd1b
ffffffff
You could see where that memory is written to by setting a GDB watchpoint on it:
(gdb) p a[-1]
$1 = 0 '\000'
(gdb) p &a[-1]
$2 = 0x7fffffffdd1f ""
(gdb) watch *(int*)0x7fffffffdd1f
Hardware watchpoint 4: *(int*)0x7fffffffdd1f
(gdb) c
Continuing.
Hardware watchpoint 4: *(int*)0x7fffffffdd1f
Old value = 0
New value = 255
main () at t.c:12
12 printf("%.16llx %.16llx\n", a, &b);
It my case above, the value is written as part of initializing count=-1. That is, with my version of gcc, count is located just before a[0]. But this may depend on compiler version, exactly how this compiler was built, etc. etc.

Lemon parser assertion failure if a set has only one entry

This might be my misunderstanding of how parsers reduce rather than a potential bug in SQLite's lemon parser. I have been experimenting with simple grammars for a database input file. The database consists of a list of at least one entry sets, things like "commands", or "maps" or...
Here's a grammar that does not work - I have started creating the entry sets and so far all I have is a "command":
database ::= entrylist.
entrylist ::= entrylist entryset.
entrylist ::= entryset.
entryset ::= command.
/* Commands are in the form %command [arguments] \n */
command ::= CMDNAME cmdargs EOL.
cmdargs ::= cmdargs cmdarg.
cmdargs ::= .
cmdarg ::= INTEGER.
cmdarg ::= TEXT.
If I run this with a test program that just feeds in tokens I get:
$ test
Debug: Input 'CMDNAME'
Debug: Shift 'CMDNAME', go to state 3
Debug: Return. Stack=[CMDNAME]
Debug: Input 'INTEGER'
Assertion failed: stateno <= YY_SHIFT_COUNT, file testpar.c, line 513
If I give the entryset an additional alternative:
entryset ::= command.
entryset ::= map.
...
map ::= MAPNAME EOL.
then the whole thing works as expected. I think perhaps you aren't allowed create a situation where a::=b and b::=c. You must have b ::= c | d at the very least. I'd like to understand if this is my mistake in understanding.
Lemon compresses the shift table to remove default actions from the end of the table. Presumably, default actions should be handled elsewhere but they aren't. Your example happens to have default actions so the table is compressed, YY_MAX_SHIFT and YY_SHIFT_COUNT are out of sync, and the assert is triggered.
In lemon.c, around line 4235, you can comment out this line:
while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--;
to prevent compression and to prevent the bug.
Generated code with compression:
#define YY_MAX_SHIFT 3
#define YY_SHIFT_COUNT (2)
#define YY_SHIFT_USE_DFLT (13)
static const unsigned char yy_shift_ofst[] = {
/* 0 */ 7, 1, 6,
};
Generated code without compression:
#define YY_MAX_SHIFT 3
#define YY_SHIFT_COUNT (3)
#define YY_SHIFT_USE_DFLT (13)
static const unsigned char yy_shift_ofst[] = {
/* 0 */ 7, 1, 6, 13,
};
I submitted the details to the SQLite mailing list earlier this year but nothing has happened yet. The correct solution is probably to continue compressing but handle default actions in the template.
Bug logged to SQLite mailing list:
http://www.mail-archive.com/sqlite-users#mailinglists.sqlite.org/msg99712.html
http://www.mail-archive.com/sqlite-users#mailinglists.sqlite.org/msg99716.html

How to build the Qt-SQL-driver-plugin 'QSQLCIPHER' for SQLite-DB with SQLCipher-extension using the Windows/MinGW-platform?

This is not typically a question where to find a step-by-step guide, but rather the guide itself.
My intention with this post is to give others a hint, who have the same problems in compiling the driver-plugin as I just had recently.
How to build the Qt-SQL-driver-plugin 'QSQLCIPHER' for SQLite-DB with SQLCipher-extension using the Windows/MinGW-platform?
How to build the Qt-SQL-driver-plugin 'QSQLCIPHER' for SQLite-DB with SQLCipher-extension using the Windows/MinGW-platform:
Qt 5.4.0 for Windows/MinGW
Download Qt
Install including the sources e.g to C:\Qt\Qt5.4.0
OpenSSL for Windows
Download Win32 OpenSSL v1.0.2a
Download Visual C++ 2008 Redistributable
Install Visual C++ 2008 Redistributable by executing 'vcredist_x86.exe'
Install OpenSSL v1.0.2a by executing 'Win32OpenSSL-1_0_2.exe'
Target directory e.g. C:\OpenSSL-Win32
During installation choose the option to install the libraries to the Windows system directory (C:\Windows\SysWOW64)
MinGW - Minimalist GNU for Windows
Download and install 'mingw-get-setup.exe'
Start of MinGW Installer
Installation of MSYS Base System
Selection: All Packages -> MSYS -> MSYS Base System
Select msys-base (Class 'bin') for installation
Menu: installation -> apply changes
Installation of files by default to directory C:\MinGW
Installation of Tcl/Tk
Selection: All Packages -> MinGW -> MinGW Contributed
Select 'mingw32-tcl' and 'mingw32-tk' (Class 'bin') for installation
Menu: installation -> apply changes
Installation of files by default to directory C:\MinGW
Copy content of C:\MinGW to the Qt-MinGW-directory C:\Qt\Qt5.4.0\Tools\mingw491_32
Create file 'fstab' in C:\Qt\Qt5.4.0\Tools\mingw491_32\msys\1.0\etc
Insert content as follows:
#Win32_Path Mount_Point
C:/Qt/Qt5.4.0/Tools/mingw491_32 /mingw
C:/Qt/Qt5.4.0/5.4 /qt
C:/ /c
zlib-Library
Download zlib-dll-Binaries
Extract and copy file 'zlib1.dll' to the Qt-MinGW-directory C:\Qt\Qt5.4.0\Tools\mingw491_32\msys\1.0\bin
SQLCipher
Download the SQLCipher-zip-file
Extract the zip-file e.g. to C:\temp\sqlcipher-master
Copy OpenSSL-Win32-libraries
Copy C:\OpenSSL-Win32\bin\libeay32.dll to C:\temp\sqlcipher-master
Copy C:\OpenSSL-Win32\lib\libeay32.lib to C:\temp\sqlcipher-master
Build SQLCipher.exe
Execute MSYS: C:\Qt\Qt5.4.0\Tools\mingw491_32\msys\1.0\msys.bat
$ cd /c/temp/sqlcipher-master
$ ./configure --prefix=$(pwd)/dist --with-crypto-lib=none --disable-tcl CFLAGS="-DSQLITE_HAS_CODEC -DSQLCIPHER_CRYPTO_OPENSSL -I/c/openssl-win32/include /c/temp/sqlcipher-master/libeay32.dll -L/c/temp/sqlcipher-master/ -static-libgcc" LDFLAGS="-leay32"
$ make clean
$ make sqlite3.c
$ make
$ make dll
$ make install
Save the executable SQLite/SQLCipher-database e.g. to C:\sqlcipher
Copy C:\temp\sqlcipher-master\dist\bin\sqlcipher.exe to C:\sqlcipher.
The file 'sqlcipher.exe' is the crypting equivalent to the non-crypting original command line interface 'sqlite3.exe'.
Copy C:\temp\sqlcipher-master\sqlite3.dll to C:\sqlcipher.
This file is the SQLite-library extended by the encryption.
The SQLite-database with SQLCipher-extension is now ready for work.
Build Qt-QSQLCIPHER-driver-plugin
Create directory:
C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\plugins\sqldrivers\sqlcipher
Create the following three files within the new directory:
File 1: smain.cpp:
#include <qsqldriverplugin.h>
#include <qstringlist.h>
#include "../../../../src/sql/drivers/sqlite/qsql_sqlite_p.h" // There was a missing " at the end of this line
QT_BEGIN_NAMESPACE
class QSQLcipherDriverPlugin : public QSqlDriverPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSqlDriverFactoryInterface" FILE "sqlcipher.json")
public:
QSQLcipherDriverPlugin();
QSqlDriver* create(const QString &);
};
QSQLcipherDriverPlugin::QSQLcipherDriverPlugin()
: QSqlDriverPlugin()
{
}
QSqlDriver* QSQLcipherDriverPlugin::create(const QString &name)
{
if (name == QLatin1String("QSQLCIPHER")) {
QSQLiteDriver* driver = new QSQLiteDriver();
return driver;
}
return 0;
}
QT_END_NAMESPACE
#include "smain.moc"
File 2: sqlcipher.pro
TARGET = qsqlcipher
SOURCES = smain.cpp
OTHER_FILES += sqlcipher.json
include(../../../sql/drivers/sqlcipher/qsql_sqlite.pri)
wince*: DEFINES += HAVE_LOCALTIME_S=0
PLUGIN_CLASS_NAME = QSQLcipherDriverPlugin
include(../qsqldriverbase.pri)
File 3: sqlcipher.json
{
"Keys": [ "QSQLCIPHER" ]
}
Copy directory C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\sql\drivers\sqlite
to C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\sql\drivers\sqlcipher
Customize file C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\sql\drivers\sqlcipher\qsql_sqlite.pri
The content of the file shall be like:
HEADERS += $$PWD/qsql_sqlite_p.h
SOURCES += $$PWD/qsql_sqlite.cpp
!system-sqlite:!contains(LIBS, .*sqlite3.*) {
include($$PWD/../../../3rdparty/sqlcipher.pri) #<-- change path of sqlite.pri to sqlcipher.pri here !
} else {
LIBS += $$QT_LFLAGS_SQLITE
QMAKE_CXXFLAGS *= $$QT_CFLAGS_SQLITE
}
The remaining two files in this directory need not to be changed.
Create file 'sqlcipher.pri' in directory C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\3rdparty with following content:
CONFIG(release, debug|release):DEFINES *= NDEBUG
DEFINES += SQLITE_OMIT_LOAD_EXTENSION SQLITE_OMIT_COMPLETE SQLITE_ENABLE_FTS3 SQLITE_ENABLE_FTS3_PARENTHESIS SQLITE_ENABLE_RTREE SQLITE_HAS_CODEC
!contains(CONFIG, largefile):DEFINES += SQLITE_DISABLE_LFS
contains(QT_CONFIG, posix_fallocate):DEFINES += HAVE_POSIX_FALLOCATE=1
winrt: DEFINES += SQLITE_OS_WINRT
winphone: DEFINES += SQLITE_WIN32_FILEMAPPING_API=1
qnx: DEFINES += _QNX_SOURCE
INCLUDEPATH += $$PWD/sqlcipher c:/openssl-win32/include
SOURCES += $$PWD/sqlcipher/sqlite3.c
LIBS += -L$$PWD/sqlcipher/lib -lsqlcipher -leay32 -lsqlite3
TR_EXCLUDE += $$PWD/*
Create and fill C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\3rdparty\sqlcipher
Create the two directories:
C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\3rdparty\sqlcipher
C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\3rdparty\sqlcipher\lib
Copy the following files to C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\3rdparty\sqlcipher:
C:\temp\sqlcipher-master\shell.c
C:\temp\sqlcipher-master\sqlite3.c
C:\temp\sqlcipher-master\sqlite3.h
C:\temp\sqlcipher-master\sqlite3ext.h
Copy the following files/directories to C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\3rdparty\sqlcipher\lib:
C:\temp\sqlcipher-master\dist\lib
C:\temp\sqlcipher-master\sqlite3.dll
C:\OpenSSL-Win32\bin\libeay32.dll
The directory now consists of the following files and directories:
C:\QT\QT5.4.0\5.4\SRC\QTBASE\SRC\3RDPARTY\SQLCIPHER
| shell.c
| sqlite3.c
| sqlite3.h
| sqlite3ext.h
|
\---lib
| libeay32.dll
| libsqlcipher.a
| libsqlcipher.la
| sqlite3.dll
|
\---pkgconfig
sqlcipher.pc
Compile the QSQLCIPHER-driver-plugin for Qt:
Open Qt-command line C:\Windows\System32\cmd.exe /A /Q /K C:\Qt\Qt5.4.0\5.4\mingw491_32\bin\qtenv2.bat
Execute the following commands:
cd C:\Qt\Qt5.4.0\5.4\Src\qtbase\src\pluins\sqldrivers\sqlcipher
qmake
mingw32-make
This builds the QSQLCIPHER-driver-plugin within the following directory:
C:\QT\QT5.4.0\5.4\SRC\QTBASE\PLUGINS\SQLDRIVERS
libqsqlcipher.a
libqsqlcipherd.a
qsqlcipher.dll
qsqlcipherd.dll
Copy 'qsqlcipher.dll' and 'qsqlcipherd.dll' to the SQL-driver-plugin-directory C:\Qt\Qt5.4.0\5.4\mingw491_32\plugins\sqldrivers.
Create a new encrypted SQLite/SQLCipher-database
Create new SQLite-Plaintext-database 'plaintext.db' with a test table and some test data
Change directory to C:\sqlcipher, which contains 'sqlcipher.exe' and 'sqlite3.dll' (see above).
C:\sqlcipher>sqlcpher.exe plaintext.db
SQLCipher version 3.8.6 2014-08-15 11:46:33
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table testtable (id integer, name text);
sqlite> insert into testtable (id,name) values(1,'Bob');
sqlite> insert into testtable (id,name) values(2,'Charlie');
sqlite> insert into testtable (id,name) values(3,'Daphne');
sqlite> select * from testtable;
1|Bob
2|Charlie
3|Daphne
sqlite> .exit
Open C:\sqlcipher\plaintext.db using a standard text-editor:
Database scheme and test data can be read in plaintext.
Encrypting the plaintext-database
This will create the database C:\sqlcipher\encrypted.db using the key 'testkey'.
C:\sqlcipher>sqlcipher.exe plaintext.db
SQLCipher version 3.8.6 2014-08-15 11:46:33
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'testkey';
sqlite> SELECT sqlcipher_export('encrypted');
sqlite> DETACH DATABASE encrypted;
sqlite> .exit
Open C:\sqlcipher\encrypted.db using a standard text-editor:
Data are now encrypted.
For more useful information visit:
https://www.zetetic.net/sqlcipher/sqlcipher-api/
Usage of the SQLite-database with SQLCipher-extension and access via Qt
Create a new Qt-command-line-project e.g. 'qsqlcipher'
Project file
QT += core sql
QT -= gui
TARGET = qsqlcipher
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
Test-program 'main.cpp'
#include <QCoreApplication>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QDebug>
#include <QString>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
qDebug() << QSqlDatabase::drivers();
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLCIPHER");
db.setDatabaseName("C:/sqlcipher/encrypted.db");
db.open();
QSqlQuery q;
q.exec("PRAGMA key = 'testkey';");
q.exec("insert into testtable (id,name) values(4,'dummy')");
q.exec("SELECT id,name anz FROM testtable");
while (q.next()) {
QString id = q.value(0).toString();
QString name = q.value(1).toString();
qDebug() << "id=" << id << ", name=" << name;
}
db.close();
return 0;
}
Compile and execute
("QSQLCIPHER", "QSQLITE", "QMYSQL", "QMYSQL3", "QODBC", "QODBC3", "QPSQL", "QPSQL7")
id= "1" , name= "Bob"
id= "2" , name= "Charlie"
id= "3" , name= "Daphne"
id= "4" , name= "dummy"
When delivering a Qt-program do not forget the Qt-libraries, the platforms-libraries, SQL-driver-plugin 'qsqlcipher.dll' and the OpenSSL-library 'libeay32.dll'.
Example for the test program above:
C:\TEMP\QSQLCIPHER-TEST
| icudt53.dll
| icuin53.dll
| icuuc53.dll
| libeay32.dll
| libgcc_s_dw2-1.dll
| libstdc++-6.dll
| libwinpthread-1.dll
| qsqlcipher.exe
| Qt5Core.dll
| Qt5Sql.dll
|
+---platforms
| qminimal.dll
| qoffscreen.dll
| qwindows.dll
|
\---sqldrivers
qsqlcipher.dll
Caution: The test program contains the key:
...
q.exec("PRAGMA key = 'testkey';");
...
This key string in the binary file of the test program can easiliy be read using a hex-editor, which is, to my opinion, a lack in security:
...
00002C90 70 68 65 72 2F 65 6E 63 72 79 70 74 65 64 2E 64 pher/encrypted.d
00002CA0 62 00 50 52 41 47 4D 41 20 6B 65 79 20 3D 20 27 b.PRAGMA key = '
00002CB0 74 65 73 74 6B 65 79 27 3B 00 00 00 69 6E 73 65 testkey';...inse
00002CC0 72 74 20 69 6E 74 6F 20 74 65 73 74 74 61 62 6C rt into testtabl
...
For approaches how to solve this problem, ask the search engine of your own choice. ;-)
E.g. search for: hide string in executable

No Output in NPPExec with Pascal

I want to write and build and execute a Pascal Program in Notepad ++. If i execute the program in cmd the output is normal, but in the console in nppexec the output is empty
My Code:
Program Edgar;
Uses Crt;
Var cnt, tip, pot : INTEGER;
Begin
TextColor(Red);
WriteLn('Hallo');
tip := -1;
cnt := 0;
Randomize;
pot := Random(2501)*10;
WriteLn(pot);
WHILE (tip <> pot) do
Begin
WriteLn('Tip: ');
ReadLn(tip);
if (tip < pot) then begin
WriteLn('Too low');
cnt := cnt + 1
end;
if (tip > pot) then begin
WriteLn('Too High');
cnt := cnt + 1
end;
end;
cnt:= cnt + 1;
WriteLn('IA IA');
WriteLn('Tries: ',cnt );
End.
Build Commands:
cd $(CURRENT_DIRECTORY)
fpc $(NAME_PART).pas
$(NAME_PART).exe
Output(Nppexec):
Free Pascal Compiler version 2.6.2 [2013/02/12]
for i386 Copyright (c) 1993-2012 by Florian Klaempfl
and others Target OS: Win32 for i386
Compiling ue23.pas
Linking ue23.exe 27 lines compiled, 0.1 sec , 33536 bytes code, 1900 bytes data
<<< Process finished.
(Exit code 0)
ue23.exe Process started >>>
If you enable unit CRT, the application will write to the console directly (using *console winapi functions) instead of using stdout.
Probably the console screen of npp is not a real console screen, but a capture of stdout (-piped) only.
Except not using crt (and thus not using cursor movement and coloring) there is not much that can be done, this is probably a NPP limitation.
After that, you need to press "Enter" while your cursor blinking in output side.
And you will get the output with these lines at the end.
<<< Process finished. (Exit code 0)
================ READY ================
There is no limitation, you can run commands from that output side of notepad++.

How do I determine if a terminal is color-capable?

I would like to change a program to automatically detect whether a terminal is color-capable or not, so when I run said program from within a non-color capable terminal (say M-x shell in (X)Emacs), color is automatically turned off.
I don't want to hardcode the program to detect TERM={emacs,dumb}.
I am thinking that termcap/terminfo should be able to help with this, but so far I've only managed to cobble together this (n)curses-using snippet of code, which fails badly when it can't find the terminal:
#include <stdlib.h>
#include <curses.h>
int main(void) {
int colors=0;
initscr();
start_color();
colors=has_colors() ? 1 : 0;
endwin();
printf(colors ? "YES\n" : "NO\n");
exit(0);
}
I.e. I get this:
$ gcc -Wall -lncurses -o hep hep.c
$ echo $TERM
xterm
$ ./hep
YES
$ export TERM=dumb
$ ./hep
NO
$ export TERM=emacs
$ ./hep
Error opening terminal: emacs.
$
which is... suboptimal.
A friend pointed me towards tput(1), and I cooked up this solution:
#!/bin/sh
# ack-wrapper - use tput to try and detect whether the terminal is
# color-capable, and call ack-grep accordingly.
OPTION='--nocolor'
COLORS=$(tput colors 2> /dev/null)
if [ $? = 0 ] && [ $COLORS -gt 2 ]; then
OPTION=''
fi
exec ack-grep $OPTION "$#"
which works for me. It would be great if I had a way to integrate it into ack, though.
You almost had it, except that you need to use the lower-level curses function setupterm instead of initscr. setupterm just performs enough initialization to read terminfo data, and if you pass in a pointer to an error result value (the last argument) it will return an error value instead of emitting error messages and exiting (the default behavior for initscr).
#include <stdlib.h>
#include <curses.h>
int main(void) {
char *term = getenv("TERM");
int erret = 0;
if (setupterm(NULL, 1, &erret) == ERR) {
char *errmsg = "unknown error";
switch (erret) {
case 1: errmsg = "terminal is hardcopy, cannot be used for curses applications"; break;
case 0: errmsg = "terminal could not be found, or not enough information for curses applications"; break;
case -1: errmsg = "terminfo entry could not be found"; break;
}
printf("Color support for terminal \"%s\" unknown (error %d: %s).\n", term, erret, errmsg);
exit(1);
}
bool colors = has_colors();
printf("Terminal \"%s\" %s colors.\n", term, colors ? "has" : "does not have");
return 0;
}
Additional information about using setupterm is available in the curs_terminfo(3X) man page (x-man-page://3x/curs_terminfo) and Writing Programs with NCURSES.
Look up the terminfo(5) entry for the terminal type and check the Co (max_colors) entry. That's how many colors the terminal supports.

Resources