I just started working with C & Xcode and I've run into a little difficulty.
All I want to do is read a file from the command line and see the output in the terminal.
I think my problem lies with the path to the file that I want to read in. I'm using a Mac and the file is on my desktop, so the path should be Users/myName/Desktop/words.txt. Is this correct?
This is my code:
#import <Foundation/Foundation.h>
int main (int argc, const char* argv[]){
if(argc == 1){
NSLog(#" you must pass at least one arguement");
return 1;
}
NSLog(#"russ");
FILE* wordFile = fopen(argv[1] , "r");
char word[100];
while (fgets(word,100,wordFile)) {
NSLog(#" %s is %d chars long", word,strlen(word));
}
fclose(wordFile);
return 0;
}//main
If you need the path to a file in OS X, an easy way to get it is to just drag the file into the Terminal.app window where you are typing the command. Voila!
The path to the desktop is /Users/[username]/Desktop/
~/Desktop/ is a user-agnostic way of denoting this, ~ represents the current users home directory. It must be expanded using a method like stringByExpandingTildeInPath
Not sure about using C# (I've never used it on Mac OS X), but in Objective-C/Cocoa, you would do..
// Get array with first index being path to desktop
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
// Get the first element
NSString *desktopPath = [paths objectAtIndex:0];
// Append words.txt to path
NSString *theFilePath = [desktopPath stringByAppendingPathComponent:#"words.txt"];
NSLog(#"%#", theFilePath);
This is the most robust way of getting the Desktop path, as a user could technically move their Desktop folder else where (although this is pretty unlikely). Another valid option is to use the NSString method stringByExpandingTildeInPath:
NSString *desktop = [#"~/Desktop" stringByExpandingTildeInPath];
NSString *theFile = [desktop stringByAppendingPathComponent:#"words.txt"]
As I said, both those are in Objective-C, but it shouldn't be hard to translate to C#, assuming you can get at the Cocoa libraries.
The code you posted works correctly:
dbr:.../build/Debug $ ./yourcode ~/Desktop/words.txt
yourcode[2106:903] russ
yourcode[2106:903] this is words.txt is 17 chars long
Your terminal automatically expands the ~/ tilda path
Close... it's
/{Volume}/Users/myName/Desktop/words.txt
... where {Volume} is the name of your hard drive. You can also try using:
~/Desktop/words.txt
... where ~ is understood to mean "your home directory", but this might not resolve correctly.
(Note - this appears to be a C question, not a C# question)
Actually, you can do:
/Users/myName/Desktop/words.txt
You don't have to give the path to the volume.
However, to get the full path in C you'd do something like:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char *home, *fullPath;
home = getenv("HOME");
fullPath = strcat(home, "/Desktop/words.txt");
The issue you're running into with passing the filename as an argument is that you need to set the current working directory to the place where the file exists.
Related
How can I get file path to Desktop directory as a string on macOS.
I need it to be done in pure C or with some C-level framework.
If you insist on using only C (why?), then your only choice is to use deprecated APIs:
#include <limits.h>
#include <CoreServices/CoreServices.h>
...
FSRef fsref;
UInt8 path[PATH_MAX];
if (FSFindFolder(kUserDomain, kDesktopFolderType, kDontCreateFolder, &fsref) == noErr &&
FSRefMakePath(&fsref, path, sizeof(path)) == noErr)
{
// Make use of path
}
If you need a CFURL rather than a path, you can use CFURLCreateFromFSRef() rather than FSRefMakePath().
Actually, while researching this, I found an API I hadn't known about. Apparently, you can use this, which apparently comes from Cocoa but uses only C types:
#include <limits.h>
#include <NSSystemDirectories.h>
char path[PATH_MAX];
NSSearchPathEnumerationState state = NSStartSearchPathEnumeration(NSDesktopDirectory, NSUserDomainMask);
while (state = NSGetNextSearchPathEnumeration(state, path))
{
// Handle path
}
The form of the API is that it may return multiple results (one on each iteration of the loop), but you should get only one for the specific use here. In that case, you can change the while to and if.
Note that, with this API, returned paths for directories in the user domain may use "~" rather than the absolute path to the user's home directory. You'll have to resolve that yourself.
Here's a short function, which works on more Unix based systems than just macOS and returns the current user's desktop folder:
#include <limits.h>
#include <stdlib.h>
/**
* Returns the path to the current user's desktop.
*/
char *path2desktop(void) {
static char real_public_path[PATH_MAX + 1] = {0};
if (real_public_path[0])
return real_public_path;
strcpy(real_public_path, getenv("HOME"));
memcpy(real_public_path + strlen(real_public_path), "/Desktop", 8);
return real_public_path;
}
The path will only be computed once.
If the function is called more than once, the old result will be returned (not thread-safe, unless the first call was protected).
I ended with usage of Objective-C in such way:
//
// main.m
// search_path_for_dir
//
// Created by Michal Ziobro on 23/09/2016.
// Copyright © 2016 Michal Ziobro. All rights reserved.
//
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
if(argc != 3)
return 1;
#autoreleasepool {
NSArray *paths = NSSearchPathForDirectoriesInDomains(atoi(argv[1]), atoi(argv[2]), YES);
NSString *path = [paths objectAtIndex:0];
[path writeToFile:#"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil];
}
return 0;
}
And than from command line I can execute this program in that way:
./search_path_for_dir 12 1
12 - NSDesktopDirectory
1 - NSUserDomainMask
I am using script in C that executes this program from command line and retrieves its output.
Here's C example calling this mini Cocoa App:
CFStringRef FSGetFilePath(int directory, int domainMask) {
CFStringRef scheme = CFSTR("file:///");
CFStringRef absolutePath = FSGetAbsolutePath(directory, domainMask);
CFMutableStringRef filePath = CFStringCreateMutable(NULL, 0);
if (filePath) {
CFStringAppend(filePath, scheme);
CFStringAppend(filePath, absolutePath);
}
CFRelease(scheme);
CFRelease(absolutePath);
return filePath;
}
CFStringRef FSGetAbsolutePath(int directory, int domainMask) {
char path_cmd[BUF_SIZE];
sprintf(path_cmd, "./tools/search_path_for_dir %d %d", directory, domainMask);
char *path = exec_cmd(path_cmd);
return CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
}
I have researched a lot on this topic but could not get anything substantial.
By normalize/canonicalize I mean to remove all the "..", ".", multiple slashes etc from a file path and get a simple absolute path.
e.g.
"/rootdir/dir1/dir2/dir3/../././././dir4//////////" to
"/rootdir/dir1/dir2/dir4"
On windows I have GetFullPathName() and I can get the canonical filepath name, but for Linux I cannot find any such API which can do the same work for me,
realpath() is there, but even realpath() needs the filepath to be present on the file system to be able to output normalized path, e.g. if the path /rootdir/dir1/dir2/dir4 is not on file system - realpath() will throw error on the above specified complex filepath input.
Is there any way by which one could get the normalized file path even if it is not existing on the file system?
realpath(3) does not resolve missing filenames.
But GNU core utilities (https://www.gnu.org/software/coreutils/) have a program realpath(1) which is similar to realpath(3) function, but have option:
-m, --canonicalize-missing no components of the path need exist
And your task can be done by canonicalize_filename_mode() function from file lib/canonicalize.c of the coreutils source.
canonicalize_filename_mode() from Gnulib is a great option but cannot be used in commercial software (GPL License)
We use the following implementation that depends on cwalk library:
#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include "cwalk.h"
/* extended version of canonicalize_file_name(3) that can handle non existing paths*/
static char *canonicalize_file_name_missing(const char *path) {
char *resolved_path = canonicalize_file_name(path);
if (resolved_path != NULL) {
return resolved_path;
}
/* handle missing files*/
char *cwd = get_current_dir_name();
if (cwd == NULL) {
/* cannot detect current working directory */
return NULL;
}
size_t resolved_path_len = cwk_path_get_absolute(cwd, path, NULL, 0);
if (resolved_path_len == 0) {
return NULL;
}
resolved_path = malloc(resolved_path_len + 1);
cwk_path_get_absolute(cwd, path, resolved_path, resolved_path_len + 1);
free(cwd);
return resolved_path;
}
I want to set a path environment variable in bash by C program.
So I coded using 'setenv' function, But it was not the answer to solve.
Could anybody suggest another way to solve this problem in C programming?
I thought the other solution that the program read the profile file, then modify and save, but actually when I opened this file there's no string I wanted about PATH variable.
You can use setenv() and putenv() to set environment variables. But these will only be set for the given program. You cannot set environment variables for the shell or its parent process.
Here an example to define the Python path.
It is created a string path and append it to the python path.
char *append_path = malloc(sizeof(char) * 1000);
append_path = "/trunk/software/hmac_sha256/:.";
printf("Append to path is:\n%s\n", append_path);
setenv("PYTHONPATH",append_path,1);//Set PYTHONPATH TO working directory https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.bpxbd00/setenv.htm
char *path = Py_GetPath();
printf("Python search path is:\n%s\n", path);
This should append the string to the PYTHONPATH environment variable. For me it is working as stated before.
In case the variable is replaced and not appended, then you just need to read the environment variable before, append the new string and then do the "setenv".
for example
//include string functions
#include <string.h>
....
char *current_path = malloc(sizeof(char) * 1000);
current_path = Py_GetPath();
printf("Current search path is:\n%s\n", current_path);
char *new_path = malloc(sizeof(char) * 1000);
new_path = "/trunk/software/hmac_sha256/:.";
printf("New to add path is:\n%s\n", new_path);
snprintf(current_path, sizeof(char) * 1000, "%s%s", current_path,new_path);//this concatenate both strings
setenv("PYTHONPATH",current_path,1);//Set PYTHONPATH TO working directory https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.bpxbd00/setenv.htm
char *path = Py_GetPath();
printf("Python search path is:\n%s\n", path);
I have the following Config.cfg
[DD]
user=**
password=***
database=***
IPServidor=****
port=***
[Controller]
Control1=8
Temp=5
Hum=7
Link=8
Volt=9
[Controller]
Control2=10
Temp=5
Hum=7
Link=8
Volt=9
I would like to read the values of the controllers only and print them to the screen like
Controller_8: 5,7,8,9
I do not want to use libconfig or glib because I have problem with undefined functions. I did the installation, I have the headers but I do not know why it does not work. So I want another solution. My first thought is with the usage of strchr to find the lines which I want (to ignore [DD] table in my case) and with the usage of strtok to get only the values of temp,hum,link,volt
char buffer1[100];
FILE *f = fopen("/home/pi/Desktop/Config.cfg","r");
while(fgets(buffer1, sizeof(buffer1), f))
{
printf("%s",buffer1);
char *pos1 = strchr(buffer1,'Controller');
if (pos1)
{
item = strtok (buffer1,"Control");
printf("Results: %s\n", buffer1);
}
}
The above code is not correct. Is just a thought. Is there any better way?
Don't try parsing ini files, use some existing library.
Ini file parsing is included in a number of "frameworks", for instance in Gtk+ or on Windows. If you can't access those, you can still use some standalone library, for instance: http://ndevilla.free.fr/iniparser/
I am trying to save some files using C, with this code:
sprintf(playerinput,"%s",end);
sprintf(fileloc,"%s/.notend",getenv("HOME"));
playerdata = fopen(fileloc, "w+"); /*create the new file*/
if (!playerdata)
{
printf("\n\t\t\tCould not save settings file.");
return;
} else {
fputs(playerinput,playerdata);
fclose(playerdata);
}
It should set playerinput to the end variable, which works on Linux, then set the file location to the homearea/.notend, then make or edit the file, and put it out. In Linux (gcc), this works fine. The same code, however, on Windows (i586-mingw32msvc-gcc, does not work. Am I doing something wrong, or is another header file needed? Currently I have:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_NOTES 200
#define MAX_NAMES_TEXT 200
#define MAX_NOTES_TEXT 2000
as my headers and defines. If you need more information, just ask.
To which location are you trying to write your file? Does the application have the right permissions to write to that location?
EDIT: Looking at the path style you just defined C://Documents and Settings//..., you should try it with C:\\\\Documents and Settings\\.... Note that there's double backslash for each slash in the path. I'm not sure if fopen() would convert / to \, so it's worth a try.
If you're sure that this would be running on Windows Vista and above you can get this path using getenv("HOMEPATH"). I would suggest a macro definition like:
#ifdef _WIN32
# define HOME_ENV "HOMEPATH"
#else
# define HOME_ENV "HOME"
#endif
followed by:
getenv(HOME_ENV) to get the home directory for the user.
The environment variable HOME is not a default environment variable on Windows so:
getenv("HOME");
will return NULL. You need to use a different function to obtain a user's home directory on Windows, SHGetFolderPath will provide this:
char path[MAX_PATH + 1] = "";
if (SUCCEEDED(SHGetFolderPath(0,
CSIDL_LOCAL_APPDATA,
0,
SHGFP_TYPE_CURRENT,
path)))
{
std::cout << path << "\n";
}
This output:
C:\Documents and Settings\admin\Local Settings\Application Data
on my machine.