On a POSIX system you would use globbing (see glob(7)...). So you could type
./mytest foo*.png bar*.jpeg
and the shell would expand the
foo*.png so your program (its
main function) would get an array of program arguments which could be quite long (e.g. hundreds or perhaps thousands of arguments). There are limitations (read execve(2), getrlimit(2)...) but quite often you won't care (typical limit could be several hundred thousands bytes for the program arguments). See also xargs(1).
(if you want your program to be able to handle a very large amount of files, e.g. several millions of them, you should have some indirect approach:
it is unreasonable to expect your
main to get megabytes of program arguments)
The shell is also able to do some even more powerful expansion, e.g. like using find(1) in
./mytest $(find a* -type f \( -name '*.jpeg' -o -name '*.png' \) )
Hint: use echo to understand expansion, e.g. replace
echo in the line above to be told how that is expanded.
BTW, you could appreciate shells able to wisely do autocompletion; this is why I prefer using zsh as my interactive shell and I often use the tab key while typing some command (if you want similar facilities inside your program look into the GNU readline library).
And you can always design your program specifically, e.g. have it accept some configuration file, some file (or even some database, e.g. sqlite) containing a list of file names, some command giving such a file list (e.g. with popen(3)....), some file pattern provided in a configuration file or (quoted) program argument (use glob(3), wordexp(3), fnmatch(3), ....) etc.
(handling file names with spaces or return characters -or weird characters like
| or starting
- etc...- in them might be tricky, but is doable; beware of code injection and have quoting conventions, or decide to avoid such weird file names, but document what you expect and what you forbid)
You could also have your program scan recursively some file tree (e.g. using nftw(3) or readdir(3) ...)
You could even embed some scripting interpreter (like Guile, Lua, etc...) in your application and enable a power user to code a script driving the work of your program (but embedding an interpreter is a strong architectural decision).
For larger applications that requires a lot of input, simply using command line becomes messy very fast.
It is not a matter of large vs small application (for example find(1) is a small program -only 240K bytes on my Debian/x86-64- by current standards, and cat(1) or wc(1) are even smaller, yet often used with many program arguments), and a power user would write some shell script to drive an application he uses often, and that is not messy .... So command line arguments are usually very convenient.
However, it really depends upon your audience: a developer is not scared by GCC accepting many options.... but your grandma might be scared by using the command line; so YMMV.
... the application will parse the XML file or JSON file
Configuration or script files could be in JSON or XML format, but usually are not (since both formats are verbose, and comments are forbidden in standard JSON). Look at existing practice (e.g. many configuration files under
/etc/ ...) for inspiration (e.g.
INI format). It is sensible to accept some kind of comments in configuration files (usually starting with
#, or perhaps
;, till end of line), to enable the sysadmin or the poweruser to explain the configuration.
You can find libraries helping in parsing configuration files, e.g. libconfig or Glib's GScanner etc etc.... And there are many functions helping in parsing program arguments (and you can combine several approaches). Cross-platform frameworks like Qt, POCO, GTK, Boost, etc... also provide support for configuration.
You could make your program become some server (e.g.using JSON RPC) and listen to messages or requests (see also socket(7)).
You could use some other forms of inter-process communication.
You could adopt (or not) the Unix philosophy and expect the power user to combine several programs (including yours), perhaps in some command pipeline.
See also this answer to a related question about "good habits for designing command line arguments".
If on Linux (or POSIX) read also Advanced Linux Programming.
Read also more about Operating Systems, notably Operating Systems : Three Easy Pieces (since your question is linked to the relation between OSes and application programs and how they are started).
Sometimes you write some shell script doing preliminary argument parsing (e.g. using getopt(1) or
getopts builtin) and other stuff and driving some real executable (perhaps in
/usr/libexec/). For example, on my Debian box,
/usr/bin/firefox is a script.
PS. things are probably different on Windows (which I don't know) which is rumored to handle
main argument expansion in some startup routine à la crt0 and provides a central registry. My answer is implicitly focused on Linux.