'\" t
.\"     Title: zfile
.\"    Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
.\"      Date: 01/17/2021
.\"    Manual: CZMQ Manual
.\"    Source: CZMQ 4.2.1
.\"  Language: English
.\"
.TH "ZFILE" "3" "01/17/2021" "CZMQ 4\&.2\&.1" "CZMQ Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el       .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
zfile \- Class for provides methods to work with files in a portable fashion\&.
.SH "SYNOPSIS"
.sp
.nf
//  This is a stable class, and may not change except for emergencies\&. It
//  is provided in stable builds\&.
//  If file exists, populates properties\&. CZMQ supports portable symbolic
//  links, which are files with the extension "\&.ln"\&. A symbolic link is a
//  text file containing one line, the filename of a target file\&. Reading
//  data from the symbolic link actually reads from the target file\&. Path
//  may be NULL, in which case it is not used\&.
CZMQ_EXPORT zfile_t *
    zfile_new (const char *path, const char *name);

//  Destroy a file item
CZMQ_EXPORT void
    zfile_destroy (zfile_t **self_p);

//  Duplicate a file item, returns a newly constructed item\&. If the file
//  is null, or memory was exhausted, returns null\&.
//  Caller owns return value and must destroy it when done\&.
CZMQ_EXPORT zfile_t *
    zfile_dup (zfile_t *self);

//  Return file name, remove path if provided
CZMQ_EXPORT const char *
    zfile_filename (zfile_t *self, const char *path);

//  Refresh file properties from disk; this is not done automatically
//  on access methods, otherwise it is not possible to compare directory
//  snapshots\&.
CZMQ_EXPORT void
    zfile_restat (zfile_t *self);

//  Return when the file was last modified\&. If you want this to reflect the
//  current situation, call zfile_restat before checking this property\&.
CZMQ_EXPORT time_t
    zfile_modified (zfile_t *self);

//  Return the last\-known size of the file\&. If you want this to reflect the
//  current situation, call zfile_restat before checking this property\&.
CZMQ_EXPORT off_t
    zfile_cursize (zfile_t *self);

//  Return true if the file is a directory\&. If you want this to reflect
//  any external changes, call zfile_restat before checking this property\&.
CZMQ_EXPORT bool
    zfile_is_directory (zfile_t *self);

//  Return true if the file is a regular file\&. If you want this to reflect
//  any external changes, call zfile_restat before checking this property\&.
CZMQ_EXPORT bool
    zfile_is_regular (zfile_t *self);

//  Return true if the file is readable by this process\&. If you want this to
//  reflect any external changes, call zfile_restat before checking this
//  property\&.
CZMQ_EXPORT bool
    zfile_is_readable (zfile_t *self);

//  Return true if the file is writeable by this process\&. If you want this
//  to reflect any external changes, call zfile_restat before checking this
//  property\&.
CZMQ_EXPORT bool
    zfile_is_writeable (zfile_t *self);

//  Check if file has stopped changing and can be safely processed\&.
//  Updates the file statistics from disk at every call\&.
CZMQ_EXPORT bool
    zfile_is_stable (zfile_t *self);

//  Return true if the file was changed on disk since the zfile_t object
//  was created, or the last zfile_restat() call made on it\&.
CZMQ_EXPORT bool
    zfile_has_changed (zfile_t *self);

//  Remove the file from disk
CZMQ_EXPORT void
    zfile_remove (zfile_t *self);

//  Open file for reading
//  Returns 0 if OK, \-1 if not found or not accessible
CZMQ_EXPORT int
    zfile_input (zfile_t *self);

//  Open file for writing, creating directory if needed
//  File is created if necessary; chunks can be written to file at any
//  location\&. Returns 0 if OK, \-1 if error\&.
CZMQ_EXPORT int
    zfile_output (zfile_t *self);

//  Read chunk from file at specified position\&. If this was the last chunk,
//  sets the eof property\&. Returns a null chunk in case of error\&.
//  Caller owns return value and must destroy it when done\&.
CZMQ_EXPORT zchunk_t *
    zfile_read (zfile_t *self, size_t bytes, off_t offset);

//  Returns true if zfile_read() just read the last chunk in the file\&.
CZMQ_EXPORT bool
    zfile_eof (zfile_t *self);

//  Write chunk to file at specified position
//  Return 0 if OK, else \-1
CZMQ_EXPORT int
    zfile_write (zfile_t *self, zchunk_t *chunk, off_t offset);

//  Read next line of text from file\&. Returns a pointer to the text line,
//  or NULL if there was nothing more to read from the file\&.
CZMQ_EXPORT const char *
    zfile_readln (zfile_t *self);

//  Close file, if open
CZMQ_EXPORT void
    zfile_close (zfile_t *self);

//  Return file handle, if opened
CZMQ_EXPORT FILE *
    zfile_handle (zfile_t *self);

//  Calculate SHA1 digest for file, using zdigest class\&.
CZMQ_EXPORT const char *
    zfile_digest (zfile_t *self);

//  Self test of this class\&.
CZMQ_EXPORT void
    zfile_test (bool verbose);

#ifdef CZMQ_BUILD_DRAFT_API
//  *** Draft method, for development use, may change without warning ***
//  Create new temporary file for writing via tmpfile\&. File is automatically
//  deleted on destroy
CZMQ_EXPORT zfile_t *
    zfile_tmp (void);

#endif // CZMQ_BUILD_DRAFT_API
//  These methods are deprecated, and now moved to zsys class\&.
CZMQ_EXPORT bool
    zfile_exists (const char *filename);
CZMQ_EXPORT ssize_t
    zfile_size   (const char *filename);
CZMQ_EXPORT mode_t
    zfile_mode   (const char *filename);
CZMQ_EXPORT int
    zfile_delete (const char *filename);
CZMQ_EXPORT bool
    zfile_stable (const char *filename);
CZMQ_EXPORT int
    zfile_mkdir  (const char *pathname);
CZMQ_EXPORT int
    zfile_rmdir  (const char *pathname);
CZMQ_EXPORT void
    zfile_mode_private (void);
CZMQ_EXPORT void
    zfile_mode_default (void);
Please add \*(Aq@interface\*(Aq section in \*(Aq\&./\&.\&./src/zfile\&.c\*(Aq\&.
.fi
.SH "DESCRIPTION"
.sp
The zfile class provides methods to work with disk files\&. A file object provides the modified date, current size, and type of the file\&. You can create a file object for a filename that does not yet exist\&. To read or write data from the file, use the input and output methods, and then read and write chunks\&. The output method lets you both read and write chunks, at any offset\&. Finally, this class provides portable symbolic links\&. If a filename ends in "\&.ln", the first line of text in the file is read, and used as the underlying file for read/write operations\&. This lets you manipulate (e\&.g\&.) copy symbolic links without copying the perhaps very large files they point to\&.
.sp
This class is a new API, deprecating the old zfile class (which still exists but is implemented in zsys now)\&.
.SH "EXAMPLE"
.PP
\fBFrom zfile_test method\fR. 
.sp
.if n \{\
.RS 4
.\}
.nf
const char *SELFTEST_DIR_RW = "src/selftest\-rw";

const char *testbasedir  = "this";
const char *testsubdir  = "is/a/test";
const char *testfile = "bilbo";
const char *testlink = "bilbo\&.ln";
char *basedirpath = NULL;   // subdir in a test, under SELFTEST_DIR_RW
char *dirpath = NULL;       // subdir in a test, under basedirpath
char *filepath = NULL;      // pathname to testfile in a test, in dirpath
char *linkpath = NULL;      // pathname to testlink in a test, in dirpath

basedirpath = zsys_sprintf ("%s/%s", SELFTEST_DIR_RW, testbasedir);
assert (basedirpath);
dirpath = zsys_sprintf ("%s/%s", basedirpath, testsubdir);
assert (dirpath);
filepath = zsys_sprintf ("%s/%s", dirpath, testfile);
assert (filepath);
linkpath = zsys_sprintf ("%s/%s", dirpath, testlink);
assert (linkpath);

// This subtest is specifically for NULL as current directory, so
// no SELFTEST_DIR_RW here; testfile should have no slashes inside\&.
// Normally tests clean up in zfile_destroy(), but if a selftest run
// dies e\&.g\&. on assert(), workspace remains dirty\&. Better clean it up\&.
if (zfile_exists (testfile) ) {
    if (verbose)
        zsys_debug ("zfile_test() has to remove \&./%s that should not have been here", testfile);
    zfile_delete (testfile);
}
zfile_t *file = zfile_new (NULL, testfile);
assert (file);
assert (streq (zfile_filename (file, "\&."), testfile));
assert (zfile_is_readable (file) == false);
zfile_destroy (&file);

//  Create a test file in some random subdirectory
if (verbose)
    zsys_debug ("zfile_test() at timestamp %" PRIi64 ": "
        "Creating new zfile %s",
        zclock_time(), filepath );

if (zfile_exists (filepath) ) {
    if (verbose)
        zsys_debug ("zfile_test() has to remove %s that should not have been here", filepath);
    zfile_delete (filepath);
}

file = zfile_new (dirpath, testfile);
assert (file);
int rc = zfile_output (file);
assert (rc == 0);
zchunk_t *chunk = zchunk_new (NULL, 100);
assert (chunk);
zchunk_fill (chunk, 0, 100);

//  Write 100 bytes at position 1,000,000 in the file
if (verbose)
    zsys_debug ("zfile_test() at timestamp %" PRIi64 ": "
        "Writing 100 bytes at position 1,000,000 in the file",
        zclock_time() );
rc = zfile_write (file, chunk, 1000000);
if (verbose)
    zsys_debug ("zfile_test() at timestamp %" PRIi64 ": "
        "Wrote 100 bytes at position 1,000,000 in the file, result code %d",
        zclock_time(), rc );
assert (rc == 0);
zchunk_destroy (&chunk);
zfile_close (file);
assert (zfile_is_readable (file));
assert (zfile_cursize (file) == 1000100);
if (verbose)
    zsys_debug ("zfile_test() at timestamp %" PRIi64 ": "
        "Testing if file is NOT stable (is younger than 1 sec)",
        zclock_time() );
assert (!zfile_is_stable (file));
if (verbose)
    zsys_debug ("zfile_test() at timestamp %" PRIi64 ": "
        "Passed the lag\-dependent tests",
        zclock_time() );
assert (zfile_digest (file));

//  Now truncate file from outside
int handle = open (filepath, O_WRONLY | O_TRUNC | O_BINARY, 0);
assert (handle >= 0);
rc = write (handle, "Hello, World\en", 13);
assert (rc == 13);
close (handle);
assert (zfile_has_changed (file));
#ifdef CZMQ_BUILD_DRAFT_API
zclock_sleep ((int)zsys_file_stable_age_msec() + 50);
#else
zclock_sleep (5050);
#endif
assert (zfile_has_changed (file));

assert (!zfile_is_stable (file));
zfile_restat (file);
assert (zfile_is_stable (file));
assert (streq (zfile_digest (file), "4AB299C8AD6ED14F31923DD94F8B5F5CB89DFB54"));

//  Check we can read from file
rc = zfile_input (file);
assert (rc == 0);
chunk = zfile_read (file, 1000100, 0);
assert (chunk);
assert (zchunk_size (chunk) == 13);
zchunk_destroy (&chunk);
zfile_close (file);

//  Check we can read lines from file
rc = zfile_input (file);
assert (rc == 0);
const char *line = zfile_readln (file);
assert (streq (line, "Hello, World"));
line = zfile_readln (file);
assert (line == NULL);
zfile_close (file);

//  Try some fun with symbolic links
zfile_t *link = zfile_new (dirpath, testlink);
assert (link);
rc = zfile_output (link);
assert (rc == 0);
fprintf (zfile_handle (link), "%s\en", filepath);
zfile_destroy (&link);

link = zfile_new (dirpath, testlink);
assert (link);
rc = zfile_input (link);
assert (rc == 0);
chunk = zfile_read (link, 1000100, 0);
assert (chunk);
assert (zchunk_size (chunk) == 13);
zchunk_destroy (&chunk);
zfile_destroy (&link);

//  Remove file and directory
zdir_t *dir = zdir_new (basedirpath, NULL);
assert (dir);
assert (zdir_cursize (dir) == 26);
zdir_remove (dir, true);
assert (zdir_cursize (dir) == 0);
zdir_destroy (&dir);

//  Check we can no longer read from file
assert (zfile_is_readable (file));
zfile_restat (file);
assert (!zfile_is_readable (file));
rc = zfile_input (file);
assert (rc == \-1);
zfile_destroy (&file);

// This set of tests is done, free the strings for reuse
zstr_free (&basedirpath);
zstr_free (&dirpath);
zstr_free (&filepath);
zstr_free (&linkpath);

const char *eof_checkfile = "eof_checkfile";
filepath = zsys_sprintf ("%s/%s", SELFTEST_DIR_RW, eof_checkfile);
assert (filepath);

if (zfile_exists (filepath) ) {
    if (verbose)
        zsys_debug ("zfile_test() has to remove %s that should not have been here", filepath);
    zfile_delete (filepath);
}
zstr_free (&filepath);

file = zfile_new (SELFTEST_DIR_RW, eof_checkfile);
assert (file);

//  1\&. Write something first
rc = zfile_output (file);
assert (rc == 0);
chunk = zchunk_new ("123456789", 9);
assert (chunk);

rc = zfile_write (file, chunk, 0);
assert (rc == 0);
zchunk_destroy (&chunk);
zfile_close (file);
assert (zfile_cursize (file) == 9);

// 2\&. Read the written something
rc = zfile_input (file);
assert (rc != \-1);
// try to read more bytes than there is in the file
chunk = zfile_read (file, 1000, 0);
assert (zfile_eof(file));
assert (zchunk_streq (chunk, "123456789"));
zchunk_destroy (&chunk);

// reading is ok
chunk = zfile_read (file, 5, 0);
assert (!zfile_eof(file));
assert (zchunk_streq (chunk, "12345"));
zchunk_destroy (&chunk);

// read from non zero offset until the end
chunk = zfile_read (file, 5, 5);
assert (zfile_eof(file));
assert (zchunk_streq (chunk, "6789"));
zchunk_destroy (&chunk);
zfile_remove (file);
zfile_close (file);
zfile_destroy (&file);

#ifdef CZMQ_BUILD_DRAFT_API
zfile_t *tempfile = zfile_tmp ();
assert (tempfile);
assert (zfile_filename (tempfile, NULL));
assert (zsys_file_exists (zfile_filename (tempfile, NULL)));
zchunk_t *tchunk = zchunk_new ("HELLO", 6);
assert (zfile_write (tempfile, tchunk, 0) == 0);
zchunk_destroy (&tchunk);

char *filename = strdup (zfile_filename (tempfile, NULL));
zfile_destroy (&tempfile);
assert (!zsys_file_exists (filename));
zstr_free (&filename);
#endif // CZMQ_BUILD_DRAFT_API

#if defined (__WINDOWS__)
zsys_shutdown();
#endif
.fi
.if n \{\
.RE
.\}
.sp
.SH "AUTHORS"
.sp
The czmq manual was written by the authors in the AUTHORS file\&.
.SH "RESOURCES"
.sp
Main web site: \m[blue]\fB\%\fR\m[]
.sp
Report bugs to the email <\m[blue]\fBzeromq\-dev@lists\&.zeromq\&.org\fR\m[]\&\s-2\u[1]\d\s+2>
.SH "COPYRIGHT"
.sp
Copyright (c) the Contributors as noted in the AUTHORS file\&. This file is part of CZMQ, the high\-level C binding for 0MQ: http://czmq\&.zeromq\&.org\&. This Source Code Form is subject to the terms of the Mozilla Public License, v\&. 2\&.0\&. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla\&.org/MPL/2\&.0/\&. LICENSE included with the czmq distribution\&.
.SH "NOTES"
.IP " 1." 4
zeromq-dev@lists.zeromq.org
.RS 4
\%mailto:zeromq-dev@lists.zeromq.org
.RE