1

These terms may not be 100% accurate, but I'm using the GCC compiler and POSIX library. I have C code compiled with the SQLite amalgamation file to a single executable.

In the user interface that exchanges JSON messages with the C program, I'd like to make it possible for users to copy the SQLite database files they create through the C program, and copy a full directory/folder.

Thus far, I've been able to rename and move files and folders programmatically.

I've read many questions and answers here, at Microsoft's C runtime library, and other places but I must be missing the fundamental points. I'm using regular old C, not C++ or C#.

My question is are there POSIX functions similar to rename(), _mkdir(), rmdir(), remove(), _stat(), that allow for programmatic copying of files and folders in Windows and Linux?

If not, can one just make a new folder and/or file and fread/fwrite the bytes from the original file to the new file?

I am primarily concerned with copying SQLite database files, although I wouldn't mind knowing the answer in general also.

Is this answer an adequate method?

Is the system() function a poor method? It seems to work quite well. However, it took awhile to figure out how to stop the messages, such as "copied 2 files" from being sent to stdout and shutting down the requesting application since it's not well-formed JSON. This answer explains how and has a link to Microsoft "Using command redirection operators". A /q in xcopy may or may not be necessary also, but certainly didn't do the job alone.

Thank you very much for any direction you may be able to provide.


The question that someone suggested as an answer and placed the little submission box on this question is one that I had already linked to in my question. I don't mean to be rude but, if it had answered my question, I would not have written this one. Thank you whoever you are for taking the time to respond, I appreciate it.

I don't see how that would be a better option than using system() because with the right parameters all the sub-directories and files of a single parent folder can be copied in one statement without having to iterate through all of them manually. Is there any reason why it would not be better to use system() apart from the fact that code will need to be different for each OS?

Handling errors are a bit different because system() doesn't return an errno but an exit code; however, the errors can be redirected from stderr to a file and pulled from there, when necessary

Gary
  • 2,393
  • 12
  • 31
  • I googled posix c file functions and found: https://www.mkompf.com/cplus/posixlist.html – Stuart Feb 25 '21 at 05:59
  • Using the `system` function ties you to a particular system; this may be bad depending on your use case. – Neil Feb 25 '21 at 06:15
  • Thank you @Stuart and @Neil. I could not locate any other directory/file related functions either. It doesn't appear that the other POSIX functions in Windows will work in Linux anyway. A number of them start with an `_` because the original has been deprecated, and also have a different number of arguments. I'm very much a novice in this area but it appears that it wouldn't be possible to have one C program that would run in both OSs whether or not `system()` is used. Is that what should be expected or should one C program in POSIX be able to run in both Windows and Linux? Thanks. – Gary Feb 25 '21 at 06:27
  • POSIX basically means Unix-like systems. It was a project launched with the idea of "hey wouldn't it we nice if we can claim that our way of doing things is standard and everyone else is wrong". And so they tossed in whole lot of the trash API Unix lib functions from the dinosaur days and said "this is standard". If someone had actually cared about making a portable operative system interface, they would have spent significantly more time on creating a sound API. The Windows API ain't pretty either, but it is _way_ better designed, invented in 1990 instead of 1970... – Lundin Feb 25 '21 at 07:47
  • @Lundin makes a good point. You have somewhat of an either/or choice depending on the OS and compiler. Linux you have, e.g. `opendir`/`readdir` or `scandir` and `mmap` or `sendfile` to efficiently copy files. (MinGW and Cygwin have caveats in this area). The windows side has its own File access and handling API. If you are writing portable code, this is one area where you will use preprocessor directives to conditionally include the needed code depending on the OS you are running on. – David C. Rankin Feb 25 '21 at 08:04
  • Go with [this answer](https://stackoverflow.com/a/29082484/11294831) of the [question](https://stackoverflow.com/q/29079011/11294831) you linked. Anyway, I would add "b" to the file modes. – the busybee Feb 25 '21 at 10:27
  • @Lundin Thank you for the information. May I please ask what exactly is the correct Windows API, if I simply make a windows specific executable rather than trying POSIX which I arrived at by default or accident anyway? I've looked through them many times but get a bit confused between Windows C runtime, the universal platform, and then WIN32 which seems to always be C++ and not C. Is there something in Windows for C other than POSIX, or is it C++ and WIN32, or something else? Can GCC/G++ still be used? I don't want to use C# or MS version of anything, such as their SQLite. Thank you. – Gary Feb 28 '21 at 02:47
  • @Gary The Windows API was originally written in C and remains that way, Microsoft pushing for C++ is relatively new. You can use gcc/g++ with the Mingw64 port, which uses Microsoft standard libs. There's also commercial alternatives if you don't like MS, most notably Embarcadero (former Borland). – Lundin Mar 01 '21 at 07:33

1 Answers1

1

rename(): posix

_mkdir(): not posix. You want mkdir which is. mkdir takes two arguments, the second of which should usually be 077.

rmdir(): posix

remove(): posix

_stat(): not posix, you want stat() which is.

_stat and _mkdir are called as such on the Windows C library because they're not quite compatible with the modern Unix calls. _mkdir is missing an argument, and _stat looks like a very old version of the Unix call. You'll have trouble on Windows with files larger than 2GB.

You could do:

#ifdef _WIN32
int mkdir(const char *path, int mode) { return _mkdir(path); } /* In the original C we could have #defined this but that doesn't work anymore */
#define stat _stat64
#endif

but if you do so, test it like crazy.

In the end, you're going to be copying stuff with stdio; this loop works. (beware the linked answer; it has bugs that'll bite ya.)

int copyfile(const char *src, const char *dst)
{
    const int bufsz = 65536;
    char *buf = malloc(bufsz);
    if (!buf) return -1; /* like mkdir, rmdir, return 0 for success, -1 for failure */
    FILE *hin = fopen(src, "rb");
    if (!hin) { free(buf); return -1; }
    FILE *hout = fopen(dst, "wb");
    if (!hout) { free(buf); fclose(hin); return -1; }
    size_t buflen;
    while ((buflen = fread(buf, 1, bufsz)) > 0) {
        if (buflen != fwrite(buf, 1, buflen)) {
            fclose(hout);
            fclose(hin);
            free(buf);
            return -1; /* IO error writing data */
        }
    }
    free(buf);
    int r = ferror(hin) ? -1 : 0; /* check if fread had indicated IO error on input */
    fclose(hin);
    return r | (fclose(hout) ? -1 : 0); /* final case: check if IO error flushing buffer -- don't omit this it really can happen; calling `fflush()` won't help. */
}
Joshua
  • 40,822
  • 8
  • 72
  • 132
  • Thank you for the information and code. Would you please tell me what the downside would be to using `system( "copy name.ext new_name.ext" )` for a file copy or `system( "xcopy path new_path /i /e /s /q 1> nul 2> nul" )` for a directory? Especially when `/e /s` picks up all sub-directories even if empty. It's been working in my tests, thus far. Is there some negative that I'm overlooking? Thanks. – Gary Feb 26 '21 at 04:23
  • @Gary: Since you asked about POSIX and Windows in the same question, everybody is assuming you're trying to write cross-platform code. While `system` is cross-platform, the command you pass to it is not. Basically, the only real use of `system` in cross-platform code is providing `!` shell invocations where the rest of the line is passed from the user to `system`. `copy` and `xcopy` don't exist on Unix derivatives. I suppose you could use `#ifdef` and invoke `cp -R` on Unix systems, but that's not what you asked at all. – Joshua Feb 26 '21 at 15:58
  • I'm just trying to understand how to copy files and folders. It doesn't appear that functions are truly cross platform, as pointed out by Lundin and David C. Rankin above. The answer I linked to and to which you provided improved code both deal only with copying a file, not a folder. I suppose a recursive method could be used to copy all folders and files within a folder. If I follow David C. Rankin's advice, my follow-up question was is there any concern about using `system()` to handle this when it can? That may be in Windows only since it appears that Linux has more functions. Thanks. – Gary Feb 26 '21 at 22:59
  • @Gary: Finally in C++ 17 a cross-platform directory entry reader was added: https://stackoverflow.com/q/612097/14768 ; but a stack of #ifdefs may well be easier. Some people claim there's a dirent.h for windows which if true would keep the #ifdefs small. – Joshua Feb 27 '21 at 01:15
  • Thank you. I'm too much of a novice in C and OSs in general to know if that `dirent.h` remark is subtle humor or not. But I've been using the header for the structs `dirent` and `stat` in order to read a directory and get the record information. Of course, I'm going to do a bit of study on them but are #ifdefs for conditional compiling based on OS? If so, is that what is generally done to distribute code rather than compiling a different executable for each OS? – Gary Feb 27 '21 at 03:40
  • @Gary: #ifdef is used to select which code to compile for which platform quite often. – Joshua Feb 27 '21 at 03:45