Check for X11 extension - c

I am writing a shell script that needs to differ its behavior and provide different options to called programs based on the presence or absence of particular X11 extensions. I have a working solution, but I am hoping for a cleaner solution. I am open to considering a simple c program to do the test and return the result. Here is what I have working as a minimal functional example:
#!/bin/sh
xdpyinfo |sed -nr '/^number of extensions/,/^[^ ]/s/^ *//p' | \
grep -q $EXTENSION && echo present
I think there is a way to simplify the sed,grep but I really would prefer not to parse xdpyinfo.

You have the C-tag, too, so let me suggest to do the xdpyinfo yourself. The following C program prints just the extensions:
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int compare(const void *a, const void *b)
{
return strcmp(*(char **) a, *(char **) b);
}
static void print_extension_info(Display * dpy)
{
int n = 0, i;
char **extlist = XListExtensions(dpy, &n);
printf("number of extensions: %d\n", n);
if (extlist) {
qsort(extlist, n, sizeof(char *), compare);
for (i = 0; i < n; i++) {
printf(" %s\n", extlist[i]);
}
}
// TODO: it might not be a good idea to free extlist, check
}
int main()
{
Display *dpy;
char *displayname = NULL;
dpy = XOpenDisplay(displayname);
if (!dpy) {
fprintf(stderr, "Unable to open display \"%s\".\n",
XDisplayName(displayname));
exit(EXIT_FAILURE);
}
print_extension_info(dpy);
XCloseDisplay(dpy);
exit(EXIT_SUCCESS);
}
Compile with e.g.: GCC
gcc -O3 -g3 -W -Wall -Wextra xdpyinfo1.0.2.c $(pkg-config --cflags --libs x11) -o xdpyinfo1.0.2
(should give a warning about unused argc but that's harmless)
Just change the printf()'s to the format you want.

Related

C main parameter

I wrote a code which has to display main parameters, but when I compiled it and typed in "*" program shows my file structure.
Command in cmd looks like this: program.exe 1 2 3 *
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const* argv[]) {
for (int i=0; i<argc; i++) printf("%s\n", argv[i]);
return 0;
}
The result is:
program
1
2
3
program.c
program.exe
10-03-20
11-02-20
And my question: Is it possible to force program to print "*" instead of listing files.
mingw causes the program to perform wildcard expansion of the parameters. Add the following to your program to disable this behaviour:
int _CRT_glob = 0;
In the unix world, the shell is expected to perform wildcard expansion.
$ perl -le'print for #ARGV' *
a
b
In the Windows world, wildcard expansion is left to the application.
>perl -le"print for #ARGV" *
*
That makes writing portable programs tricky. Since mingw is often used to compile programs that weren't written with Windows in mind, its C runtime library performs wildcard expansion of the parameters automatically.
a.c:
#include <stdio.h>
int main(int argc, char const* argv[]) {
for (int i=0; i<argc; i++)
printf("%s\n", argv[i]);
return 0;
}
>gcc -Wall -Wextra -pedantic-errors a.c -o a.exe & a *
a
a.c
a.exe
But, mingw provides an out. Adding the following to your program disables this behaviour:
int _CRT_glob = 0;
a.c:
#include <stdio.h>
int _CRT_glob = 0;
int main(int argc, char const* argv[]) {
for (int i=0; i<argc; i++)
printf("%s\n", argv[i]);
return 0;
}
>gcc -Wall -Wextra -pedantic-errors a.c -o a.exe & a *
a
*

Makefile in C (ubuntu) multiple definition

I am learning c and trying to build using makefile. I am stuck on the following error and don't know what to do next.
the build command is
gcc -o logfind logfind.o cmdargutils.o filesystem_utils.o file_utils.o strutils.o
If I need both file_utils.o and cmdargutils.o but if I add both I get the following error.
error screenshot
ERROR
file_utils.o:(.rodata+0x0): multiple definition of `MAX_LINE'
logfind.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status
Makefile:2: recipe for target 'logfind' failed
make: *** [logfind] Error 1
The source is:
Makefile
logfind: clean logfind.o
gcc -o logfind logfind.o cmdargutils.o filesystem_utils.o file_utils.o strutils.o
logfind.o: logfind.c cmdargutils.o file_utils.o filesystem_utils.o strutils.o error_codes.h
gcc -c logfind.c
cmdargutils.o: cmdargutils.c cmdargutils.h
gcc -c cmdargutils.c
file_utils.o: file_utils.c file_utils.h
gcc -c file_utils.c
filesystem_utils.o: filesystem_utils.c filesystem_utils.h
gcc -c filesystem_utils.c
strutils.o: strutils.c strutils.h
gcc -c strutils.c
clean:
rm -f *.o logfind
cmdargutils.h
#ifndef CMD_ARG_UTILS
#define CMD_ARG_UTILS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include "error_codes.h"
#include "strutils.h"
struct Argument {
bool is_and_operation;
int count;
char **search_terms;
};
struct Argument *argument_create(int argc, char **argv, int start, bool is_and_operation);
void argument_destroy(struct Argument *argument);
struct Argument *parse_arguments(int argc, char **argv);
#endif
error_codes.h
#ifndef ERROR_CODES
#define ERROR_CODES
enum error_codes {
MEMORY_ERROR,
INPUT_ERROR
};
#endif
file_utils.h
#ifndef FILE_UTILS
#define FILE_UTILS
#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "cmdargutils.h"
const size_t MAX_LINE = 1024;
bool is_match(char *, struct Argument *);
bool scan_file(char *, struct Argument *);
#endif
filesystem_utils.h
#ifndef FILESYSTEM_UTILS
#define FILESYSTEM_UTILS
#include <glob.h>
#include <string.h>
#include "strutils.h"
struct SearchFiles {
int count;
char **paths;
};
struct SearchFiles *search_files_create(int count, char** paths);
void search_files_destroy(struct SearchFiles *search_files);
struct SearchFiles *scan_directory(char *directory_path, char *pattern);
#endif
strutils.h
#ifndef STRUTILS
#define STRUTILS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "error_codes.h"
char *strdup(const char *source);
char **copy_string_array(char **source, int start, int end);
#endif
logfind.c
#include <stdio.h>
#include <stdlib.h>
#include <glob.h>
#include "cmdargutils.h"
#include "filesystem_utils.h"
#include "file_utils.h"
int main(int argc, char **argv) {
struct Argument *argument = parse_arguments(argc, argv);
int i = 0;
struct SearchFiles *search_files = scan_directory(".", "*.*");
for(i = 0; i < search_files->count; i++) {
scan_file(search_files->paths[i], argument);
}
search_files_destroy(search_files);
argument_destroy(argument);
return 0;
}
cmdargutils.c
#include "cmdargutils.h"
struct Argument *argument_create(int argc, char **argv, int start, bool is_and_operation){
struct Argument *argument = (struct Argument *)malloc(sizeof(struct Argument));
if(!argument) {
printf("Could not initialize arguments.\n");
exit(MEMORY_ERROR);
}
argument->count = argc - start;
argument->is_and_operation = is_and_operation;
argument->search_terms = copy_string_array(argv, start, argc);
return argument;
}
void argument_destroy(struct Argument *argument){
int i = 0;
for(i = 0; i < argument->count; i++) {
free(argument->search_terms[i]);
}
free(argument->search_terms);
free(argument);
argument = NULL;
}
struct Argument *parse_arguments(int argc, char **argv) {
struct Argument *argument = NULL;
bool is_and_operation = true;
int start = 0;
if(argc < 2) {
printf("Not enough arguments\n");
exit(INPUT_ERROR);
}
char *operation = argv[1];
if(strcmp(operation, "-o") == 0) {
is_and_operation = false;
if(argc < 3) {
printf("Not enough arguments\n");
exit(INPUT_ERROR);
}
}
start = is_and_operation ? 1 : 2;
argument = argument_create(argc, argv, start, is_and_operation);
return argument;
}
file_utils.c
#include "file_utils.h"
bool is_match(char *line, struct Argument *argument) {
int i = 0;
bool isMatch = false;
for(i = 0; i < argument->count; i++) {
char *found = strcasestr(line, argument->search_terms[i]);
if(!found) {
if(argument->is_and_operation) {
isMatch = false;
break;
} else {
continue;
}
} else {
isMatch = true;
if(argument->is_and_operation) {
continue;
} else {
break;
}
}
}
return isMatch;
}
bool scan_file(char *path, struct Argument *argument) {
FILE *file = fopen(path, "r");
int line_number = 0;
char *line = malloc(MAX_LINE);
while(fgets(line, MAX_LINE - 1, file)!= NULL) {
++line_number;
if(is_match(line, argument)) {
printf("%s:%d\n", path, line_number);
printf("\t%s\n", line);
}
}
free(line);
fclose(file);
}
filesystem_utils.c
#include "filesystem_utils.h"
struct SearchFiles *search_files_create(int count, char** paths) {
struct SearchFiles *search_files = (struct SearchFiles *)malloc(sizeof(struct SearchFiles));
search_files->count = count;
search_files->paths = copy_string_array(paths, 0, count);
return search_files;
}
void search_files_destroy(struct SearchFiles *search_files) {
int i = 0;
for(i = 0; i < search_files->count; i++) {
free(search_files->paths[i]);
}
free(search_files->paths);
free(search_files);
search_files = NULL;
}
struct SearchFiles *scan_directory(char *directory_path, char *pattern) {
glob_t globbuf;
int error = glob(pattern, GLOB_MARK, NULL, &globbuf);
if(!error) {
struct SearchFiles *search_files = search_files_create(globbuf.gl_pathc, globbuf.gl_pathv);
globfree(&globbuf);
return search_files;
}
return NULL;
}
strutils.c
#include "strutils.h"
char *strdup(const char *source) {
char *dest = malloc(strlen(source) + 1);
if(!dest) {
printf("Memory allocation error\n");
exit(MEMORY_ERROR);
}
strcpy(dest, source);
return dest;
}
char **copy_string_array(char **source, int start, int end) {
char **dest = (char **)malloc(sizeof(char *) * (end - start));
int di = 0;
int si = start;
for(di = 0, si = start; si < end;
si++, di++) {
dest[di] = strdup(source[si]);
}
return dest;
}
read documentation!
First, take a few hours to read documentation of GNU make, and read how to invoke GCC. You also need to understand more about the preprocessor, so read documentation of cpp. You want to take advantage of builtin GNU make rules (so run make -p to understand them) and variables. See also this answer. You could use remake (as remake -x) to debug your Makefile. You apparently don't understand how make and how gcc should be used, so you need to read more. Read also a C tutorial, look into some C reference, and glance when needed into the C11 standard n1570. Of course, read the documentation of every function you use (e.g. printf(3) etc..). For Linux system programming, read a book like ALP and relevant man pages from syscalls(2) and intro(3) etc...
Then read How to debug small programs. You certainly want to compile with all warnings and debug info.
a better Makefile
You might try something like:
# a better Makefile
# your C compiler
CC= gcc
# the verbose remove
RM= rm -vf
# your C compilation flags
CFLAGS= -Wall -Wextra -g
# your C source files
MY_CSOURCES= logfind.c cmdargutils.c filesystem_utils.c file_utils.c strutils.c
# the corresponding object files
MY_OBJECTS= $(patsubst %.c, %.o, $(MY_CSOURCES))
# the conventional phony targets
.PHONY: all clean
# the only program is for the default target all
all: logfind
logfind: $(MY_OBJECTS)
$(LINK.c) $< -o $#
# cleaning the mess
clean:
$(RM) logfind *.o *~
Of course, you need dependencies for object files on header files. You could compute them automatically, but it is simpler to explicit them, so add something like:
strutils.o: strutils.c strutils.h
and so on for each other object files.
BTW my HelloWorld/ directory on github is a tutorial example for using make
your multiple definition bug
You are getting multiple definition of MAX_LINE because it is defined in a header file included by several translation units, hence several translation units define it.
So either make it a preprocessor constant #define MAX_LINE 1024 in your header file_utils.h, or put there only a declaration like extern const int MAX_LINE; and define it only once in a single translation unit, as const int MAX_LINE=1024; in file_utils.c
general hints
I strongly recommend doing some iterative and incremental development: code only one or two dozen lines at once, then compile them, improve them to get no warnings, debug them with the GDB debugger and test them. At last repeat all this till satisfied. I do recommend using also a version control system (like git) even for school homework.
You might want to use valgrind to hunt memory leaks and other dynamic memory allocation bugs.
You could also use some static source analyzer like clang-analyzer or even Frama-C.
Once your program is debugged, you might add optimization flags like -O2 into your CFLAGS (in particular if you benchmark it with time(1)).
You could be interested by ntfw(3).

How can one implement "assert()" to make use of gcc's optimizer's static dataflow analysis?

Consider the following code....
#include <stdio.h>
void func( char * a, int i)
{
printf( "%c\n", a[i]);
}
int main( int argc __attribute__((unused)), char * argv[] __attribute__((unused)))
{
int i = 10;
char a[] = "abc";
func( a, i);
return 0;
}
Compile with..
gcc -W -Wall -Wextra -o b b.c
and no warnings.
Run it, it runs, but prints garbage.
Compile with
gcc -O3 -W -Wall -Wextra -o b b.c
And gcc correctly points out the error....
b.c: In function ‘main’:
b.c:5:21: warning: ‘*((void *)&a+10)’ is used uninitialized in this function [-Wuninitialized]
printf( "%c\n", a[i]);
^
b.c:11:9: note: ‘a’ was declared here
char a[] = "abc";
Hey! That's quite Smart of gcc, it analysed across the function boundary! (I have observed on large projects gcc is now astoundingly clever about this!)
Now counter intuitively, adding asserts make things worse!
Consider....
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
void func( char * a, int i)
{
assert( i < 4);
printf( "%c\n", a[i]);
}
int main( int argc __attribute__((unused)), char * argv[] __attribute__((unused)))
{
int i = 10;
char a[] = "abc";
func( a, i);
return 0;
}
Compiling without optimizations again produces no warnings, but at run time you, correctly....
a: a.c:7: func: Assertion `i < 4' failed.
Compilation aborted (core dumped) at Fri May 4 10:52:26
But compile with ...
gcc -O3 -W -Wall -Wextra -o a a.c
...now results in NO warnings!
ie. Although gcc knows the assert will trigger at run time... it can't tell me at compile time anymore.
ie. Counter intuitively, adding asserts and error checks to my code has made me less safe.
I can't help feel there must be some cunning Cthulhu inspired way of utilizing what gcc clearly knows, to fail the assertion at compile time!
Any suggestions?
Update: Here is a slightly different variant....
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
static char _rangeCheckVariable;
void bunc( int i)
{
if( __builtin_constant_p( (4))) {
char __rangeCheck[(4)]="abc";
_rangeCheckVariable = __rangeCheck[(i)];
} else {
assert( (i) < (4));
}
printf( "%d\n",i);
}
void func( int i)
{
bunc( i);
}
int main( int argc __attribute__((unused)), char * argv[] __attribute__((unused)))
{
int i = 10;
func( i);
return 0;
}
Compiling with
gcc -g3 -ggdb3 -O3 -W -Wall -Wextra -c d.c ;objdump -S d.o
Results in
d.c: In function ‘main’:
d.c:11:41: warning: array subscript is above array bounds [-Warray-bounds]
_rangeCheckVariable = __rangeCheck[(i)];
^
ie. You sort off, almost, could construct a check_range( i, size) macro that checked at compile time that i was less than size.
Update 2: Even weirder.... The following compiles without warnings, but.....
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
static char __rangeCheckVariable;
#define asshurt(exp) \
do { \
char __rangeCheck[2]="a"; \
__rangeCheckVariable = \
__rangeCheck[(exp) ? 0 : 10]; \
} while(0)
void bunc( int i)
{
asshurt( i< 4);
assert( i<4);
printf( "%d\n",i);
}
void func( int i)
{
bunc( i);
}
int main( int argc __attribute__((unused)), char * argv[] __attribute__((unused)))
{
int i = 10;
func( i);
return 0;
}
Removed the line assert( i< 4); and you get
gcc -g3 -ggdb3 -O3 -W -Wall -Wextra -c d.c ;objdump -S d.o
d.c: In function ‘main’:
d.c:11:22: warning: array subscript is above array bounds [-Warray-bounds]
__rangeCheck[(exp) ? 0 : 10]; \
^
d.c:17:4: note: in expansion of macro ‘asshurt’
asshurt( i< 4);
^
Update 3: Even weirder.
Have a play with this over at that most wonderful site Godbolt.... Try flicking between -Os and -O2 optimization settings.
#include <stdlib.h>
void assertFailure( void) __attribute__((warning("Compile time assertion failure")));
int z(void);
int main()
{
int j;
for( j=0;j<4;j++) {
if( z()) break;
}
if( __builtin_constant_p(!(j < 4)))
{
if(!(j < 4))
assertFailure();
}
else
if( !(j < 4))
abort();
return 0;
}
On -Os there are no warnings (correctly) on -O2 and above... it incorrectly assembles in a call to assertFailure.
Update 4: Variant that works for all optimization settings (but not for C++)
#include <stdlib.h>
#include <stdio.h>
void assertFailure( void) __attribute__((warning("Compile time assertion failure")));
static unsigned u;
int z(void) { return u++ % 2u;}
#define assert(exp) \
__builtin_choose_expr( __builtin_constant_p(!(exp)), \
((!(exp)) ? assertFailure() : (void)0), \
((__builtin_expect( !(exp), 0)) ? abort() : (void)0))
void bunc( char * a, unsigned j)
{
printf( "%c\n", a[j]);
// assert( j < 4);
}
void func( char * a)
{
bunc( a, 5);
}
int main()
{
int j;
char a[]="abc";
func( a);
for( j=0;j<4;j++) {
if( z()) break;
}
assert( j < 4);
assert( 4 < 5);
// assert( 5 < 4);
return 0;
}
You can play with it on godbolt An interesting difference is gcc-5.1 warnings seem better than 8.1!
Something like this will work in your example:
void assert_warn_abort (void)
__attribute__ ((warning ("assertion failure"), noreturn));
#define assert(expr) \
({ \
if (expr) \
(void)0; \
else \
{ \
if (__builtin_constant_p (expr)) \
assert_warn_abort (); \
else \
abort (); \
} \
})
However, warning on every statically known assertion failure can cause many spurious warnings with heavy inlining.

Realloc using extern

Hi im trying to figure out how to use realloc. I have made a small test project that im trying to malloc for the creation of the first element in main, followed by adding a number to that location then reallocating the array to hold one more element.
Main.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int i =0;
int *fileTest = (int*) malloc(sizeof(char * ));
int amtFiles=0;
for(i=0;i<3;i++){
parseInput(fileTest , amtFiles);
printf("%d", fileTest[1]);
}
}
parse.c
#include <stdlib.h>
#include "stddef.h";
#include <stdlib.h>
extern int parseInput(int *fileTest,int amtFiles){
printf("enter number");
scanf("%d",amtFiles);
fileTest = realloc(fileTest, (amtFiles * sizeof(char *)));
}
makefile
CC = gcc
CC_FLAGS = -g -ansi -pedantic -Wall -w
FILES = main.c parse.c
OUT_EXE = Test2
build: $(FILES)
$(CC) $(CC_FLAGS) -o $(OUT_EXE) $(FILES)
clean:
rm -f *.o core *.exe *~
rebuild: clean build
C is pass by value. You should return the pointer value. Otherwise the original one is unchanged. So from parseInput return the pointer or pass address of the variable and change it accordingly (not shown in example).
While reallocating use another pointer variable other than the original one, in case of failure you don't lose reference to the old memory.
Don't print uninitialized value. (in main()).
Don't cast the return value of malloc.
When printing a pointer do this printf("%p",(void*)fileTest[1]);.
Also you should put the extern declaration in main.c. You have passed incorrect number of arguments to the parseInput function. The code didn' compile.
Then after correcting the input - if you try then you might get segmentation fault or some similar error.
I have tried demonstrating something over here. The naming can be much more better. I have tried to keep the general aspects of what you have tried. Multiple source file with realloc used and tested, working with pointers etc.
main.c
#include <stdio.h>
#include <stdlib.h>
#include "myheader.h"
int main(void)
{
int *fileTest = NULL;
int amtFiles;
int weTried = 2;
while(weTried --> 0){
printf("%s\n", "Enter the array size.\n");
if( scanf("%d",&amtFiles) != 1){
fprintf(stderr, "%s\n","Error in input" );
exit(1);
}
else if( amtFiles <= 0){
fprintf(stderr, "%s\n", "Enter positive integral value");
exit(1);
}
fileTest = parseInput(fileTest, amtFiles);
if( fileTest == NULL){
fprintf(stderr, "%s\n","Realloc failed." );
exit(1);
}
for(int i = 0; i < amtFiles; i++){
printf("[%d]\n",fileTest[i]);
}
}
return 0;
}
parse.c
#include <stdlib.h>
#include <stdio.h>
#include "myheader.h"
int* parseInput(int *fileTest,int amtFiles)
{
if( amtFiles <= 0){
fprintf(stderr, "%s\n", "Error in number of inputs");
exit(1);
}
int *t = realloc(fileTest, (amtFiles * sizeof * t));
if( t == NULL ){
fprintf(stderr, "%s\n", "Execution exception");
return NULL;
}
fileTest = t;
for(int i = 0; i < amtFiles; i++){
fileTest[i] = 2017+i;
}
return fileTest;
}
myheader.h
extern int* parseInput(int *,int );
Compiled and run
gcc -Wall -Wextra -Werror main.c parse.c
./a.out

C program won't compile without warnings when GCC is called by makefile - Works otherwise

So I have a program here, that works perfectly when called with
$ gcc test.c -o test -std=c99
But when called in a makefile:
all: test
test: test.c
gcc test.c -o test -std=c99
it produces some warnings instead and gives a segmentation fault.
terminal output:
gcc -g test.c -o tester -std=c99
test.c: In function ‘test_split’:
test.c:43:2: warning: implicit declaration of function‘strdup[-Wimplicit-function-declaration]
char *str_cpy = strdup(str); // Allow mutation of original string
test.c:43:18: warning: initialization makes pointer from integer without a cast [enabled by default]
char *str_cpy = strdup(str); // Allow mutation of original string
Above error does not appear otherwise and does not produse a segmentation fault.
The code segment that fails is here. string.h is included in header.
The file is just a large file to test other functions.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#define CNRM "\x1b[0m"
#define CRED "\x1b[31m"
#define CGRN "\x1b[32m"
int stringsum(char *s);
void stringsum2(char *s, int *res);
int distance_between(char *s, char c);
char *string_between(char *s, char c);
char **split(char *s);
static int test_num = 1;
static void logger(int passed, char *s)
{
char *res;
char *color;
if (passed) {
res = "PASS";
color = CGRN;
} else {
res = "FAIL";
color = CRED;
}
printf("[Test %d][%s%s%s] %s\n", test_num++, color, res, CNRM, s);
}
static void test_split(char *str, char **correct)
{
int i, pass = 1;
char buf[512] = { 0 };
char *str_cpy = strdup(str); // Allow mutation of original string
char **res = split(str_cpy);
if (!res || !res[0]) {
pass = 0;
sprintf(buf, "split() returned NULL or an empty array");
goto end;
}
for (i = 0; correct[i]; i++) {
if (!res[i]) {
pass = 0;
sprintf(buf, "split() returned fewer words than expected");
goto end;
}
}
if (res[i]) {
pass = 0;
sprintf(buf, "split() returned more words than expected");
goto end;
}
sprintf(buf, "\n%-16s%-16s\n", "Returned", "Expected");
for (i = 0; res[i]; i++) {
char tmp[256] = { 0 };
sprintf(tmp, "%-16s%-16s\n", res[i], correct[i]);
strcat(buf, tmp);
if (strcmp(res[i], correct[i])) {
pass = 0;
goto end;
}
}
end:
logger(pass, buf);
free(str_cpy);
}
static void test_stringsum(char *input, int expected)
{
int test;
char buf[256] = { 0 };
test = stringsum(input);
sprintf(buf, "Returned: %d, Expected: %d", test, expected);
logger(test == expected, buf);
}
static void test_distance_between(char *str, char c, int expected)
{
int test;
char buf[256] = { 0 };
test = distance_between(str, c);
sprintf(buf, "Returned: %d, Expected: %d", test, expected);
logger(test == expected, buf);
}
static void test_string_between(char *str, char c, const char *expected)
{
char *res_char;
char buf[256] = { 0 };
res_char = string_between(str, c);
snprintf(buf, sizeof(buf), "Returned: %s, Expected: %s", res_char, expected);
if (!res_char && expected) {
logger(0, buf);
} else {
if (!expected)
logger(!res_char, buf);
else
logger(!strcmp(res_char, expected), buf);
free(res_char);
}
}
static void test_stringsum2(char *input, int expected)
{
int res_int;
char buf[256] = { 0 };
stringsum2(input, &res_int);
sprintf(buf, "Returned: %d, Expected: %d", res_int, expected);
logger(res_int == expected, buf);
}
int main(void)
{
printf("Testing stringsum()\n");
test_stringsum("abcd", 10);
test_stringsum("a!", -1);
test_stringsum("aAzZ", 54);
test_stringsum("ababcDcabcddAbcDaBcabcABCddabCddabcabcddABCabcDd", 120);
test_stringsum("", 0);
test_num = 1;
printf("\nTesting distance_between()\n");
test_distance_between("a1234a", 'a', 5);
test_distance_between("a1234", 'a', -1);
test_distance_between("123456a12334a123a", 'a', 6);
test_distance_between("", 'a', -1);
test_num = 1;
printf("\nTesting string_between()\n");
test_string_between("a1234a", 'a', "1234");
test_string_between("a1234", 'a', NULL);
test_string_between("A123adette er svaretaasd2qd3asd12", 'a', "dette er sv");
test_string_between("", 'a', NULL);
test_num = 1;
printf("\nTesting stringsum2()\n");
test_stringsum2("abcd", 10);
test_stringsum2("abcd!", -1);
test_stringsum2("bbbdbbbbbdbbdbbbbbddbbbbbdbbdbbbbdbd", 90);
test_stringsum2("", 0);
test_num = 1;
printf("\nTesting split()\n");
test_split("abcd", (char *[]){ "abcd", NULL });
test_split("Hei du", (char *[]){ "Hei", "du", NULL });
test_split("Dette er mange ord", (char *[]){ "Dette", "er", "mange", "ord", NULL });
return 0;
}
Any ideas?
Edit: Added full code.
strdup() is defined a bunch of standards (SVr4, 4.3BSD, POSIX.1-2001), but not the C standard. If you specify -std=c99, gcc by default disables functions defined in these standards. The warning about "implicit declaration" is because C has (annoying) legacy feature of declaring functions implicitly, if you just call them, with certain rules such as return type int, which in this case doesn't match your assignment to char*. These warnings are something you should always fix.
In this case, with -std=c99, it can be fixed by using feature test macros.
The linked manual page tells in which cases the function declaration will be included, so for example this will build it without problems:
gcc test.c -o test -std=c99 -D_POSIX_C_SOURCE=200809L
or with gcc you can use this, which enables pretty much all the features of the libc.
gcc test.c -o test -std=c99 -D_GNU_SOURCE
You could also add the define in the .c file as normal #define before you include the relevant system headers. It may be better to have it at the same place as the -std=c99 though (ie. command line option), because here it kinda goes together with that.
A more extreme solution would be to switch to -std=gnu99. But think carefully about doing that, because then it is easy to accidentally use language features which aren't standard C. And then you will run into much more porting trouble, than just writing a few functions, if you need to port the software to other C compiler which doesn't have GNU extensions.
If you ask gcc for C99 compliant code, it will do as you asked and forget it ever knew about strdup()- This function is not covered by the C99 standard, but is rather POSIX only, so strdup is nowhere in a header file in C99 mode.
As your code doesn't see the prototype for that reason, and doesn't know strdup () will return a pointer but rather assumes an integer, it might do all sorts of havoc when 32-/64- mixed mode code where (sizeof(int) != sizeof(char*)) is generated.
Simple remedy: Don't try to use strdup in c99 mode.
Why you seem to see the bug when compiling using make and not otherwise, is something we cannot deduct from the information you have provided. I guess for some reason you aren't switching gcc into c99 mode when using the command line.

Resources