scanf() without displaying on screen - c

Is there any function in C on Linux that permit to wait an user input without displaying on terminal what he is typing ? (like when you enter your password on terminal on Linux)
I found getch() but it's not working on Linux ... :(
I found this but it's too complicated ... :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
int main(void)
{
char buffer[100];
struct termios infos;
int size;
if (tcgetattr(STDIN_FILENO, &infos) == -1)
{
fprintf(stderr, "Erreur tcgetattr.\n");
return (EXIT_FAILURE);
}
infos.c_lflag &= ~ECHO;
if (tcsetattr(STDIN_FILENO, TCSANOW, &infos) == -1)
{
fprintf(stderr, "Erreur tcsetattr.\n");
return (EXIT_FAILURE);
}
if ((size = read(STDIN_FILENO, buffer, sizeof(char) * 100)) == -1)
{
fprintf(stderr, "Erreur durant la lecture.\n");
return (EXIT_FAILURE);
}
buffer[size - 1] = '\0';
printf("le buffer contient [%s]\n", buffer);
return (EXIT_SUCCESS);
}

It's not scanf that is displaying the characters on screen, it's the terminal emulation which is echoing characters you type. You can disable that using the technique you suggested (or use a library to hide this), but no amount of fiddling with other functions to read STDIN is going to help.

The getpass function is not reliably present and has "obsolete, avoid" warnings in its manpage; however, it does essentially what you want. (I suspect it was obsoleted because it's not reentrant and doesn't allow the entry of an arbitrarily long password, which is kind of important nowadays.) The readpassphrase function is even less portable and has its own quirks (most of the flags should not be used, for instance) but does let you control how big the read buffer is. And gnulib has getpass-gnu, which adheres to the getpass calling convention but returns a malloced string rather than a pointer to static storage, thus fixing the major problems with the original getpass.
In a serious program I would probably start with the gnulib getpass, rename the function (to avoid potential conflicts with libc) and then hack out some of the more dubious features (like reading from stdin if /dev/tty is unavailable - the Right Thing there is to fail hard, but provide some other way of getting a passphrase into the program for scripting use). Or, if feasible, I would build the program around SSH keys rather than passwords.

Related

How can i make getchar() function hold backspace

#include<stdio.h>
int main(void)
{
int c;
while((c=getchar())!=EOF)
{
if(c==8) // 8 is ASCII value of backspace
printf("\\b");
}
}
Now I want to enter backspace and want getchar() function to return ASCII of backspace to c(int variable)
Note- I am not asking about getch function i know that getch command is able to read backspace
I only want to know whether getchar is able to read backspace or not
If yes,How?
How to do it please explain me
I am new to C programming
If the stream stdin reads from a file, getchar() will handle backspace characters ('\b' or 8 in ASCII) like any other regular character and return it to the caller.
The reason it does not do that when reading from the console (aka terminal or tty) is related to the configuration of the console itself. The console is in cooked mode by default, so as to handle echo, backspace and line buffering, but also signals such as SIGINT sent for Ctrl-C and many more subtile settings.
The C Standard does not provide any way to change the terminal modes, but POSIX systems have the stty command and the termios system calls available to C programs to do this.
Once you configure the console in raw mode, also set stdin to unbuffered mode with setvbuf(stdin, NULL, _IONBF, 0) so getchar() reads each byte from the console as it is typed.
Configuring the console is a complex issue, you may want to read this first: http://www.linusakesson.net/programming/tty/
If your system supports termios as standardized in POSIX.1-2001, then you can manipulate the standard input terminal to not buffer your input. Consider the following example:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* SIGINT handler */
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
if (!done)
done = signum;
}
static int install_done(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
/* Reverting terminal back to original settings */
static struct termios terminal_config;
static void revert_terminal(void)
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminal_config);
}
int main(void)
{
int c;
/* Set up INT (Ctrl+C), TERM, and HUP signal handlers. */
if (install_done(SIGINT) ||
install_done(SIGTERM) ||
install_done(SIGHUP)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Make terminal input noncanonical; not line buffered. Also disable echo. */
if (isatty(STDIN_FILENO)) {
struct termios config;
if (tcgetattr(STDIN_FILENO, &terminal_config) == 0 &&
tcgetattr(STDIN_FILENO, &config) == 0) {
config.c_lflag &= ~(ICANON | ECHO);
config.c_cc[VMIN] = 1; /* Blocking input */
config.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &config);
atexit(revert_terminal);
}
}
/* Set standard input unbuffered. */
setvbuf(stdin, NULL, _IONBF, 0);
printf("Press Ctrl+C to exit.\n");
fflush(stdout);
while (!done) {
c = fgetc(stdin);
if (c == EOF)
printf("Read EOF%s\n", ferror(stdin) ? " as an error occurred" : "");
else
printf("Read %d = 0x%02x\n", c, (unsigned int)c);
fflush(stdout);
}
return EXIT_SUCCESS;
}
The #define line tells your C library headers to expose POSIX.1 features for GNU-based systems.
The done flag is set whenever an INT (Ctrl+C), TERM, or HUP signal is received. (HUP signal is sent if you disconnect from the terminal, for example by closing the terminal window.)
The terminal_config structure will contain the original terminal settings, used by revert_terminal() registered as an at-exit function, to revert the terminal settings back to the original ones read at program startup.
The function isatty(STDIN_FILENO) returns 1 if standard input is a terminal. If so, we obtain the current terminal settings, and modify them for non-canonical mode, and ask that each read blocks until at least one character is read. (The I/O functions tend to get a bit confused if you set .c_cc[VMIN]=0 and .c_cc[VTIME]=0, so that if no input is pending, fgetc() returns 0. Typically it looks like an EOF to stdio.h I/O functions.)
Next, we tell the C library to not internally buffer standard input, using setvbuf(). Normally, the C library uses an input buffer for standard input, for efficiency. However, for us, it would mean the C library would buffer characters typed, and our program might not see them immediately when typed.
Similarly, standard output is also buffered for efficiency. The C library should flush all complete lines to the actual standard output, but we can use the fflush(stdout) call to ensure everything we've written to stdout is flushed to the actual standard output at that point.
In main(), we then have a simple loop, that reads keypresses, and prints them in decimal and hexadecimal.
Note that when a signal is delivered, for example the INT signal because you typed Ctrl+C, the delivery of the signal to our handle_done() signal handler interrupts the fgetc() call if one is pending. This is why you see Read EOF when you press Ctrl+C; if you check ferror(stdin) afterwards, you'll see it returns nonzero (which indicates an error occurred). The "error" in this case is EINTR, "interrupted by a signal".
Also note that when you press some certain keys, like cursor or function keys, you'll see multiple characters generated, usually beginning with 27 and 91 (decimal; 0x1B 0x5B in hexadecimal; "\033[" if expressed as a C string literal). These are usually, but not always, ANSI escape sequences. In general, they are terminal-specific codes that one can obtain via tigetstr(), tigetnum(), and tigetflag() using the terminfo database.
A much more portable way to do this, is to use a Curses library; either ncurses on most systems, or PDCurses on Windows machines. Not only do they provide a much easier interface, but it does it in a terminal-specific way, for maximum compatibility across systems.
C programs using the Curses functions can be compiled against any Curses library, so the same C source file can be compiled and run on Linux, Mac, and Windows machines. However, ncurses does contain quite a few extensions, which may not be provided by other Curses libraries like PDCurses.

Can I access stdin without keyboard?

I have compiled the following code with gcc
int main() {
int a = 0;
fprintf( stdin, "%d", 123 );
fscanf( stdin, "%d", &a );
printf( "%d\n", a );
return 0;
}
In my expectation, the program should executes straightly (i.e., the program never pause and wait for user input). But it still stop, and wait for my input.
I want to know what happen when I try to write something to stdin and how to modify this code and it can execute straightly?
stdin is for input only, stdout is for output. (4566976's answer shows you what happens when you try to output to stdin) See for example the glibc documentation on standard streams
(in short, writing to stdin makes no sense at all)
If you print out the return value of fprintf(stdin you can see that the function call fails.
In the shell you can pipe something into the stdin of the process.
#include <stdio.h>
int main(void) {
int a = 0, ret;
printf("%d\n", ret = fprintf( stdin, "%d", 123 ));
if (ret < 0) perror("fprintf");
fscanf( stdin, "%d", &a );
printf( "%d\n", a );
return 0;
}
$ echo 123 | ./a.out
-1
fprintf: Bad file descriptor
123
$
In addition of the fprintf(stdin, bug you also forgot that stdin is not the keyboard. The latest C11 standard does not know about the keyboard. On a Linux graphical desktop, only the X11 server is reading from the physical keyboard.
Practically speaking, on POSIX systems notably such as Linux, stdin can be a pipe(7) (using pipelines in your shell is very common), a fifo(7), a socket(7), a plain file (thru redirection) or even /dev/null, and of course also a terminal.
The funny thing these days is that terminals are very often virtual emulated devices (I did not see any real physical terminal in this century, outside of museums), read about pseudotty. The details are quite arcane for historical reasons. Read the tty demystified page. See also ANSI escape code wikipage & console_codes(4) and tty(4) (so consider /dev/tty and perhaps /dev/console)
You can check (with isatty(3)) that stdin is a terminal (actually a pseudotty) using isatty(STDIN_FILENO)...
Practically speaking, when you really want to use the terminal, I strongly recommend using a library like ncurses or GNU readline (both are using termios(3))
Don't forget that I/O is generally buffered, and use fflush(3) wisely.
BTW, you should have compiled with all warnings & debug info (gcc -Wall -Wextra -g) then use the gdb debugger. And strace(1) would have been very useful too.
Maybe you wanted to pipe to your own program (but that is weird, and often wrong, unless you take great care about all the implications; it is however a very useful trick for handling signal(7) in event oriented programs, notably those with some GUI). Beware that pipes have a limited buffer size (so avoid deadlocks, probably by having your event loop with poll(2)) and read about PIPE_BUF and write. You might have tried:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int pfd[2] = {-1,-1};
int a= 0;
if (pipe(pfd)) { perror("pipe"); exit (EXIT_FAILURE); };
if (dup2(pfd[0],STDIN_FILENO)<0)
{ perror("dup2 stdin"); exit(EXIT_FAILURE);};
if (dup2(pfd[1],STDOUT_FILENO)<0)
{ perror("dup2 stdout"); exit(EXIT_FAILURE);};
if (printf("%d\n", 123)<=0) { perror("printf"); exit(EXIT_FAILURE); };
if (fflush(stdout)) { perror("fflush"); exit(EXIT_FAILURE); };
if (scanf("%d", &a)<1) { perror("scanf"); exit(EXIT_FAILURE); };
if (a != 123) { fprintf(stderr, "impossible happened a=%d\n", a); };
fprintf(stderr, "done...got a=%d\n", a);
}
You should read Advanced Linux Programming and learn more about syscalls(2); it has several chapters related to this. Read carefully pipe(2) and dup2(2) and be aware that the above program would be wrong for a larger output (bigger that PIPE_BUF, which on my system is several kilobytes)
BTW, you can get a readable FILE* from a memory buffer using fmemopen(3). For writing (e.g. with fprintf) to an output buffer, consider open_memstream and don't forget to fflush it before accessing the output buffer.
You can ungetc() a few characters and then read them with fscanf().
#include <stdio.h>
int main()
{
int value = 0;
ungetc ( '\n', stdin);//reverse order. newline first here but last from fscanf
ungetc ( '3', stdin);
ungetc ( '2', stdin);
ungetc ( '1', stdin);
fscanf ( stdin, "%d", &value);
printf ( "value is %d\n", value);
return 0;
}
output: value is 123
You're simply incorrect thinking that fscanf(stdin, "format", ...); does not block and wait for input, because it does.

How to abort getchar() command in C?

I am basically a beginner C++ programmer...and it's my first attempt to code in C.
I am trying to program a snake game (using system ("cls")).
In this program I need to get a character as an input (basically to let the user change the direction of movement of snake)... and if the use doesn't input any character within half a second then this character input command needs to be aborted and my remaining code should get executed.
Please give suggestions to sort out this problem.
EDIT: Thanks for the suggestions, but
My main motive of asking this question was to find a method to abort the getchar command even if the user has not entered anything....Any suggestions on this? And by the way my platform is windows
The best way, in my opinion, is using libncurses.
http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/
You have all the tools to make a snake easily.
If you think it's too easy (it is a relatively high level library), look at the termcaps library.
EDIT: So, a non-blocking read with termcaps is :
#include <termios.h>
#include <unistd.h>
#include <term.h>
uintmax_t getchar()
{
uintmax_t key = 0;
read(0, &key, sizeof(key));
return key;
}
int main(int ac, char **av, char **env)
{
char *name_term;
struct termios term;
if ((name_term = getenv("TERM")) == NULL) // looking for name of term
return (-1);
if (tgetent(NULL, &name_term) == ERR) // get possibilities of term
return (-1);
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 0; term.c_cc[VTIME] = 0; // non-blocking read
if (tcgetattr(0, term) == -1) // applying modifications.
return (-1);
/* Your code here with getchar() */
term.c_lflag &= (ICANON | ECHO);
if (tcgetattr(0, term) == -1) // applying modifications.
return (-1);
return (0);
}
EDIT 2:
You have to compile with
-lncurses
option.
The way to do this on a UNIX-like platform (such as Linux) is to use the select function. You can find its documentation online. I'm not sure if this function is available on Windows; you didn't specify an operating system.
I got the best suited answer to my question in comments, which was posted by #eryksun.
The best way is to use a function kbhit() (part of conio.h).
You can spawn a new thread that can emulate pressing of the Enter Key after 30 seconds.
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "User32.lib")
void ThreadProc()
{
// Sleep for 30 seconds
Sleep(30*1000);
// Press and release enter key
keybd_event(VK_RETURN, 0x9C, 0, 0);
keybd_event(VK_RETURN, 0x9C, KEYEVENTF_KEYUP, 0);
}
int main()
{
DWORD dwThreadId;
HANDLE hThread = CreateThread(NULL, 0,(LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0,&dwThreadId);
char key = getchar();
// you are out of getchar now. You can check the 'key' for a value of '10' to see if the thread did it.
// Kill thread before you do getchar again
}
Be careful with this technique, specially if you do geatchar() in a loop, otherwise you might end up with lot of threads pressing ENTER key! Be sure to kill the thread before you start getchar() again.

Change input separator in c

Hi i was reading K & R C where in the example to use getchar we have to press RET to end the input. Is there any way to change the input so that I can change the newline as the input separator to a comma. I use a Linux Mint . 64 bit. I get the input by running the program as ./Hello.o
Eg.
Hello world<RET>
but as I type a comma or dot the input should end
Eg.
Hello world, //End of input due to comma
Is there any way to change new line to another character to comma
It depends what function you want to use. For example, if you want to use getdelim you can provide a delimiter argument
ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
for example:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char * lineptr = malloc(1000 * sizeof(char));
size_t n = 1000;
/*type something in that ends in a comma*/
getdelim(&lineptr, &n, ',', stdin);
/*print the result*/
printf("%s\n", lineptr);
free(lineptr);
return 0;
}
Note that this still requires you to press enter but everything after the comma will be discarded.
edit
Maybe you could try something like this?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
/* Use this variable to remember original terminal attributes. */
struct termios saved_attributes;
void
reset_input_mode (void)
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
void set_input_mode (void)
{
struct termios tattr;
char *name;
/* Make sure stdin is a terminal. */
if (!isatty (STDIN_FILENO))
{
fprintf (stderr, "Not a terminal.\n");
exit (EXIT_FAILURE);
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
/* Set the funny terminal modes. */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
int main()
{
char c;
set_input_mode ();
while (1)
{
read (STDIN_FILENO, &c, 1);
if (c == ',')
break;
else
putchar (c);
}
return 0;
}
This program will receive input until a comma. I can't take any credit for it, I found this link on noncanonical input.
You can use getch() function available in ncurses library.
You do no need to press Enter after each character input, therefore you can scan input in a while loop using getch() unless you get a , or any special character you want.
I would first read the standard input line by line using getline(3). Then you can do your own tokenizing on that line, e.g. with sscanf(3), strtok(3) or other means (e.g. using strchr(3) appropriately). See this answer for some code (but getline won't enable you to avoid pressing the return key, because the kernel tty subsystem is processing that key, unless you do raw keyboard input which is really difficult).
On Linux, you might be interested in using readline(3) from the readline library (which is quite powerful, so learn more about it). Maybe you could use the ncurses library.
Be aware that terminals are very complex things (or abstractions), mostly for historical reasons. Read with care the tty demystified page, and the Keyboard and Console HowTo.
Avoiding pressing the enter or return key is surprisingly difficult on Unix systems (and probably on others too!) because the kernel (and not only the application or its libc i.e. <stdio.h> functions) is usually buffering the input line. To avoid that, you need to do very low level and difficult programming (which could take you several weeks of work). Read first Advanced Linux Programming.
So instead of doing all the difficult coding by yourself, take several days to learn how to use readline (or maybe ncurses). Even with the help of such libraries, it is not that easy!

Tried and true simple file copying code in C?

This looks like a simple question, but I didn't find anything similar here.
Since there is no file copy function in C, we have to implement file copying ourselves, but I don't like reinventing the wheel even for trivial stuff like that, so I'd like to ask the cloud:
What code would you recommend for file copying using fopen()/fread()/fwrite()?
What code would you recommend for file copying using open()/read()/write()?
This code should be portable (windows/mac/linux/bsd/qnx/younameit), stable, time tested, fast, memory efficient and etc. Getting into specific system's internals to squeeze some more performance is welcomed (like getting filesystem cluster size).
This seems like a trivial question but, for example, source code for CP command isn't 10 lines of C code.
This is the function I use when I need to copy from one file to another - with test harness:
/*
#(#)File: $RCSfile: fcopy.c,v $
#(#)Version: $Revision: 1.11 $
#(#)Last changed: $Date: 2008/02/11 07:28:06 $
#(#)Purpose: Copy the rest of file1 to file2
#(#)Author: J Leffler
#(#)Modified: 1991,1997,2000,2003,2005,2008
*/
/*TABSTOP=4*/
#include "jlss.h"
#include "stderr.h"
#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_fcopy_c[] = "#(#)$Id: fcopy.c,v 1.11 2008/02/11 07:28:06 jleffler Exp $";
#endif /* lint */
void fcopy(FILE *f1, FILE *f2)
{
char buffer[BUFSIZ];
size_t n;
while ((n = fread(buffer, sizeof(char), sizeof(buffer), f1)) > 0)
{
if (fwrite(buffer, sizeof(char), n, f2) != n)
err_syserr("write failed\n");
}
}
#ifdef TEST
int main(int argc, char **argv)
{
FILE *fp1;
FILE *fp2;
err_setarg0(argv[0]);
if (argc != 3)
err_usage("from to");
if ((fp1 = fopen(argv[1], "rb")) == 0)
err_syserr("cannot open file %s for reading\n", argv[1]);
if ((fp2 = fopen(argv[2], "wb")) == 0)
err_syserr("cannot open file %s for writing\n", argv[2]);
fcopy(fp1, fp2);
return(0);
}
#endif /* TEST */
Clearly, this version uses file pointers from standard I/O and not file descriptors, but it is reasonably efficient and about as portable as it can be.
Well, except the error function - that's peculiar to me. As long as you handle errors cleanly, you should be OK. The "jlss.h" header declares fcopy(); the "stderr.h" header declares err_syserr() amongst many other similar error reporting functions. A simple version of the function follows - the real one adds the program name and does some other stuff.
#include "stderr.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void err_syserr(const char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
exit(1);
}
The code above may be treated as having a modern BSD license or GPL v3 at your choice.
As far as the actual I/O goes, the code I've written a million times in various guises for copying data from one stream to another goes something like this. It returns 0 on success, or -1 with errno set on error (in which case any number of bytes might have been copied).
Note that for copying regular files, you can skip the EAGAIN stuff, since regular files are always blocking I/O. But inevitably if you write this code, someone will use it on other types of file descriptors, so consider it a freebie.
There's a file-specific optimisation that GNU cp does, which I haven't bothered with here, that for long blocks of 0 bytes instead of writing you just extend the output file by seeking off the end.
void block(int fd, int event) {
pollfd topoll;
topoll.fd = fd;
topoll.events = event;
poll(&topoll, 1, -1);
// no need to check errors - if the stream is bust then the
// next read/write will tell us
}
int copy_data_buffer(int fdin, int fdout, void *buf, size_t bufsize) {
for(;;) {
void *pos;
// read data to buffer
ssize_t bytestowrite = read(fdin, buf, bufsize);
if (bytestowrite == 0) break; // end of input
if (bytestowrite == -1) {
if (errno == EINTR) continue; // signal handled
if (errno == EAGAIN) {
block(fdin, POLLIN);
continue;
}
return -1; // error
}
// write data from buffer
pos = buf;
while (bytestowrite > 0) {
ssize_t bytes_written = write(fdout, pos, bytestowrite);
if (bytes_written == -1) {
if (errno == EINTR) continue; // signal handled
if (errno == EAGAIN) {
block(fdout, POLLOUT);
continue;
}
return -1; // error
}
bytestowrite -= bytes_written;
pos += bytes_written;
}
}
return 0; // success
}
// Default value. I think it will get close to maximum speed on most
// systems, short of using mmap etc. But porters / integrators
// might want to set it smaller, if the system is very memory
// constrained and they don't want this routine to starve
// concurrent ops of memory. And they might want to set it larger
// if I'm completely wrong and larger buffers improve performance.
// It's worth trying several MB at least once, although with huge
// allocations you have to watch for the linux
// "crash on access instead of returning 0" behaviour for failed malloc.
#ifndef FILECOPY_BUFFER_SIZE
#define FILECOPY_BUFFER_SIZE (64*1024)
#endif
int copy_data(int fdin, int fdout) {
// optional exercise for reader: take the file size as a parameter,
// and don't use a buffer any bigger than that. This prevents
// memory-hogging if FILECOPY_BUFFER_SIZE is very large and the file
// is small.
for (size_t bufsize = FILECOPY_BUFFER_SIZE; bufsize >= 256; bufsize /= 2) {
void *buffer = malloc(bufsize);
if (buffer != NULL) {
int result = copy_data_buffer(fdin, fdout, buffer, bufsize);
free(buffer);
return result;
}
}
// could use a stack buffer here instead of failing, if desired.
// 128 bytes ought to fit on any stack worth having, but again
// this could be made configurable.
return -1; // errno is ENOMEM
}
To open the input file:
int fdin = open(infile, O_RDONLY|O_BINARY, 0);
if (fdin == -1) return -1;
Opening the output file is tricksy. As a basis, you want:
int fdout = open(outfile, O_WRONLY|O_BINARY|O_CREAT|O_TRUNC, 0x1ff);
if (fdout == -1) {
close(fdin);
return -1;
}
But there are confounding factors:
you need to special-case when the files are the same, and I can't remember how to do that portably.
if the output filename is a directory, you might want to copy the file into the directory.
if the output file already exists (open with O_EXCL to determine this and check for EEXIST on error), you might want to do something different, as cp -i does.
you might want the permissions of the output file to reflect those of the input file.
you might want other platform-specific meta-data to be copied.
you may or may not wish to unlink the output file on error.
Obviously the answers to all these questions could be "do the same as cp". In which case the answer to the original question is "ignore everything I or anyone else has said, and use the source of cp".
Btw, getting the filesystem's cluster size is next to useless. You'll almost always see speed increasing with buffer size long after you've passed the size of a disk block.
the size of each read need to be a multiple of 512 ( sector size ) 4096 is a good one
Here is a very easy and clear example: Copy a file. Since it is written in ANSI-C without any particular function calls I think this one would be pretty much portable.
Depending on what you mean by copying a file, it is certainly far from trivial. If you mean copying the content only, then there is almost nothing to do. But generally, you need to copy the metadata of the file, and that's surely platform dependent. I don't know of any C library which does what you want in a portable manner. Just handling the filename by itself is no trivial matter if you care about portability.
In C++, there is the file library in boost
One thing I found when implementing my own file copy, and it seems obvious but it's not: I/O's are slow. You can pretty much time your copy's speed by how many of them you do. So clearly you need to do as few of them as possible.
The best results I found were when I got myself a ginourmous buffer, read the entire source file into it in one I/O, then wrote the entire buffer back out of it in one I/O. If I even had to do it in 10 batches, it got way slow. Trying to read and write out each byte, like a naieve coder might try first, was just painful.
The accepted answer written by Steve Jessop does not answer to the first part of the quession, Jonathan Leffler do it, but do it wrong: code should be written as
while ((n = fread(buffer, 1, sizeof(buffer), f1)) > 0)
if (fwrite(buffer, n, 1, f2) != 1)
/* we got write error here */
/* test ferror(f1) for a read errors */
Explanation:
sizeof(char) = 1 by definition, always: it does not matter how many bits in it, 8 (in most cases), 9, 11 or 32 (on some DSP, for example) — size of char is one. Note, it is not an error here, but an extra code.
The fwrite function writes upto nmemb (second argument) elements of specified size (third argument), it does not required to write exactly nmemb elements. To fix this you must write the rest of the data readed or just write one element of size n — let fwrite do all his work. (This item is in question, should fwrite write all data or not, but in my version short writes impossible until error occurs.)
You should test for a read errors too: just test ferror(f1) at the end of loop.
Note, you probably need to disable buffering on both input and output files to prevent triple buffering: first on read to f1 buffer, second in our code, third on write to f2 buffer:
setvbuf(f1, NULL, _IONBF, 0);
setvbuf(f2, NULL, _IONBF, 0);
(Internal buffers should, probably, be of size BUFSIZ.)

Resources