Why is © (the copyright symbol) replaced with (C) when using wprintf? - c

When I try to print the copyright symbol © with printf or write, it works just fine:
#include <stdio.h>
int main(void)
{
printf("©\n");
}
#include <unistd.h>
int main(void)
{
write(1, "©\n", 3);
}
Output:
©
But when I try to print it with wprintf, I get (C):
#include <stdio.h>
#include <wchar.h>
int main(void)
{
wprintf(L"©\n");
}
Output:
(C)
It's fixed when I add a call to setlocale, though:
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main(void)
{
setlocale(LC_ALL, "");
wprintf(L"©\n");
}
Output:
©
Why is the original behavior present and why is it fixed when I call setlocale? Additionally, where does this conversion take place? And how can I make the behavior after setlocale the default?
compilation command:
gcc test.c
locale:
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
echo $LC_CTYPE:
uname -a:
Linux penguin 4.19.79-07511-ge32b3719f26b #1 SMP PREEMPT Mon Nov 18 17:41:41 PST 2019 x86_64 GNU/Linux
file test.c (same on all of the examples):
test.c: C source, UTF-8 Unicode text
gcc --version:
gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
/lib/x86_64-linux-gnu/libc-2.24.so (glibc version):
GNU C Library (Debian GLIBC 2.24-11+deb9u4) stable release version 2.24, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 6.3.0 20170516.
Available extensions:
crypt add-on version 2.1 by Michael Glad and others
GNU Libidn by Simon Josefsson
Native POSIX Threads Library by Ulrich Drepper et al
BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.debian.org/Bugs/>.
cat /etc/debian_version:
9.12

The locale of the calling processes is not automatically inherited by the new process.
When the program first starts up, it is in the C locale. The man page for setlocale(3) says the following:
On startup of the main program, the portable "C" locale is selected
as default. A program may be made portable to all locales by calling:
setlocale(LC_ALL, "");
...
The locale "C" or "POSIX" is a portable locale; its LC_CTYPE part corresponds to the 7-bit ASCII character set.
So any multibyte / non-ASCII character is converted into one or more ASCII characters as the output shows.
The locale can be set as follows:
setlocale(LC_ALL, "");
The LC_ALL flag specifies changing all locale-related variables. An empty string for the locale means to set the locale according to the relevant environment variables. Once this is done, you should see the characters for your shell's locale.
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main()
{
char *before = setlocale(LC_ALL, NULL);
setlocale(LC_ALL, "");
char *after = setlocale(LC_ALL, NULL);
wprintf(L"before locale: %s\n", before);
wprintf(L"after locale: %s\n", after);
wprintf(L"©\n");
wprintf(L"\u00A9\n");
return 0;
}
Output:
before locale: C
after locale: en_US.utf8
©
©

Related

C program: getenv return NULL in GDB/DDD

I try to call getenv in my C code, this can return correct env string in terminal, while it returns NULL in GDB/DDD.
void main() {
char * a = getenv("ANCHOR_STEM");
if (strlen(a)>0)
printf("%s\n", a);
}
The GDB/DDD is started from the same terminal.
Even I "show environment", this env exists.
Anyone any idea?
OS/Tools version info:
RHEL Linux hostname1 2.6.32-754.3.5.el6.x86_64 #1 SMP Thu Aug 9 11:56:22 EDT 2018 x86_64 GNU/Linux
GNU gdb (GDB) 7.12
gcc (GCC) 6.3.0
Even I "show environment", this env exists.
When GDB invokes your program, it starts a new shell to run this program in.
When the environment changes for the target program, most often this is the result of your shell initialization file (~/.bashrc, ~/.kshrc, etc.) changing the environment.
It is a really bad idea to change environment for non-interactive shells. Documentation on how to avoid it.
include proper header files
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
Treat warning as error.
If getenv returns NULL it is because it does not find the environment variable. but still you have to be careful. This instruction if (strlen(a)>0) becomes illegal, if getenv(....), fails. getenv sends back NULL and this instruction crash the program. It's better to test the validity of the pointer before to execute this line if (strlen(a)>0) -> which is not necessarily useful.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char * a = getenv("ANCHOR_STEM");
if( NULL != a ){
(void)puts(a);
return EXIT_SUCCESS;
}
(void)puts("none");
return EXIT_FAILURE;
}

Using printk to print data on Linux terminal

I currently started learning the Linux Device driver programming in Linux. where I found this small piece of code printing hello world using printk() function.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "Hello World!!!\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye Hello World!!!\n");
}
module_init(hello_init);
module_exit(hello_exit);
After compiling code using make command and load driver using insmod command. I'm not getting the "Hello world" printed on screen instead its printing only on the log file /var/log/kern.log. But I want printk to print on my ubuntu terminal. I'm using ubuntu(14.04). Is it possible?
It isn't possible to redirect kernel logs and massages to gnome-terminal and there you have to use dmesg.
But in a virtual terminal(open one with ctrl+F1-F6) you can redirect them to standard output.
First determine tty number by entering tty command in virtual terminal.The output may be /dev/tty(1-6).
Compile and run this code with the argument you specified.
/*
* setconsole.c -- choose a console to receive kernel messages
*
* Copyright (C) 1998,2000,2001 Alessandro Rubini
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(int argc, char **argv)
{
char bytes[2] = {11,0}; /* 11 is the TIOCLINUX cmd number */
if (argc==2) bytes[1] = atoi(argv[1]); /* the chosen console */
else {
fprintf(stderr, "%s: need a single arg\n",argv[0]); exit(1);
}
if (ioctl(STDIN_FILENO, TIOCLINUX, bytes)<0) { /* use stdin */
fprintf(stderr,"%s: ioctl(stdin, TIOCLINUX): %s\n",
argv[0], strerror(errno));
exit(1);
}
exit(0);
}
For example if your output for tty command was /dev/tty1then type this two command:
gcc setconsole.c -o setconsole
sudo ./setconsole 1
This will set your tty to receive kernel messages.
Then compile and run this code.
/*
* setlevel.c -- choose a console_loglevel for the kernel
*
* Copyright (C) 1998,2000,2001 Alessandro Rubini
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/klog.h>
int main(int argc, char **argv)
{
int level;
if (argc==2) {
level = atoi(argv[1]); /* the chosen console */
} else {
fprintf(stderr, "%s: need a single arg\n",argv[0]); exit(1);
}
if (klogctl(8,NULL,level) < 0) {
fprintf(stderr,"%s: syslog(setlevel): %s\n",
argv[0],strerror(errno));
exit(1);
}
exit(0);
}
There are 8 level of kernel messages as you specify in your code KERN_ALERT is one of them.To make console receive all of them you should run above code with 8 as arguement.
gcc setlevel.c -o setlevel
sudo ./setlevel 8
Now you can insert your module to kernel and see kernel logs in console.
By the way these codes are from ldd3 examples.
printk prints to the kernel log. There is no "screen" as far as the kernel is concerned. If you want to see the output of printk in real time, you can open a terminal and type the following dmesg -w. Note that the -w flag is only supported by recent versions of dmesg (which is provided by the util-linux package).

using ‘-Wcast-qual’ option to check the compilation

I am reading an introduction to gcc in which it says:
‘-Wcast-qual’ this option warns about pointers that are cast to remove
a type qualifier, such as const. For example, the following function
discards the const qualifier from its input argument, allowing it to
be overwritten:
void
f (const char * str)
{
char * s = (char *)str;
s[0] = ’\0’; }
The modification of the original contents of str is a violation of its
const property. This option will warn about the improper cast of the
variable str which allows the string to be modified.
I tried to repeat this, expecting a warning, but there's no warning when it is compiled. My code is as follows:
#include <stdio.h>
void f(const char * str);
int main() {
char Str = 'a';
char * myStr;
myStr = & Str;
printf ("result: %c \n", * myStr);
f(myStr);
printf ("result: %c \n", * myStr);
return 0;
}
void
f (const char * str)
{
char * s = (char *)str;
s[0] = '\0';
}
the command is : gcc -Wcast-qual castqual.c
Can anyone explain this inconsistency?
Per the comments, it turns out that the OP is using a mac, where gcc as a command is a wrapper around clang - the llvm based compiler.
The version of clang that's being used accepts the option but does nothing with it. Support for the warning is being added and should appear in the XCode 7/clang 7 timeframe; however the question was about gcc and not about clang.
In this case, you need to install a proper copy of gcc using, either manually or using a package manager such as homebrew or macports. Once you've installed gcc, it becomes available as, for example, gcc-4.8, gcc-5, etc (depends on the package manager). When you want to compile code with actual gcc, you use: gcc-4.8 -Wcast-qual testcode.c. If you're using autoconf tools you can use export CC=gcc-4.8; export CXX=g++-4.8, etc. and then that compiler will be picked up by the package.
Verifying that you're calling gcc, rather than clang requires checking the output from gcc --version. On a clang wrapper you see things like:
$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.4.0
Thread model: posix
whereas on an actual gcc instance you see:
$ gcc-5 --version
gcc-5 (Homebrew gcc 5.1.0) 5.1.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
imho aside: you can make a symlink to the specific gcc-4.8 called gcc, and get it to invoke directly like that but it can break other things.

gdb unexpected behavior: in nested if

I am using
p12user#ubuntu:~$ uname -a
Linux ubuntu 2.6.32-40-generic #87-Ubuntu SMP Tue Mar 6 00:56:56 UTC 2012 x86_64 GNU/Linux
p12user#ubuntu:~$ gdb -v
GNU gdb (GDB) 7.1-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
p12user#ubuntu:~/programming$ gcc --version
gcc (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
On debugging this program in gdb, which is compiled as gcc -g program.c:
#include <stdio.h>
int main()
{
int condition1 = 1;
if(condition1)
{
if(!condition1)
{
printf("The control shouldn't come here.\n");
}
}
else
{
printf("in else\n");
}
return 0;
}
the behaviour is:
Breakpoint 1, main () at program.c:4
4 int condition1 = 1;
(gdb) n
5 if(condition1)
(gdb) n
7 if(!condition1)
(gdb) n
9 printf("The control shouldn't come here.\n");
(gdb) n
16 return 0;
(gdb) n
17 }
(gdb)
The behaviour at LINE 9 is unexpected in gdb. However, the print statement is only shown in gdb but not executed. And if I put a matching ELSE (with some print statement) with inner-IF then this doesn't happen, also if matching ELSE for outer-IF is removed then this doesn't happen. Am I missing something trivial here?
Make sure you're building your program without any kinds of optimization, as the program you've shown seems to be trivially optimizable which can lead to source code statements not making into the executable.
In GCC you'd use:
gcc -O0 ...
or
g++ -O0 ...
for this.
in
2.6.18-164.el5 #1 SMP Tue Aug 18 15:51:48 EDT 2009 x86_64 x86_64 x86_64 GNU/Linux
gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)
GNU gdb Fedora (6.8-37.el5)
it is ok!

Getting absolute path of a file

How can I convert a relative path to an absolute path in C on Unix?
Is there a convenient system function for this?
On Windows there is a GetFullPathName function that does the job, but I didn't find something similar on Unix...
Use realpath().
The realpath() function shall derive,
from the pathname pointed to by
file_name, an absolute pathname that
names the same file, whose resolution
does not involve '.', '..', or
symbolic links. The generated pathname
shall be stored as a null-terminated
string, up to a maximum of {PATH_MAX}
bytes, in the buffer pointed to by
resolved_name.
If resolved_name is a null pointer,
the behavior of realpath() is
implementation-defined.
The following example generates an
absolute pathname for the file
identified by the symlinkpath
argument. The generated pathname is
stored in the actualpath array.
#include <stdlib.h>
...
char *symlinkpath = "/tmp/symlink/file";
char actualpath [PATH_MAX+1];
char *ptr;
ptr = realpath(symlinkpath, actualpath);
Try realpath() in stdlib.h
char filename[] = "../../../../data/000000.jpg";
char* path = realpath(filename, NULL);
if(path == NULL){
printf("cannot find file with name[%s]\n", filename);
} else{
printf("path[%s]\n", path);
free(path);
}
There is also a small path library cwalk which works cross-platform. It has cwk_path_get_absolute to do that:
#include <cwalk.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char buffer[FILENAME_MAX];
cwk_path_get_absolute("/hello/there", "./world", buffer, sizeof(buffer));
printf("The absolute path is: %s", buffer);
return EXIT_SUCCESS;
}
Outputs:
The absolute path is: /hello/there/world
Also try "getcwd"
#include <unistd.h>
char cwd[100000];
getcwd(cwd, sizeof(cwd));
std::cout << "Absolute path: "<< cwd << "/" << __FILE__ << std::endl;
Result:
Absolute path: /media/setivolkylany/WorkDisk/Programming/Sources/MichailFlenov/main.cpp
Testing environment:
setivolkylany#localhost$/ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 8.6 (jessie)
Release: 8.6
Codename: jessie
setivolkylany#localhost$/ uname -a
Linux localhost 3.16.0-4-amd64 #1 SMP Debian 3.16.36-1+deb8u2 (2016-10-19) x86_64 GNU/Linux
setivolkylany#localhost$/ g++ --version
g++ (Debian 4.9.2-10) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Resources