I am trying to write a tiny program in C that will open a file and then run the filename in terminal to stream the file to my Apple TV.
The reason I want to do this is so I can right click a media file, select 'Open With', choose 'Apple TV' from the list and then have it stream to my Apple TV via the airstream program.
My code so far is as follows
#include <stdio.h>
#include <string.h>
int main ()
{
char command[50];
strcpy( command, "airstream '/home/steve/media.mp4' -o 192.168.0.2" );
system(command);
return(0);
}
Very simple, but I'm not sure how to handle a file being passed to the application to allow it to get the filename and modify the command.
(First, let me make a comment on strcpy(): as-is, the call to strcpy() is superfluous (and imposes a security issue), because you are using a constant string. You could have written system("airstream '/home/steve/media.mp4' -o 192.168.0.2") instead.)
If you want to construct a command given a filename, you could in theory write
char command[LINE_MAX];
snprintf(command, sizeof command, "some_command %s", argv[1]);
system(command);
But that again rises a security problem, because now your program can be hijacked to execute an arbitrary external program, by passing it a malformed command-line argument, similarly to an SQL injection attack.
You'd be better off finding the library/API the airstream executable uses, and incorporate that directly into your program. If no such thing exists, you have to make sure to at least validate the user input (i. e. escape special characters, etc.) before handing it over to the shell for execution.
You need to use the arguments passed to the main() function. Change the function's signature to:
int main(int argc, char *argv[])
Then loop over the string pointers in argv[], which will be the command-line arguments your program was given.
Related
I have a display file for a menu. I successfully made a working CL program which displays the menu and waits for input. Simply enough, all it does is display the file and waits for the user to press F1 to quit.
Display file (approximation):
A R DISPLAY
A CA01(01 'Exit')
A 2 2'some text....'
Creation command: crtdspf file(display) srcfile(test) srcmbr(display)
CL program:
PGM
DCLF FILE(DISPLAY) RCDFMT(DISPLAY)
LOOP: SNDRCVF
IF COND(&IN01 *EQ '1') THEN(DO)
GOTO END
ENDDO
GOTO LOOP
END:
ENDPGM
Compile command: crtclpgm pgm(test) srcfile(test) srcmbr(clsrc) output(*print)
What is the equivalent of SNDRCVF in C?
Here is what I've come with so far. It compiles fine, but when I call it, it does nothing.
#include <stdio.h>
#include <recio.h>
// Include my display file.
#pragma mapinc("display","lib/display(display)", "both", "")
#include "display"
// Shortcuts to the generated structs.
typedef LIB_DISPLAY_DISPLAY_i_t input_t;
typedef LIB_DISPLAY_DISPLAY_o_t output_t;
int main(int argc, char* argv[]){
input_t input;
output_t output;
_RFILE* dspf;
// The file opens fine.
dspf = _Ropen("lib/display", "wr");
if(dspf == NULL){
printf("ERROR: Display file open failed.\n");
return 0;
}
// I tell which record format to use.
_Rformat(dspf, "display");
// Write my file to display?
_Rwrite(dspf, "", 0);
// Wait for input.
_Rreadn(dspf, &input, sizeof(input), __DFT);
// Close it and quit.
_Rclose(dspf);
return 0;
}
Compile command: crtbndc pgm(test) srcfile(test) srcmbr(main) output(*print)
Then call: call test
What am I doing wrong?
I made a few minor changes. First, for your TYPEDEFs, I used this:
// Shortcuts to the generated structs.
typedef MYLIB_CDSPMNU_DISPLAY_both_t input_t;
typedef MYLIB_CDSPMNU_DISPLAY_both_t output_t;
Because you specified "both", the referenced identifier name should have 'both' rather than 'i' or 'o'. It's not clear how you could've successfully compiled as you had it. Perhaps you had an earlier successful compilation so that your CALL command worked, but the compiled program wasn't a current version.
Then I opened the file with this mode:
// The file opens fine.
dspf = _Ropen("mylib/cdspmnu", "rr+");
You had "wr", so it it was opened only for output ("wr"iting). You need it for input and output. Your joblog should show a C2M3005 "File is not opened for read operations." after you CALL your program (depending on what compiled version you actually CALL).
And I changed your _Rformat() function:
// I tell which record format to use.
_Rformat(dspf, "DISPLAY");
From the ILE C/C++ Runtime Library Functions manual, the definition of _Rformat() says:
The fmt parameter is a null-ended C string. The fmt parameter must
be in uppercase.
The format name isn't folded to uppercase like file and library names are in other places. No idea why not; it's just the way it is. Personally, I'd use uppercase wherever it actually means an uppercase name without relying on the compiler; so I'd also change a couple other places in the code.
Technically, I also changed the DSPF source to reference the F3 key rather than the F1 key that you show in your DDS. The F1 key would normally be for 'help' functions while F3 is a standard for 'Exit'. That doesn't really matter, but it's one habit to get started. And a name or two was changed just to fit within my environment.
No spooled files were necessary. Easiest way to view your job's "joblog" after a CALL command is to run a DSPJOBLOG command. Perhaps better, though, is to use the basic command entry display provided by CALL QCMD. Basic joblog messages can be toggled on/off on that display by use of the F10 key to either "Include detailed messages" or "Exclude detailed messages".
All in all, you were pretty close. Not bad at all if this was your first attempt to work with a DSPF.
How can I parse arguments without the hyphen in C?
I.e. virsh install vm
or
git pull origin master
When I tried it out, if there is no - prefix, everything just gets ignored and argc returns 1 (argv[0] is the program call).
I'm using Linux, but it would be nice if there was a cross platform method to achieve this.
UPDATE: the problem was me using a # in front of the first argument, I was trying to pass in #XX eg number_program #12. Needless to say this doesn't work.
Are you using some library to parse the arguments for you? There is no special 'hyphen' arguments when passing in parameters to a C program specifically. Parse argv however you like.
For example:
#include <stdio.h>
int main(int argc, char **argv)
{
int i;
for(i=0; i<argc; i++) {
//dont do this without proper input validation
printf("%s\n", argv[i]);
}
return 0;
}
Example run:
$ ./a.out test test test -hyphen
./a.out
test
test
test
-hyphen
argv contains the program name and the arguments to the program, in the order they were given in the command line.* Hyphens aren't special; they just make it easy for both people and computers to separate options from other args.
If you want to interpret args a certain way, that's your prerogative. That's what git does, basically interpreting argv[1] (if it exists, of course) as the name of a subcommand. And you don't need any libraries in order to do that. You just need to decide how you want the args interpreted.
* Modulo some cross-platform differences in how args are parsed; *nix typically does some pre-parsing for you and expands wildcard patterns, for example. You won't have 100% cross-platform compatibility unless you understand those differences and are ready for them.
I am currently writing my own terminal in C.
I found out, that there is multiple variants of the exec() methode, that i can use.
Its simple occurance lead me to use execv():
int main(int argc , char* argv[]){
char* dir = getcwd(NULL, 0);
char* command[] = {"echo", "Hello", "World", "!!!", NULL};
execv(dir, command);
}
From my understanding this should work. It is compiling, but nothing happens.
The path argument to execv is supposed to be the path specification to the executable file you want to run, not just a directory as returned by getcwd. From the manpage:
The initial argument for these functions is the pathname of a file which is to be executed.
In other words, you're looking for something like:
execv ("/bin/echo", command);
The code you currently have is trying to run your current directory, something that's unlikely to end well, and something you may have noticed if you checked the return value from execv along with errno: nudge, nudge, wink, wink :-)
In terms of what to do for other programs, you simply substitute their full path name for /bin/echo.
You should also be aware that exec is a family of functions, each with slight variations.
Some allow environments to be passed, some automatically search the path for your executable (depending on the name given), and some use variable argument lists rather than arrays. If you want to use the automatic path searching, you would look into execvp rather than execv, then you don't have to worry about where the executable file is located.
I am very new with c and less experienced with any other language :/
For an assignment at uni, I am a little stuck on this small part. Essentially I am required to write a 'ls' function that has 4 optional arguments, for example:
list [-l] [-f] [pathname] [localfile]
Now, the first two are straight forward. To make things more difficult, the 'localfile' doesn't necessarily exist and the 'pathname'(if given) will be located on the server I'm connecting to through a socket (so checking if it is a file is out and checking the pathname is out). I was thinking, check last 4 chars in the string for a '.txt' or something similar. I'm actually completely stumped and will present this problem to my course conveyor tomorrow, if I can't find a solution.
This is a very small part of what I actually have to do but any push in the right direction would be appreciated.
You will need to process argc and argv to get your command line arguments. That is the first thing to work on, getting the arguments - ensuring they are correct, and determining what is being asked for.
int main(int argc, char *argv[])
Assuming your are on Linux/Unix, you will need to use the directory functions opendir()/readdir()/closedir() - dirent.h. The stat() function will be required to satisfy the -l requirement. access() will determine if a file exists and then stat() will tell you if the file is a regular file or a directory.
I'd make a struct to hold the four optional arguments and return it from a function called "process_arguments" that takes argc and argv as parameters.
struct args {
bool valid;
bool l_option;
bool f_option
char directory[200];
char filename[200];
}
With the requirement for a socket connection you will have to write a "server program" that will be constantly running on the server and a "client program" that it will fork to handle the requests from your local program. Try and locate examples of socket programs.
Another check for whether you have a path string or a filename is to look for the path separator character - '/' if the server is Unix/Linux. This scheme shouldn't have any path separators in filenames, so the presence of one tells you it is a path.
Is it possible to open a program using another program? For example:
I want to make a command line application in C that will prompt the user to type in the name of a program (lets say Microsoft Word.app), and that program will launch. Would I do something like this:
#include <stdio.h>
#include <time.h>
int main (int argc, const char * argv[]) {
char programName[1000];
printf("Type in the name of the program you would like to open: ");
scanf("%s", programName);
popen(programName);
}
However, popen() asks me for another char. How would I go about using popen() to open the program?
EDIT: The following code works!
#include <stdio.h>
#include <time.h>
int main (int argc, const char * argv[]) {
char programName[1000];
char app[100] = ".app";
char openApp[100] = "open /Applications/";
printf("Type in the name of the program you would like to open: ");
scanf("%s", programName);
strcat(openApp, programName);
strcat(openApp, app);
system(openApp);
}
popen lets you launch a program and get a file descriptor to its input or output, much like fopen works for files. For instance, if you wanted to read the output of your program, you'd use popen("program", "r"). On the other hand, if you want to write to its input, you would use popen("program", "w"). Mac OS X also allows for r+, which lets you read the output and write to the input but this capability isn't standard and shouldn't be relied on for cross-platform code.
If you just want to launch a program, you might as well use the system function, which does that and waits until the program exits, at which point it returns the status code. system actually invokes the shell to work, so arguments will undergo expansion (environment variables, ~, etc).
EDIT Following your comment that system("Microsoft Word.app") doesn't work as you'd expect: there are several reasons for this, actually. Starting with the message you get: this is because what you wrote is equivalent to opening a terminal window and typing Microsoft Word.app. In other words, it tries to find a program called "Microsoft", then pass it the argument "Word.app". You would need to either quote the program name or escape spaces to have the shell understand it's a whole program name and not a program name then an argument: system("Microsoft\ Word.app")
Now, this should complain saying that the shell can't find the program "Microsoft Word.app", which is already a step forward.
This is because on Mac OS, app files aren't executable files: they're folders that the Finder displays as a single file. You can verify that by ctrl+clicking (or right-clicking) an app and selecting "Show package contents" (this will open the app folder). The actual executable for Microsoft Word.app must be somewhere along the path of Microsoft Word.app/Contents/MacOS/Microsoft Word.
As you can see, this is getting kind of complex. Luckily enough, Apple provides the open executable, which can use a bunch of OS services to figure out those details. It allows to launch applications in the following fashion:
open -a Microsoft\ Word
This should launch Word. (Notice how you still need to escape the spaces.) In pure C code, that would get you something like this:
system("open -a Microsoft\\ Word");
If you choose to use Objective-C and Cocoa, however, there is a very simple way to open applications:
NSString* appName = #"Microsoft Word"; // no escape!
[[NSWorkspace sharedWorkspace] launchApplication:appName];
NSString objects can be created from C string easily enough:
NSString* appName = [[NSString alloc] initWithCString:programName encoding:NSUTF8StringEncoding];
[[NSWorkspace sharedWorkspace] launchApplication:appName];
[appName release];
It would be better to use system(3) for this purpose.
The popen(3) function establishes a pipeline that can be read or written by the caller. But GUI applications do not use standard input and standard output, they connect to the graphical interface server, sometimes called the "window server". This server is already running and already has decided what its keyboard input will be, and it is always writing its output to the video device.
To start a .app you should actually run the open(1) program, so try something like:
system("open /Applications/MacVim.app");