}
+/**
+ * Return a write handle for serialization.
+ *
+ * @param h master context
+ * @param ext component of the path
+ * @param ent entity identifier (or emtpy string for the directory)
+ * @return NULL on error
+ */
+static struct GNUNET_BIO_WriteHandle *
+get_write_handle (struct GNUNET_FS_Handle *h,
+ const char *ext,
+ const char *ent)
+{
+ char *fn;
+ struct GNUNET_BIO_WriteHandle *ret;
+
+ fn = get_serialization_file_name (h, ext, ent);
+ if (fn == NULL)
+ return NULL;
+ ret = GNUNET_BIO_write_open (fn);
+ GNUNET_free (fn);
+ return ret;
+}
+
+
/**
* Using the given serialization filename, try to deserialize
* the file-information tree associated with it.
}
+/**
+ * Create a temporary file on disk to store the current
+ * state of "fi" in.
+ *
+ * @param fi file information to sync with disk
+ */
+void
+GNUNET_FS_file_information_sync_ (struct GNUNET_FS_FileInformation * fi)
+{
+ char *fn;
+ char *dn;
+ const char *end;
+ const char *nxt;
+ struct GNUNET_BIO_WriteHandle *wh;
+
+ if (NULL == fi->serialization)
+ {
+ dn = get_serialization_file_name (fi->h, "publish-fi", "");
+ fn = GNUNET_DISK_mktemp (dn);
+ GNUNET_free (dn);
+ if (fn == NULL)
+ return; /* epic fail */
+ end = NULL;
+ nxt = fn;
+ while ('\0' != nxt)
+ {
+ if (DIR_SEPARATOR == *nxt)
+ end = nxt + 1;
+ nxt++;
+ }
+ if ( (end == NULL) ||
+ (strlen (end) == 0) )
+ {
+ GNUNET_break (0);
+ GNUNET_free (fn);
+ return;
+ }
+ GNUNET_break (6 == strlen (end));
+ fi->serialization = GNUNET_strdup (end);
+ GNUNET_free (fn);
+ }
+ wh = get_write_handle (fi->h, "publish-fi", fi->serialization);
+ if (wh == NULL)
+ {
+ GNUNET_free (fi->serialization);
+ fi->serialization = NULL;
+ return;
+ }
+ /* FIXME: actual serialization here! */
+ GNUNET_BIO_write_close (wh);
+}
+
+
+
/**
* Find the entry in the file information struct where the
* serialization filename matches the given name.
*/
struct GNUNET_FS_FileInformation *dir;
+ /**
+ * Handle to the master context.
+ */
+ struct GNUNET_FS_Handle *h;
+
/**
* Pointer kept for the client.
*/
const struct GNUNET_FS_FileInformation *p,
uint64_t offset);
+
+/**
+ * Synchronize this file-information struct with its mirror
+ * on disk. Note that all internal FS-operations that change
+ * file information data should already call "sync" internally,
+ * so this function is likely not useful for clients.
+ *
+ * @param fi the struct to sync
+ */
+void
+GNUNET_FS_file_information_sync_ (struct GNUNET_FS_FileInformation *f);
+
+
+
/**
* Master context for most FS operations.
*/
#include "fs_tree.h"
-/**
- * Create a temporary file on disk to store the current
- * state of "fi" in.
- *
- * @param fi file information to sync with disk
- */
-void
-GNUNET_FS_file_information_sync (struct GNUNET_FS_FileInformation * fi)
-{
- if (NULL == fi->serialization)
- {
- fi->serialization = NULL; // FIXME -- need cfg!
- }
- // FIXME...
-}
-
-
-/**
- * Load file information from the file to which
- * it was sync'ed.
- *
- * @param fn name of the file to use
- * @return NULL on error
- */
-struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_recover (const char *fn)
-{
- struct GNUNET_FS_FileInformation *ret;
- ret = NULL;
- // FIXME!
- return ret;
-}
-
-
/**
* Obtain the name under which this file information
* structure is stored on disk. Only works for top-level
/**
* Create an entry for a file in a publish-structure.
*
+ * @param h handle to the file sharing subsystem
* @param client_info initial value for the client-info value for this entry
* @param filename name of the file or directory to publish
* @param keywords under which keywords should this file be available
* @return publish structure entry for the file
*/
struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_create_from_file (void *client_info,
+GNUNET_FS_file_information_create_from_file (struct GNUNET_FS_Handle *h,
+ void *client_info,
const char *filename,
const struct GNUNET_FS_Uri *keywords,
const struct GNUNET_CONTAINER_MetaData *meta,
GNUNET_free (fi);
return NULL;
}
- ret = GNUNET_FS_file_information_create_from_reader (client_info,
+ ret = GNUNET_FS_file_information_create_from_reader (h,
+ client_info,
sbuf.st_size,
&data_reader_file,
fi,
anonymity,
priority,
expirationTime);
+ ret->h = h;
ret->filename = GNUNET_strdup (filename);
fn = filename;
while (NULL != (ss = strstr (fn,
/**
* Create an entry for a file in a publish-structure.
*
+ * @param h handle to the file sharing subsystem
* @param client_info initial value for the client-info value for this entry
* @param length length of the file
* @param data data for the file (should not be used afterwards by
* @return publish structure entry for the file
*/
struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_create_from_data (void *client_info,
+GNUNET_FS_file_information_create_from_data (struct GNUNET_FS_Handle *h,
+ void *client_info,
uint64_t length,
void *data,
const struct GNUNET_FS_Uri *keywords,
uint32_t priority,
struct GNUNET_TIME_Absolute expirationTime)
{
- return GNUNET_FS_file_information_create_from_reader (client_info,
+ return GNUNET_FS_file_information_create_from_reader (h,
+ client_info,
length,
&data_reader_copy,
data,
/**
* Create an entry for a file in a publish-structure.
*
+ * @param h handle to the file sharing subsystem
* @param client_info initial value for the client-info value for this entry
* @param length length of the file
* @param reader function that can be used to obtain the data for the file
* @return publish structure entry for the file
*/
struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_create_from_reader (void *client_info,
+GNUNET_FS_file_information_create_from_reader (struct GNUNET_FS_Handle *h,
+ void *client_info,
uint64_t length,
GNUNET_FS_DataReader reader,
void *reader_cls,
struct GNUNET_FS_FileInformation *ret;
ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
+ ret->h = h;
ret->client_info = client_info;
ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
if (ret->meta == NULL)
ret->data.file.file_size = length;
ret->anonymity = anonymity;
ret->priority = priority;
- GNUNET_FS_file_information_sync (ret);
return ret;
}
*/
struct EXTRACTOR_PluginList *extractors;
+ /**
+ * Master context.
+ */
+ struct GNUNET_FS_Handle *h;
+
/**
* Function to call on each directory entry.
*/
}
if (S_ISDIR (sbuf.st_mode))
{
- fi = GNUNET_FS_file_information_create_from_directory (NULL,
+ fi = GNUNET_FS_file_information_create_from_directory (dsc->h,
+ NULL,
filename,
dsc->scanner,
dsc->scanner_cls,
// FIXME: remove path from filename in metadata!
keywords = GNUNET_FS_uri_ksk_create_from_meta_data (meta);
ksk_uri = GNUNET_FS_uri_ksk_canonicalize (keywords);
- fi = GNUNET_FS_file_information_create_from_file (NULL,
+ fi = GNUNET_FS_file_information_create_from_file (dsc->h,
+ NULL,
filename,
ksk_uri,
meta,
* convenience function.
*
* @param cls must be of type "struct EXTRACTOR_Extractor*"
+ * @param h handle to the file sharing subsystem
* @param dirname name of the directory to scan
* @param do_index should files be indexed or inserted
* @param anonymity desired anonymity level
*/
int
GNUNET_FS_directory_scanner_default (void *cls,
+ struct GNUNET_FS_Handle *h,
const char *dirname,
int do_index,
uint32_t anonymity,
struct EXTRACTOR_PluginList *ex = cls;
struct DirScanCls dsc;
+ dsc.h = h;
dsc.extractors = ex;
dsc.proc = proc;
dsc.proc_cls = proc_cls;
* passed (GNUNET_FS_directory_scanner_default). This is strictly a
* convenience function.
*
+ * @param h handle to the file sharing subsystem
* @param client_info initial value for the client-info value for this entry
* @param filename name of the top-level file or directory
* @param scanner function used to get a list of files in a directory
* @return publish structure entry for the directory, NULL on error
*/
struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_create_from_directory (void *client_info,
+GNUNET_FS_file_information_create_from_directory (struct GNUNET_FS_Handle *h,
+ void *client_info,
const char *filename,
GNUNET_FS_DirectoryScanner scanner,
void *scanner_cls,
meta = GNUNET_CONTAINER_meta_data_create ();
GNUNET_FS_meta_data_make_directory (meta);
scanner (scanner_cls,
+ h,
filename,
do_index,
anonymity,
emsg);
ksk = NULL; // FIXME...
// FIXME: create meta!
- ret = GNUNET_FS_file_information_create_empty_directory (client_info,
+ ret = GNUNET_FS_file_information_create_empty_directory (h,
+ client_info,
ksk,
meta,
anonymity,
while (dc.entries != NULL)
{
dc.entries->dir = ret;
- GNUNET_FS_file_information_sync (dc.entries);
dc.entries = dc.entries->next;
}
fn = filename;
fn,
strlen (fn) + 1);
ret->filename = GNUNET_strdup (filename);
- GNUNET_FS_file_information_sync (ret);
return ret;
}
* use of "GNUNET_FS_file_information_create_from_directory"
* is not appropriate.
*
+ * @param h handle to the file sharing subsystem
* @param client_info initial value for the client-info value for this entry
* @param meta metadata for the directory
* @param keywords under which keywords should this directory be available
* @return publish structure entry for the directory , NULL on error
*/
struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_create_empty_directory (void *client_info,
+GNUNET_FS_file_information_create_empty_directory (struct GNUNET_FS_Handle *h,
+ void *client_info,
const struct GNUNET_FS_Uri *keywords,
const struct GNUNET_CONTAINER_MetaData *meta,
uint32_t anonymity,
struct GNUNET_FS_FileInformation *ret;
ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
+ ret->h = h;
ret->client_info = client_info;
ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
ret->keywords = GNUNET_FS_uri_dup (keywords);
ret->is_directory = GNUNET_YES;
ret->anonymity = anonymity;
ret->priority = priority;
- GNUNET_FS_file_information_sync (ret);
return ret;
}
ent->next = dir->data.dir.entries;
dir->data.dir.entries = ent;
dir->data.dir.dir_size = 0;
- GNUNET_FS_file_information_sync (ent);
- GNUNET_FS_file_information_sync (dir);
return GNUNET_OK;
}
GNUNET_asprintf (&pcc->p->emsg,
_("Upload failed: %s"),
msg);
- GNUNET_FS_file_information_sync (pcc->p);
pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
pi.value.publish.specifics.error.message = pcc->p->emsg;
pcc->p->client_info = GNUNET_FS_publish_make_status_ (&pi, pcc->sc, pcc->p, 0);
}
- GNUNET_FS_file_information_sync (pcc->p);
if (NULL != pcc->cont)
pcc->sc->upload_task
= GNUNET_SCHEDULER_add_with_priority (pcc->sc->h->sched,
sc);
return;
}
- GNUNET_FS_file_information_sync (p);
if (NULL != p->dir)
signal_publish_completion (p, sc);
/* move on to next file */
_("Upload failed: %s"),
emsg);
GNUNET_free (emsg);
- GNUNET_FS_file_information_sync (p);
pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
pi.value.publish.specifics.error.message = p->emsg;
_("Recursive upload failed: %s"),
p->emsg);
}
- GNUNET_FS_file_information_sync (p);
pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
pi.value.publish.specifics.error.message = p->emsg;
daemon->publish_seed = seed;
daemon->verbose = verbose;
daemon->publish_sched = sched;
- fi = GNUNET_FS_file_information_create_from_reader (daemon,
+ fi = GNUNET_FS_file_information_create_from_reader (daemon->fs,
+ daemon,
size,
&file_generator,
daemon,
}
else if (S_ISDIR (sbuf.st_mode))
{
- fi = GNUNET_FS_file_information_create_from_directory (NULL,
+ fi = GNUNET_FS_file_information_create_from_directory (ctx,
+ NULL,
args[0],
&GNUNET_FS_directory_scanner_default,
l,
}
else
{
- fi = GNUNET_FS_file_information_create_from_file (NULL,
+ fi = GNUNET_FS_file_information_create_from_file (ctx,
+ NULL,
args[0],
NULL,
NULL,
buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
meta = GNUNET_CONTAINER_meta_data_create ();
kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
- fi = GNUNET_FS_file_information_create_from_data ("publish-context",
+ fi = GNUNET_FS_file_information_create_from_data (fs,
+ "publish-context",
FILESIZE,
buf,
kuri,
struct GNUNET_FS_FileInformation *fi1;
struct GNUNET_FS_FileInformation *fi2;
struct GNUNET_FS_FileInformation *fidir;
+ struct GNUNET_FS_Handle *fs;
size_t i;
+ fs = GNUNET_FS_start (s, cfg, "test-fs-file-information", NULL, NULL,
+ GNUNET_FS_FLAGS_NONE,
+ GNUNET_FS_OPTIONS_END);
fn1 = GNUNET_DISK_mktemp ("gnunet-file_information-test-dst");
buf = GNUNET_malloc (FILESIZE);
for (i = 0; i < FILESIZE; i++)
meta = GNUNET_CONTAINER_meta_data_create ();
kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
- fi1 = GNUNET_FS_file_information_create_from_file ("file_information-context1",
+ fi1 = GNUNET_FS_file_information_create_from_file (fs,
+ "file_information-context1",
fn1,
kuri,
meta,
1,
42,
GNUNET_TIME_relative_to_absolute (LIFETIME));
- fi2 = GNUNET_FS_file_information_create_from_file ("file_information-context2",
+ fi2 = GNUNET_FS_file_information_create_from_file (fs,
+ "file_information-context2",
fn2,
kuri,
meta,
1,
42,
GNUNET_TIME_relative_to_absolute (LIFETIME));
- fidir = GNUNET_FS_file_information_create_empty_directory ("file_information-context-dir",
+ fidir = GNUNET_FS_file_information_create_empty_directory (fs,
+ "file_information-context-dir",
kuri,
meta,
1,
GNUNET_DISK_directory_remove (fn2);
GNUNET_free_non_null (fn1);
GNUNET_free_non_null (fn2);
+ GNUNET_FS_stop (fs);
}
meta = GNUNET_CONTAINER_meta_data_create ();
kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
- fi1 = GNUNET_FS_file_information_create_from_file ("list_indexed-context1",
+ fi1 = GNUNET_FS_file_information_create_from_file (fs,
+ "list_indexed-context1",
fn1,
kuri,
meta,
1,
42,
GNUNET_TIME_relative_to_absolute (LIFETIME));
- fi2 = GNUNET_FS_file_information_create_from_file ("list_indexed-context2",
+ fi2 = GNUNET_FS_file_information_create_from_file (fs,
+ "list_indexed-context2",
fn2,
kuri,
meta,
2,
42,
GNUNET_TIME_relative_to_absolute (LIFETIME));
- fidir = GNUNET_FS_file_information_create_empty_directory ("list_indexed-context-dir",
+ fidir = GNUNET_FS_file_information_create_empty_directory (fs,
+ "list_indexed-context-dir",
kuri,
meta,
3,
meta = GNUNET_CONTAINER_meta_data_create ();
kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
- fi1 = GNUNET_FS_file_information_create_from_file ("publish-context1",
+ fi1 = GNUNET_FS_file_information_create_from_file (fs,
+ "publish-context1",
fn1,
kuri,
meta,
1,
42,
GNUNET_TIME_relative_to_absolute (LIFETIME));
- fi2 = GNUNET_FS_file_information_create_from_file ("publish-context2",
+ fi2 = GNUNET_FS_file_information_create_from_file (fs,
+ "publish-context2",
fn2,
kuri,
meta,
2,
42,
GNUNET_TIME_relative_to_absolute (LIFETIME));
- fidir = GNUNET_FS_file_information_create_empty_directory ("publish-context-dir",
+ fidir = GNUNET_FS_file_information_create_empty_directory (fs,
+ "publish-context-dir",
kuri,
meta,
3,
buf[i] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 256);
meta = GNUNET_CONTAINER_meta_data_create ();
kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
- fi = GNUNET_FS_file_information_create_from_data ("publish-context",
+ fi = GNUNET_FS_file_information_create_from_data (fs,
+ "publish-context",
FILESIZE,
buf,
kuri,
GNUNET_free (buf);
meta = GNUNET_CONTAINER_meta_data_create ();
kuri = GNUNET_FS_uri_ksk_create_from_args (2, keywords);
- fi = GNUNET_FS_file_information_create_from_file ("publish-context",
+ fi = GNUNET_FS_file_information_create_from_file (fs,
+ "publish-context",
fn,
kuri,
meta,
/**
- * Create an (empty) temporary file on disk.
+ * Create an (empty) temporary file on disk. If the given name is not
+ * an absolute path, the current 'TMPDIR' will be prepended. In any case,
+ * 6 random characters will be appended to the name to create a unique
+ * filename.
*
* @param t component to use for the name;
* does NOT contain "XXXXXX" or "/tmp/".
void **client_info);
-/**
- * Recover file information structure from disk.
- *
- * @param fn filename for the structure on disk
- * @return NULL on error
- */
-struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_recover (const char *fn);
-
-
/**
* Obtain the name under which this file information
* structure is stored on disk. Only works for top-level
GNUNET_FS_file_information_get_id (struct GNUNET_FS_FileInformation *s);
-/**
- * Synchronize this file-information struct with its mirror
- * on disk. Note that all internal FS-operations that change
- * file information data should already call "sync" internally,
- * so this function is likely not useful for clients.
- *
- * @param fi the struct to sync
- */
-void
-GNUNET_FS_file_information_sync (struct GNUNET_FS_FileInformation *f);
-
/**
* Create an entry for a file in a publish-structure.
*
+ * @param h handle to the file sharing subsystem
* @param client_info initial client-info value for this entry
* @param filename name of the file or directory to publish
* @param keywords under which keywords should this file be available
* @return publish structure entry for the file
*/
struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_create_from_file (void *client_info,
+GNUNET_FS_file_information_create_from_file (struct GNUNET_FS_Handle *h,
+ void *client_info,
const char *filename,
const struct GNUNET_FS_Uri *keywords,
const struct GNUNET_CONTAINER_MetaData *meta,
/**
* Create an entry for a file in a publish-structure.
*
+ * @param h handle to the file sharing subsystem
* @param client_info initial client-info value for this entry
* @param length length of the file
* @param data data for the file (should not be used afterwards by
* @return publish structure entry for the file
*/
struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_create_from_data (void *client_info,
+GNUNET_FS_file_information_create_from_data (struct GNUNET_FS_Handle *h,
+ void *client_info,
uint64_t length,
void *data,
const struct GNUNET_FS_Uri *keywords,
/**
* Create an entry for a file in a publish-structure.
*
+ * @param h handle to the file sharing subsystem
* @param client_info initial client-info value for this entry
* @param length length of the file
* @param reader function that can be used to obtain the data for the file
* @return publish structure entry for the file
*/
struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_create_from_reader (void *client_info,
+GNUNET_FS_file_information_create_from_reader (struct GNUNET_FS_Handle *h,
+ void *client_info,
uint64_t length,
GNUNET_FS_DataReader reader,
void *reader_cls,
* Type of a function that will be used to scan a directory.
*
* @param cls closure
+ * @param h handle to the file sharing subsystem
* @param dirname name of the directory to scan
* @param do_index should files be indexed or inserted
* @param anonymity desired anonymity level
* @return GNUNET_OK on success
*/
typedef int (*GNUNET_FS_DirectoryScanner)(void *cls,
+ struct GNUNET_FS_Handle *h,
const char *dirname,
int do_index,
uint32_t anonymity,
* convenience function.
*
* @param cls must be of type "struct EXTRACTOR_Extractor*"
+ * @param h handle to the file sharing subsystem
* @param dirname name of the directory to scan
* @param do_index should files be indexed or inserted
* @param anonymity desired anonymity level
*/
int
GNUNET_FS_directory_scanner_default (void *cls,
+ struct GNUNET_FS_Handle *h,
const char *dirname,
int do_index,
uint32_t anonymity,
* passed (GNUNET_FS_directory_scanner_default). This is strictly a
* convenience function.
*
+ * @param h handle to the file sharing subsystem
* @param client_info initial client-info value for this entry
* @param filename name of the top-level file or directory
* @param scanner function used to get a list of files in a directory
* @return publish structure entry for the directory, NULL on error
*/
struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_create_from_directory (void *client_info,
+GNUNET_FS_file_information_create_from_directory (struct GNUNET_FS_Handle *h,
+ void *client_info,
const char *filename,
GNUNET_FS_DirectoryScanner scanner,
void *scanner_cls,
* use of "GNUNET_FS_file_information_create_from_directory"
* is not appropriate.
*
+ * @param h handle to the file sharing subsystem
* @param client_info initial client-info value for this entry
* @param keywords under which keywords should this directory be available
* directly; can be NULL
* @return publish structure entry for the directory , NULL on error
*/
struct GNUNET_FS_FileInformation *
-GNUNET_FS_file_information_create_empty_directory (void *client_info,
+GNUNET_FS_file_information_create_empty_directory (struct GNUNET_FS_Handle *h,
+ void *client_info,
const struct GNUNET_FS_Uri *keywords,
const struct GNUNET_CONTAINER_MetaData *meta,
uint32_t anonymity,
/**
- * Create an (empty) temporary file on disk.
+ * Create an (empty) temporary file on disk. If the given name is not
+ * an absolute path, the current 'TMPDIR' will be prepended. In any case,
+ * 6 random characters will be appended to the name to create a unique
+ * filename.
*
* @param t component to use for the name;
* does NOT contain "XXXXXX" or "/tmp/".
char *tmpl;
char *fn;
- tmpdir = getenv ("TMPDIR");
- tmpdir = tmpdir ? tmpdir : "/tmp";
-
- GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
+ if ( (t[0] != '/') &&
+ (t[0] != '\\') )
+ {
+ tmpdir = getenv ("TMPDIR");
+ tmpdir = tmpdir ? tmpdir : "/tmp";
+ GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
+ }
+ else
+ {
+ GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
+ }
#ifdef MINGW
fn = (char *) GNUNET_malloc (MAX_PATH + 1);
if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))