Check locale language in C - c

I want to check if my system is zh_TW.UTF-8 or zh_CN.UTF-8.
Use the following code, I can tell that it is UTF-8.
But, how can I tell whether it is zh_TW or zh_CN?
#include <langinfo.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
//setlocale(LC_CTYPE,"");
setlocale(LC_ALL,"");
printf("%s\n",nl_langinfo(CODESET));
printf("%s\n",nl_langinfo(CRNCYSTR));
exit(EXIT_SUCCESS);
}

Try:
printf("%s\n", setlocale(LC_ALL, ""));
And, read this.

The function setlocale() returns a pointer to a string that contains the locale information. You can take a copy of it (but may not modify it, and subsequent calls to setlocale() may overwrite the previous value). The string can be used for setting the locale again in future.
char *loc_str = setlocale(LC_ALL, "");
if (loc_str == 0)
...failed to set locale...
printf("LC_ALL = %s\n", loc_str);
If you set one category, you get a string back that would allow you to reinstate that category. You could look at LC_COLLATE and see what is set:
char *loc_str = setlocale(LC_COLLATE, "");
if (loc_str == 0)
...failed to set locale...
printf("LC_COLLATE = %s\n", loc_str);
Etc.
It may or may not give you a human-readable value - but at least experiment to see what it in use.
You could also look at the LANG environment variable, or the LC_* environment variables.

On a POSIX-like system, there is a simple way: try the locale command:
system("locale");
or
FILE *handle = popen("locale", "r+");
size_t readn;
char buf[128];
while ((readn = fread(buf, 1, 128, handle)) > 0) {
fwrite(buf, 1, readn, stdout);
}
pclose(handle);

Related

Printing/writing wchar_t?

First off: I know there are similar topics for C++, but I am curious about standard C, and I don't believe my problem is related to previous problems.
I am trying to implement Unicode support for a simple program which just asks the user to select a directory through a folder browser and then passes it to another program (only got to the first part). But when attempting to write the received path to a file, it results in a 0-byte file. And when printing it out using wprintf_s, non-ASCII characters come out as question marks. I don't believe there is any undefined behavior or anything as I've double checked documentation. So what am I doing wrong?
The code currently looks like this (just the bare minimum for strict test conditions):
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <shlobj.h>
#include <stdio.h>
int main()
{
BROWSEINFOW bi = { 0 };
LPITEMIDLIST pidl;
wchar_t path[MAX_PATH];
pidl = SHBrowseForFolderW(&bi);
SHGetPathFromIDListW(pidl, path);
wprintf_s(L"%s\n", path);
return 0;
}
The above code prints it regularly. When attempting to write to a file instead, I replace the wprintf_s call with this (having declared FILE *f first of course):
if(_wfopen_s(&f, L"C:\\test.txt", L"w"))
{
fwprintf_s(f, L"%s\n", path)
fclose(f);
}
However, I have also tried using fwrite with both w and wb mode, but all methods results in an empty file.
You need _O_U16TEXT for console output, and "UTF-16LE" for file output.
Also, _wfopen_s returns zero when successful according to MS documentation:
Return Value Zero if successful; an error code on failure. See errno,
_doserrno, _sys_errlist, and _sys_nerr for more information about these error codes.
You should make sure return value is zero
if (0 == _wfopen_s(&f, filename, L"w, ccs=UTF-16LE")){
//isokay ...
}
or check if f is non-NULL. For example:
#include <stdio.h>
#include <io.h> //for _setmode
#include <fcntl.h> //for _O_U16TEXT
int main()
{
_setmode(_fileno(stdout), _O_U16TEXT);
const wchar_t *buf = L"ελληνική";
wprintf(L"%s\n", buf);
FILE *f = NULL;
_wfopen_s(&f, L"C:\\test\\test.txt", L"w, ccs=UTF-16LE");
if (f)
{
fwprintf_s(f, L"%s\n", buf);
fclose(f);
}
return 0;
}

built in command to change prompt in custom linux/unix shell

I have looked all over google and I find how to change in the bash config files, but my project requires a built in command to change the prompt.
I declared char pointer outside any function, my command modifies it, but when the function returns (int to continue a do while loop) and the prompt is displayed again, it is blank.
I have tried using a structure, union, and even a second char pointer and got the same issue.
I thought using a global char pointer that could be accessed and modified in any function would be the solution to this part of my project.
I would appreciate and will try any response.
Edit:
posted on my phone, tried to ask w/o code, but here it is
Code:
char *prmpt;
...
int main(int argc, char **argv)
prmpt="$$ ";
do
{
printf("%s ", prmpt);
}while(1)
int cmd_prompt(char **args)
{
prmpt = (char*)args[1];
return 1;
}
Essentially one needs to use fgets or getline or better yet they might use readline or editline. Here is an example using getline:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *prompt;
size_t len = 256;
size_t i = 0;
if (!(prompt = malloc(256 * sizeof(char))))
return 1;
strcpy(prompt, "$");
while ( 1 )
{
printf("[[%s]] ", prompt);
i = getline(&prompt, &len, stdin);
prompt[i-1] = '\0';
}
}
Or if you might try implementing getline on your own using getchar ( perhaps if getline is not available on your system, or you just want to know how it works). And eventually move on to writing your own editline library if you continue to be interested in writing shells.

Obtain the name of the current directory (not the path)

I had seen, and I had used a couple of time the function cwd() to get the absolute path of a folder, but there's a question, and that's if it's possible with C to get just the name of a folder.
For example, let's suppose that I execute a program on this folder:
/home/sealcuadrado/work1/include
If I don't give any arguments to my program, I will use cwd() and surely I will get the absolute path to that folder.
But what I want is just the name of the actual folder, in this case include. Can this be done in C (i had seen in in Python and C#)?
Apply the basename()
function to the result of getcwd().
An alternative is to mess around getting the inode number of the current directory (.) and then open and scan the parent directory (..) looking for the name with the corresponding inode number. That gets tricky if the parent directory contains NFS auto-mount points (home directories, for example); you can end up auto-mounting an awful lot of file systems if you aren't careful (which is slow and mostly pointless).
you can parse the result of getcwd()
Maybe not the most elegant way, but this should work by using a combination of strchr() and memmove().
#include <stdio.h>
#include <string.h>
int main() {
char *s;
char buf[] = "/home/folder/include";
s = strrchr (buf, '/');
if (s != NULL) {
memmove(s, s+1, strlen(s+1)+1);
printf ("%s\n", s);
}
return 0;
}
prints include
EDIT: The following code is better and also calls getcwd()
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
char buffer[200];
char *s;
getcwd(buffer,sizeof(buffer));
s = strrchr(buffer, '/');
if (s != NULL) {
printf ("%s\n", s+1);
}
return 0;
}

Getting and Setting the path

I've been giving the task of writing a simple shell, I've managed to get some really basic functionality however for one of the stages it's asking me to get and set the environment. This is the details of the task.
Stage 4: Getting and setting the path – 10 marks
Keep original path
The reason this is necessary is because we would like to restore the path to what was originally on exiting the shell. This is important because any changes you do to the shell’s environment (i.e. setenv() function above), does not only affect the shell itself but also any other program that will be executed from the same terminal afterwards. For this reason, it is a good idea to put things back the way they were on exit.
A single string will be enough to keep the original path.
Saving the path should be the first thing your shell does when it starts up.
Print and change the path – built-in commands
From a C program we can access the environment using the getenv() function and we can change the environment using the setenv() function. If you look at the manual pages for setenv(), you will find how it works (i.e. parameters needed and return values) as well as what you need to include to use it.
getpath – print system path & setpath – set system path
These two commands are about the environment parameter PATH. The first just gets and prints its value, while the second takes a path (string of colon separated list of directories) as a parameter and makes it the value of PATH. You can getenv() and setenv() respectively for this purpose.
Restore path
You just change the PATH environment parameter to its original value (i.e. the one you saved at the start up of the shell).
Restoring the path should the last thing your shell does before it exits.
Stage 4: Testing
First, make sure that all the tests you carried out for stage 3 still work. Be careful though, as we are now changing the path this will affect the execution of external programs.
To check the additional functionality, you should start by checking that the save and restore of the path work. A good idea here is to print the path when you save it at the beginning of the execution of the shell and then again when you exit at the end. In both cases the printed path should be exactly the same!
Following that you should check that when getpath is called you print the current path, which should be the same as the original one.
Then you should focus on testing setpath. First, setpath the path to a new value and test that getpath prints it, then try also to see how changing the path really affects the execution of external commands (e.g. set the path to only ‘.’ and try ‘ls’ or try the shell itself, etc).
This is my code:
/* S.NO:201148541 Simple Shell Example
Completed upto Stage 3 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#define BUFFER_SIZE 1<<16
#define ARR_SIZE 1<<16
void parse_args(char *buffer, char** args,
size_t args_size, size_t *nargs)
{
char *buf_args[args_size];
char **cp;
char *wbuf;
size_t i, j;
wbuf=buffer;
buf_args[0]=buffer;
args[0] =buffer;
for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
break;
}
for (j=i=0; buf_args[i]!=NULL; i++){
if(strlen(buf_args[i])>0)
args[j++]=buf_args[i];
}
*nargs=j;
args[j]=NULL;
}
int main(int argc, char *argv[], char *envp[]){
char buffer[BUFFER_SIZE];
char *args[ARR_SIZE];
int *ret_status;
size_t nargs;
pid_t pid;
char curDir[100];
while(1){
getcwd(curDir, 100);
printf("%s->", curDir);
fgets(buffer, BUFFER_SIZE, stdin);
parse_args(buffer, args, ARR_SIZE, &nargs);
if (nargs==0) continue;
if(strcmp(args[0], "cd") == 0){
chdir(args[1]);
}
else if (!strcmp(args[0], "exit" )) exit(0);
pid = fork();
if (pid){
pid = wait(ret_status);
printf("finished\n", pid);
}
else {
if( execvp(args[0], args)) {
puts(strerror(errno));
exit(127);
}
}
}
}
return 0;
}
I'm really at a loss and any guidance would be helpful.
Given that we don't know what your previous steps are, and going by the advice
Then you should focus on testing setpath. First, setpath the path to a
new value and test that getpath prints it, then try also to see how
changing the path really affects the execution of external commands
(e.g. set the path to only ‘.’ and try ‘ls’ or try the shell itself,
etc).
You can do like this...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *path, *old, *tobeSet;
path = malloc(1024);
path = getenv("PATH");
old = malloc(strlen(path));
tobeSet = malloc(10); // just to be safe
strcpy(tobeSet,".");
if(setenv("PATH",tobeSet,1)!=0)
{
printf("Couldn't set path\n");
return 0;
}
printf("\nPATH::\t%s\n",path);
printf("\n\nNewPath::\t%s\n",tobeSet);
if(setenv("PATH",path,1)!=0)
{
printf("Couldn't restore path\n");
return 0;
}
printf("\n\nOld path ::\t%s\n",path);
free(path);
free(old);
free(tobeSet);
return 0;
}

fopen in C on solaris

I've been trying to get this code to work for hours! All I need to do is open a file to see if it is real and readable. I'm new to C so I'm sure there is something stupid I'm missing. Here is the code (shorthand, but copied):
#include <stdio.h>
main() {
char fpath[200];
char file = "/test/file.this";
sprintf(fpath,"~cs4352/projects/proj0%s",file);
FILE *fp = fopen(fpath,"r");
if(fp==NULL) {
printf("There is no file on the server");
exit(1);
}
fclose(fp);
//do more stuff
}
I have also verified that the path is correctly specifying a real file that I have read permissions to. Any other ideas?
Edit 1: I do know that the fpath ends up as "~cs4352/projects/proj0/test/file.this"
Edit 2: I have also tried the using the absolute file path. In both cases, I can verify that the paths are properly built via ls.
Edit 3: There errno is 2... I'm currently trying to track what that means in google.
Edit 4: Ok, errno of 2 is "There is no such file or directory". I am getting this when the reference path in fopen is "/home/courses1/cs4352/projects/proj0/index.html" which I verified does exist and I have read rights to it. As for the C code listed below, there may be a few semantic/newbie errors in it, but gcc does not give me any compile time warnings, and the code works exactly as it should except that it says that it keeps spitting errno of 2. In other words, I know that all the strings/char array are working properly, but the only thing that could be an issue is the fopen() call.
Solution: Ok, the access() procedure is what helped me the most (and what i am still using as it is less code, not to mention the more elegant way of doing it). The problem actually came from something that I didn't explain to you all (because I didn't see it until I used access()). To derrive the file, I was splitting strings using strtok() and was only splitting on " \n", but because this is a UNIX system, I needed to add "\r" to it as well. Once I fixed that, everything fell into place, and I'm sure that the fopen() function would work as well, but I have not tested it.
Thank you all for your helpful suggestions, and especially to Paul Beckingham for finding this wonderful solution.
Cheers!
The "~" is expanded by the shell, and is not expanded by fopen.
To test the existence and readability of a file, consider using the POSIX.1 "access" function:
#include <unistd.h>
if (access ("/path/to/file", F_OK | R_OK) == 0)
{
// file exists and is readable
}
First, file needs to be declared as char* or const char*, not simply char as you've written. But this might just be a typo, the compiler should at least give a warning there.
Secondly, use an absolute path (or a path relative to the current directory), not shell syntax with ~. The substitution of ~cs4352 by the respective home directory is usually done by the shell, but you are directly opening the file. So you are trying to open a file in a ~cs4352 subdirectory of your current working directory, which I guess is not what you want.
Other people have probably produced the equivalent (every modern shell, for example), but here's some code that will expand a filename with ~ or ~user notation.
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <assert.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char *relfname(const char *name, char *buffer, size_t bufsiz)
{
assert(name != 0 && buffer != 0 && bufsiz != 0);
if (name[0] != '~')
strncpy(buffer, name, bufsiz);
else
{
const char *copy;
struct passwd *usr = 0;
if (name[1] == '/' || name[1] == '\0')
{
usr = getpwuid(getuid());
copy = &name[1];
}
else
{
char username[PATH_MAX];
copy = strchr(name, '/');
if (copy == 0)
copy = name + strlen(name);
strncpy(username, &name[1], copy - &name[1]);
username[copy - &name[1]] = '\0';
usr = getpwnam(username);
}
if (usr == 0)
return(0);
snprintf(buffer, bufsiz, "%s%s", usr->pw_dir, copy);
}
buffer[bufsiz-1] = '\0';
return buffer;
}
#ifdef TEST
static struct { const char *name; int result; } files[] =
{
{ "/etc/passwd", 1 },
{ "~/.profile", 1 },
{ "~root/.profile", 1 },
{ "~nonexistent/.profile", 0 },
};
#define DIM(x) (sizeof(x)/sizeof(*(x)))
int main(void)
{
int i;
int fail = 0;
for (i = 0; i < DIM(files); i++)
{
char buffer[PATH_MAX];
char *name = relfname(files[i].name, buffer, sizeof(buffer));
if (name == 0 && files[i].result != 0)
{
fail++;
printf("!! FAIL !! %s\n", files[i].name);
}
else if (name != 0 && files[i].result == 0)
{
fail++;
printf("!! FAIL !! %s --> %s (unexpectedly)\n", files[i].name, name);
}
else if (name == 0)
printf("** PASS ** %s (no match)\n", files[i].name);
else
printf("** PASS ** %s -> %s\n", files[i].name, name);
}
return((fail == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}
#endif
You could try examining errno for more information on why you're not getting a valid FILE*.
BTW-- in unix the global value errno is set by some library and system calls when they need to return more information than just "it didn't work". It is only guaranteed to be good immediately after the relevant call.
char file = "/test/file.this";
You probably want
char *file = "/test/file.this";
Are you sure you do not mean
~/cs4352/projects/proj0%s"
for your home directory?
To sum up:
Use char *file=/test/file.this";
Don't expect fopen() to do shell substitution on ~ because it won't. Use the full path or use a relative path and make sure the current directory is approrpriate.
error 2 means the file wasn't found. It wasn't found because of item #2 on this list.
For extra credit, using sprintf() like this to write into a buffer that's allocated on the stack is a dangerous habit. Look up and use snprintf(), at the very least.
As someone else here mentioned, using access() would be a better way to do what you're attempting here.

Resources